<!--
AppSprint docs markdown
Canonical HTML: https://appsprint.app/docs/ios-swift
Markdown URL: https://appsprint.app/docs/ios-swift.md
Docs index: https://appsprint.app/docs.md
Sitemap: https://appsprint.app/sitemap.xml
LLM guide: https://appsprint.app/llms.txt
-->

# iOS (Swift)

Installation, configuration, event tracking, and full API reference for the iOS (Swift) SDK.

## Requirements

- iOS 14.0+
- Xcode 15+
- Swift 5.9 with structured concurrency (async/await)

## 1. Install

Swift Package Manager (recommended) or CocoaPods

```swift
// Swift Package Manager. In Xcode: File → Add Package Dependencies…
// Or in Package.swift:
.package(url: "https://github.com/getappsprint/appsprint-ios-sdk", from: "1.0.1")

// CocoaPods. In your Podfile:
pod "AppSprintSDK", "~> 1.0.1"
```

> Shipped as a precompiled XCFramework with a bundled PrivacyInfo.xcprivacy manifest. No source code is included.

[View on GitHub (SPM + CocoaPods)](https://github.com/getappsprint/appsprint-ios-sdk)

## 2. Configure

```swift
import AppSprintSDK

// Call as early as possible. App.init() (SwiftUI) or AppDelegate.didFinishLaunching.
Task {
  await AppSprint.shared.configure(
    AppSprintConfig(apiKey: "as_live_xxxxx")
  )
}
```

Non-blocking. configure() returns after restoring local state; install registration runs in a detached Task with exponential backoff. Queued events flush automatically once install completes.

### Configuration options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `apiKey` | `String` | — | Your live API key (starts with as_live_). |
| `apiURL` | `URL` | https://api.appsprint.app | Override for staging or self-hosted environments. |
| `enableAppleAdsAttribution` | `Bool` | true | Fetches the Apple AdServices token at install time and triggers a delayed attribution refresh 75 seconds later. |
| `customerUserId` | `String?` | nil | Your internal user ID. Persists across launches and replays automatically if the first send fails. |
| `autoTrackSessions` | `Bool` | true | Fires session_start on configure() and on foreground, debounced to one event per 30 minutes. |
| `autoRefreshAttribution` | `Bool` | true | Refetches /v1/sdk/attribution on configure(), foreground, and after a late AdServices PATCH. |
| `googleAdsConsent` | `GoogleAdsConsent?` | nil | Optional Google Ads ad user data consent forwarded as consent.adUserData on Google Ads uploads. |
| `isDebug` | `Bool` | false | Forces logLevel = .debug. |
| `logLevel` | `AppSprintLogLevel` | .warn | .debug, .info, .warn, or .error. Routes through os.Logger on iOS 14+. |

## 3. Track events

```swift
// Standard event
await AppSprint.shared.sendEvent(.login)
```

```swift
// Revenue event. Currency must be a 3-letter ISO code.
await AppSprint.shared.sendEvent(.purchase, params: [
  "revenue": 9.99,
  "currency": "USD"
])
```

```swift
// Custom event with parameters
await AppSprint.shared.sendEvent(.custom, name: "level_complete", params: [
  "level": 5,
  "score": 1200,
])
```

### Supported event types

`session_start`, `login`, `sign_up`, `register`, `purchase`, `subscribe`, `start_trial`, `add_payment_info`, `add_to_cart`, `add_to_wishlist`, `initiate_checkout`, `view_content`, `view_item`, `search`, `share`, `tutorial_complete`, `achieve_level`, `level_start`, `level_complete`, `custom`

## 4. Read attribution

```swift
let attr = AppSprint.shared.getAttribution()
print(attr?.source)         // "apple_ads", "tracking_link", or "organic"
print(attr?.isAttributed)   // Bool
print(attr?.campaignName)   // Campaign name when available
print(attr?.appleAds?.campaignId)
print(attr?.link?.name)
```

## API reference

### `configure(_:)` → async

Initializes the SDK. Returns immediately; install registration runs in the background with retry on failure.

```swift
await AppSprint.shared.configure(AppSprintConfig(apiKey: "…"))
```

### `sendEvent(_:name:params:)` → async

Enqueues an event locally and schedules a flush. Does not block on a network round-trip.

```swift
await AppSprint.shared.sendEvent(.purchase, params: ["revenue": 9.99, "currency": "USD"])
```

### `flush()` → async

Drains the queue immediately. Safe to call repeatedly; concurrent flushes are deduplicated.

```swift
await AppSprint.shared.flush()
```

### `refreshAttribution()` → AttributionResult?

Fetches the latest attribution from /v1/sdk/attribution. Self-heals a 404 install_not_found by re-running install.

```swift
let attr = await AppSprint.shared.refreshAttribution()
```

### `setCustomerUserId(_:)` → async

Updates the customer user ID. Sent immediately if install is registered; otherwise queued and retried automatically.

```swift
await AppSprint.shared.setCustomerUserId("user-123")
```

### `getAppSprintId()` → String?

Returns the install ID, or nil before install registration completes.

```swift
let id = AppSprint.shared.getAppSprintId()
```

### `getAttribution()` → AttributionResult?

Returns the cached AttributionResult. Available synchronously without a network call.

```swift
let attr = AppSprint.shared.getAttribution()
```

### `getAttributionParams()` → [String: String]

Partner-ready flat payload for forwarding to RevenueCat, Superwall, etc.

```swift
let params = AppSprint.shared.getAttributionParams()
```

### `enableAppleAdsAttribution()` → Bool

Re-enables Apple Ads at runtime and sends the AdServices token via PATCH. Returns whether the SDK was configured.

```swift
AppSprint.shared.enableAppleAdsAttribution()
```

### `isInitialized` → Bool

True after configure() returns. Does not imply install registration succeeded.

```swift
AppSprint.shared.isInitialized
```

### `isSdkDisabled()` → Bool

True if a 401 or 403 from the backend permanently disabled the SDK.

```swift
AppSprint.shared.isSdkDisabled()
```

### `sendTestEvent()` → TestEventResult

Posts a diagnostic event and returns (success, message). Use during development.

```swift
let result = await AppSprint.shared.sendTestEvent()
```

### `clearData()`

Wipes local state and the event queue. Use to reset the SDK.

```swift
AppSprint.shared.clearData()
```


## Offline behavior

- Events that fail to send are automatically queued in native storage (up to 100 events).
- The queue persists across app restarts.
- Queued events are retried automatically when configure() completes, when the app moves to the background, or when you call flush().
- If a queued event receives a 401/403, the SDK disables itself and clears the queue.

## Platform notes

- configure() is annotated @MainActor and is non-blocking. Calling it from didFinishLaunchingWithOptions or App.init() does not delay app startup.
- ATT must be requested while the app is foreground-active. AppSprintNative.requestTrackingAuthorization() waits for that state internally, so calling it from app init is safe.
- IDFA is collected only when ATT is authorized. If the user grants ATT after install, the SDK PATCHes the IDFA on the next foreground automatically.
- Apple AdServices token fetch (AAAttribution.attributionToken) runs on iOS 14.3+. Failure is non-fatal; a fresh token is fetched on retry.
- The SDK uses URLSession.waitsForConnectivity = true, so transient offline windows queue inside the OS rather than failing fast.
- Background flushes are wrapped in beginBackgroundTask so iOS does not kill the queue worker mid-loop.

## Troubleshooting

**getAppSprintId() returns nil**
configure() returns before install registration finishes. Wait until isInitialized is true, then poll briefly, or read the value inside an event handler that fires after first launch.

**Events do not appear in dashboard**
Confirm the API key starts with as_live_. Call sendTestEvent() and inspect the returned message. Check Console.app for [AppSprint] logs at debug level.

**SDK disabled after 401/403**
A rejected key disables the SDK permanently. Call clearData(), then configure() with a valid key.

**Attribution returns organic when an Apple Ads install was expected**
Backend resolution can take up to 75 seconds for Apple AdServices. The SDK refetches automatically by default. If autoRefreshAttribution is off, call refreshAttribution() manually.

**Queued events seem to never send**
A queued event over ~90 KB is dropped on the next flush (the backend caps bodies at 100 KB). Smaller events flush on foreground, background, or another sendEvent.

## Connectivity test

```swift
let result = await AppSprint.shared.sendTestEvent()
print("\(result.success) — \(result.message)")
```

## Verification checklist

1. Create a blank iOS app and add the SPM package or CocoaPods pod.
2. Call configure() in App.init() or AppDelegate.didFinishLaunchingWithOptions.
3. Verify getAppSprintId() returns a value after first launch.
4. Send login, purchase, and custom events. Confirm they appear in the dashboard.
5. Kill the app while offline, send an event, relaunch with connectivity. Verify the event lands.
6. Call sendTestEvent() and confirm success is true.

---

## Docs navigation

Use the Markdown URLs when reading the docs programmatically. Use the HTML URLs when you need the interactive docs UI.

- [Overview](https://appsprint.app/docs) ([Markdown](https://appsprint.app/docs.md)) — Introduction to AppSprint
- [Quickstart](https://appsprint.app/docs/quickstart) ([Markdown](https://appsprint.app/docs/quickstart.md)) — Get up and running in 5 minutes
- [React Native](https://appsprint.app/docs/react-native) ([Markdown](https://appsprint.app/docs/react-native.md)) — React Native / Expo SDK reference
- [iOS (Swift)](https://appsprint.app/docs/ios-swift) ([Markdown](https://appsprint.app/docs/ios-swift.md)) — Native Swift SDK reference
- [Android (Kotlin)](https://appsprint.app/docs/android) ([Markdown](https://appsprint.app/docs/android.md)) — Native Android SDK reference
- [Flutter](https://appsprint.app/docs/flutter) ([Markdown](https://appsprint.app/docs/flutter.md)) — Flutter plugin reference
- [RevenueCat](https://appsprint.app/docs/revenuecat) ([Markdown](https://appsprint.app/docs/revenuecat.md)) — Webhook integration for subscription attribution
- [Superwall](https://appsprint.app/docs/superwall) ([Markdown](https://appsprint.app/docs/superwall.md)) — Webhook integration for paywall attribution
- [Apple Search Ads](https://appsprint.app/docs/apple-search-ads) ([Markdown](https://appsprint.app/docs/apple-search-ads.md)) — Campaign and keyword attribution
- [Google Ads](https://appsprint.app/docs/google-ads) ([Markdown](https://appsprint.app/docs/google-ads.md)) — Offline click conversion upload
- [TikTok Ads](https://appsprint.app/docs/tiktok-ads) ([Markdown](https://appsprint.app/docs/tiktok-ads.md)) — Events API server-side event forwarding
