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

# iOS SDK (Swift)

> Add Appeeky Analytics to a native iOS app with Swift Package Manager

The Swift SDK records installs, sessions, screens and your own events, and links each install to the RevenueCat or Superwall customer. It sends the same data as the [React Native SDK](https://docs.appeeky.com/docs/analytics-sdk-reference.md), so the dashboard, attribution and revenue work the same way.

* No IDFA and no App Tracking Transparency prompt. The install id is a random id stored in the app.
* Events are queued on the device and sent in batches. They survive offline periods and app restarts.
* Apple Search Ads attribution (AdServices) and SKAdNetwork / AdAttributionKit conversion values are built in.
* iOS 15+, Swift 5.9+, no third-party dependencies. Every method is safe to call from any thread.

The source is on GitHub: [appeeky/analytics-swift](https://github.com/appeeky/analytics-swift).

***

## Setup

**1. Turn on Analytics for the app**

In Appeeky, open the app under **My Apps → Attribution** and click **Turn on attribution**. Copy the publishable key (`apk_pub_…`). See the [Quickstart](https://docs.appeeky.com/docs/analytics-quickstart.md) for the API call.

**2. Add the package**

In Xcode, choose **File › Add Package Dependencies…**, enter `https://github.com/appeeky/analytics-swift.git` and add the `AppeekyAnalytics` library to your app target.

In a `Package.swift`:

```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
dependencies: [
    .package(url: "https://github.com/appeeky/analytics-swift.git", from: "0.1.0"),
],
targets: [
    .target(name: "MyApp", dependencies: [
        .product(name: "AppeekyAnalytics", package: "analytics-swift"),
    ]),
]
```

**3. Add the postback keys to Info.plist**

These keys make iOS send copies of winning SKAdNetwork and AdAttributionKit postbacks to Appeeky:

```xml theme={"theme":{"light":"github-light","dark":"github-dark"}}
<key>NSAdvertisingAttributionReportEndpoint</key>
<string>https://appeeky.com</string>
<key>AdAttributionKit</key>
<dict>
    <key>AttributionCopyEndpoint</key>
    <string>https://appeeky.com</string>
</dict>
```

An app can name only one endpoint. Leave the keys out if another provider already receives the copies, and use your own registrable domain when you [self-host](https://docs.appeeky.com/docs/analytics-self-hosting.md). See [SKAdNetwork](https://docs.appeeky.com/docs/analytics-skadnetwork.md).

**4. Configure the SDK and pass it the URLs that open the app**

Call `configure` once, as early as possible. Hand every URL that opens the app to the SDK, so campaign links and smart links are attributed.

```swift SwiftUI theme={"theme":{"light":"github-light","dark":"github-dark"}}
import AppeekyAnalytics
import SwiftUI

@main
struct MyApp: App {
    init() {
        Appeeky.configure(key: "apk_pub_...")
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
                .onOpenURL { Appeeky.handleOpenURL($0) }
                .onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { Appeeky.handleUserActivity($0) }
        }
    }
}
```

```swift App delegate theme={"theme":{"light":"github-light","dark":"github-dark"}}
import AppeekyAnalytics

func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    Appeeky.configure(key: "apk_pub_...")
    Appeeky.handleLaunchOptions(launchOptions)
    return true
}

func application(_ app: UIApplication, open url: URL,
                 options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
    Appeeky.handleOpenURL(url)
    return true
}

func application(_ application: UIApplication, continue userActivity: NSUserActivity,
                 restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    Appeeky.handleUserActivity(userActivity)
    return true
}
```

```swift Scene delegate theme={"theme":{"light":"github-light","dark":"github-dark"}}
func scene(_ scene: UIScene, willConnectTo session: UISceneSession,
           options connectionOptions: UIScene.ConnectionOptions) {
    Appeeky.handleConnectionOptions(connectionOptions) // cold start through a link
}

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    URLContexts.forEach { Appeeky.handleOpenURL($0.url) }
}

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
    Appeeky.handleUserActivity(userActivity)
}
```

The SDK sends to `https://ingest.appeeky.com/v1/ingest`. For a self-hosted server, pass `endpoint: URL(string: "https://analytics.example.com/v1/ingest")!`.

**5. Link the install to RevenueCat or Superwall**

```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
import RevenueCat

Appeeky.identify(Purchases.shared.appUserID)

// After a login:
let (customerInfo, _) = try await Purchases.shared.logIn(userId)
Appeeky.identify(userId)

// On logout:
Appeeky.identify(nil)
```

With [Superwall](https://docs.appeeky.com/docs/analytics-superwall.md), pass the same user id you give `Superwall.shared.identify(userId:)`. `identify` sends a batch within about a second, so the install is linked before the first purchase.

**6. Track screens**

Call `screen` from `.onAppear` of each top-level SwiftUI view, or from `viewDidAppear` in UIKit:

```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
Appeeky.screen("Paywall")
```

***

## Methods

| Method | What it does |
| - | - |
| `Appeeky.configure(key:endpoint:)` | Starts the SDK. Later calls are ignored. |
| `Appeeky.identify(_:)` | Links the install to a RevenueCat or Superwall user id. `nil` on logout. |
| `Appeeky.screen(_:properties:)` | Records a screen view. |
| `Appeeky.track(_:properties:value:)` | Records a custom event, with optional properties and a number. |
| `Appeeky.handleOpenURL(_:)`, `handleUserActivity(_:)`, `handleLaunchOptions(_:)`, `handleConnectionOptions(_:)` | Pass the URL that opened the app. Only the first launch's URL is attribution input. |
| `Appeeky.deferredDeepLink()` | The smart link that opened the app on its first launch, once. `nil` afterwards. |
| `Appeeky.trialStarted()`, `Appeeky.purchased(_:)` | Raise the SKAdNetwork conversion value. |
| `Appeeky.adRevenue(_:network:adUnit:format:)` | Reports a paid ad impression in USD. |
| `Appeeky.flush()` | Sends queued events now (async, or `flush(completion:)`). |
| `Appeeky.installId` | This install's random id. |

```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
Appeeky.track("onboarding_completed", properties: ["step": 3])
Appeeky.track("export", properties: ["format": "pdf", "shared": true], value: 1)
```

Event names may use letters, digits, `_`, `.`, `:` and `-`, up to 64 characters. Property values are strings (up to 512 characters), numbers, booleans or `nil`.

### Options

```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
Appeeky.configure(AppeekyOptions(
    key: "apk_pub_...",
    flushInterval: 10,
    sessionTimeout: 30 * 60,
    debug: true
))
```

| Option | Default | Effect |
| - | - | - |
| `key` | required | Publishable key |
| `endpoint` | Appeeky hosted | A self-hosted server's ingest URL, ending in `/v1/ingest` |
| `resolveAdServicesOnDevice` | `true` | Exchange the AdServices token with Apple from the device |
| `adServicesToken` | built in | Replace the `AAAttribution.attributionToken()` call |
| `flushInterval` | `10` s | How often queued events are sent while the app is open |
| `flushAt` | `20` | Send as soon as this many events are queued |
| `maxQueueSize` | `1000` | Oldest events are dropped beyond this |
| `sessionTimeout` | `1800` s | Time in the background before a new session starts |
| `context` | | `DeviceContext` fields that override the detected ones |
| `disabled` | `false` | Collect and send nothing, for example after an opt-out |
| `debug` | `false` | Log rejected batches and network errors (`os.Logger`, subsystem `com.appeeky.analytics`) |

***

## Automatic Events

| Event | When |
| - | - |
| `first_open` | First launch after install |
| `session_start` | Launch, or returning after `sessionTimeout` in the background |
| `app_background` | Going to the background, with `foreground_ms` |
| `identify` / `reset` | `identify(id)` / `identify(nil)` |
| `ad_revenue` | `adRevenue(...)` |

When the system launches the app in the background (background fetch, a silent push), the first session starts when the user actually opens it.

***

## Smart Links on iOS

A [smart link](https://docs.appeeky.com/docs/analytics-smart-links.md) opens the app through its URL scheme when it is installed, with the campaign added to the URL, and the App Store otherwise. The SDK waits up to two seconds after `configure` for the launch URL.

```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
if let destination = await Appeeky.deferredDeepLink() {
    router.open(destination)
}
```

iOS has no deferred deep link through the App Store. Matching an App Store install back to a click would need device fingerprinting, which Apple does not allow.

***

## SKAdNetwork Conversion Values

The SDK keeps the SKAdNetwork (iOS 15.4+) and AdAttributionKit (iOS 17.4+) conversion value up to date. Values only go up:

| Fine value | Coarse | Set by |
| - | - | - |
| 1 | low | First launch (automatic) |
| 2 | medium | Came back on a later day (automatic) |
| 8 | medium | `Appeeky.trialStarted()` |
| 16–63 | high | `Appeeky.purchased(valueUsd)`: 16 + $2 steps, capped at $94+ |

```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
// After a RevenueCat purchase succeeds:
Appeeky.purchased(29.99)
```

`purchased` and `trialStarted` only change the conversion value. Revenue comes from RevenueCat or Superwall, so nothing is counted twice.

***

## Privacy

The SDK sends the install id, app version, OS version, device model, locale and the events you track. It never reads advertising identifiers. The package ships a privacy manifest (`PrivacyInfo.xcprivacy`). In App Store Connect, declare **Usage Data › Product Interaction** and **Purchases** as not linked to the user's identity and not used for tracking. If your RevenueCat app user id is a real account id, declare them as linked instead. See [Privacy](https://docs.appeeky.com/docs/analytics-privacy.md).

State and the event queue are kept in `Application Support/AppeekyAnalytics/`.


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