> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apsio.io/llms.txt
> Use this file to discover all available pages before exploring further.

# iOS and iPadOS

> Install the Apsio SDK in an iOS or iPadOS app, iOS 15 and later.

API preview: names may change before 1.0.

## Requirements

* iOS or iPadOS 15.0 or later.
* Swift Package Manager, with Xcode 16 or later. Your app can use the Swift 5 or the Swift 6
  language mode; the snippets on this page are checked in Swift 6 mode.

## Install

Add the package `https://github.com/Apsio/apsio-apple-sdk` with Swift Package Manager and
link the `Apsio` product to your app target. There is no tagged release yet; during early
access, use the `main` branch.

The package depends on [KSCrash](https://github.com/kstenerud/KSCrash) 2.6.0 for crash
capture. If your app already links KSCrash, Swift Package Manager resolves one copy.

## Start

Start Apsio as early as possible: in your SwiftUI `App` initializer, or in
`application(_:didFinishLaunchingWithOptions:)`.

```swift theme={null}
import SwiftUI
import Apsio

@main
struct ShopApp: App {
    init() {
        Apsio.start(key: "a-…")  // your app key, from Settings → Apps
    }

    var body: some Scene {
        WindowGroup { Text("Shop") }
    }
}
```

```swift theme={null}
import UIKit
import Apsio

final class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        Apsio.start(key: "a-…")
        return true
    }
}
```

Calling `start` again does nothing. The app key is not a secret: it ships inside your app and
only routes data to your project.

## What is recorded

From then on Apsio records, with nothing else to do:

* crashes, sent when the app next launches;
* terminations without a crash report (out of memory, watchdog), inferred on the next launch;
* app start time, and screen load times for UIKit view controllers;
* network requests made with `URLSession`: method, URL without the query string, host,
  status and duration;
* breadcrumbs for screens, network requests and app lifecycle changes;
* sessions, and the daily MetricKit reports (launch, hangs, memory, CPU, terminations).

Crashes are not recorded while Xcode's debugger is attached: the debugger stops the app
first. To test a crash, stop the app in Xcode and open it from the home screen.

## Options

```swift theme={null}
Apsio.start(key: "a-…", options: ApsioOptions(
    installationId: true,                   // count users, not only sessions
    firstPartyHosts: ["api.example.com"],  // add a traceparent header to your own backend
    crashes: true, screens: true, network: true, metricKit: true
))
```

| Option | Default | What it does |
| - | - | - |
| `installationId` | `true` | A random id per install, so Apsio can count users. See [Privacy and consent](/concepts/privacy). |
| `firstPartyHosts` | `[]` | Hosts that receive a W3C `traceparent` header, so your backend's traces join the app's. |
| `crashes` | `true` | Crash capture with KSCrash. |
| `screens` | `true` | Screen tracking for UIKit view controllers. |
| `network` | `true` | Spans and breadcrumbs for `URLSession` requests. |
| `metricKit` | `true` | MetricKit reports and hang diagnostics. |
| `endpoint` | Apsio's ingest | Sends to another ingest URL, for a proxy. |
| `configEndpoint` | Apsio's ingest | Fetches the [remote configuration](/remote-configuration) from another URL. |

`traceparent` is only added to the hosts you list, never to third parties. Query strings and
request bodies are never recorded.

## Users and attributes

Identify the user with a hash you compute. Never pass an email or a name.

```swift theme={null}
import CryptoKit

let idHash = SHA256.hash(data: Data(userId.utf8)).map { String(format: "%02x", $0) }.joined()
Apsio.setUser(idHash: idHash)
// when the user signs out:
Apsio.clearUser()
```

An attribute set with `setAttribute` is added to every record from then on; `nil` removes it.

```swift theme={null}
Apsio.setAttribute("plan", "pro")
Apsio.setAttribute("cart.items", 3)
Apsio.setAttribute("plan", nil)
```

Attribute values are strings, booleans, integers, doubles or string arrays
(`AttributeValue`).

## Logs, errors and breadcrumbs

```swift theme={null}
Apsio.log(.info, "Checkout opened", attributes: ["cart.items": 3])
Apsio.recordError(error, fingerprint: "checkout-card-declined")
Apsio.addBreadcrumb("Tapped pay")
```

* Log levels are `.trace`, `.debug`, `.info`, `.warn`, `.error` and `.fatal`. Logs below the
  minimum level of the [remote configuration](/remote-configuration) (Info by default) are
  dropped on the device.
* `recordError` records a handled error with its stack. Errors are grouped into issues like
  crashes; `fingerprint` replaces the default grouping. See
  [Crashes and issues](/concepts/issues#grouping).
* Breadcrumbs are the steps shown before a crash or an error in the same session.

## Spans

```swift theme={null}
let span = Apsio.startSpan("checkout.submit")  // requests made meanwhile nest under it
span.setAttribute("payment.method", "card")
span.end()             // or span.end(error: true)
```

Spans started while another is open become its children.

## Screens in SwiftUI

UIKit view controllers are tracked on their own, named after their class. In SwiftUI, name
screens yourself, and report when a screen shows its real content to measure its time to
full display:

```swift theme={null}
import SwiftUI
import Apsio

struct CheckoutView: View {
    @State private var items: [String] = []

    var body: some View {
        List(items, id: \.self) { Text($0) }
            .onAppear { Apsio.trackScreen("Checkout") }
            .task {
                items = await loadCart()
                Apsio.reportFullyDrawn()  // the screen now shows its real content
            }
    }
}
```

## Feature flags

```swift theme={null}
Apsio.setFeatureFlag("new-checkout", variant: "b")
```

The flag and its variant are attached to the session and to crashes.

## Privacy and consent

```swift theme={null}
Apsio.setConsent(false)  // stops collection and deletes what is waiting to be sent
Apsio.setConsent(true)   // resumes collection
```

The choice is kept across launches. If your app must ask before anything is collected, call
`start` only after the user agrees.

The SDK ships a privacy manifest for each of its modules. Declare crash, performance and
diagnostic data in your App Store privacy label; if you keep the installation id on, also
declare a device ID used for analytics, not linked to the user and not used for tracking.
See [Privacy and consent](/concepts/privacy).

## Sending now

Records leave in batches. To send what is queued now, for example before a test ends:

```swift theme={null}
await Apsio.flush()
```

## Background uploads

What is left when the app moves to the background is handed to iOS, which sends it later.
Optionally, let iOS know when Apsio's uploads finish:

```swift theme={null}
import UIKit
import Apsio

final class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String,
                     completionHandler: @escaping () -> Void) {
        _ = Apsio.handleBackgroundEvents(identifier: identifier, completion: completionHandler)
    }
}
```

Apsio calls `completionHandler` on the main thread. `handleBackgroundEvents` returns `false`
for background sessions that are not Apsio's; handle those as before.

## Next

* [Upload your dSYMs](/symbols/upload), so crashes show function names and lines.
* [Remote configuration](/remote-configuration): sampling and capture without a release.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.