Documentation

iOS (Swift)

Install the iOS (Swift) SDK, register installs, track events, and read attribution. Setup takes about 5 minutes.

Requirements

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

Install

Swift Package Manager (recommended) or CocoaPods

// Swift Package Manager. In Xcode: File → Add Package Dependencies…
// Or in Package.swift:
.package(url: "https://github.com/getappsprint/appsprint-ios-sdk", from: "1.1.7")
// CocoaPods. In your Podfile:
pod "AppSprintSDK", "~> 1.1.7"

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

Published on GitHub (SPM + CocoaPods). Always use the latest version.

Configure

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.

Track events

// Standard event
await AppSprint.shared.sendEvent(.login)
// Revenue event. Currency must be a 3-letter ISO code.
await AppSprint.shared.sendEvent(.purchase, params: [
"revenue": 9.99,
"currency": "USD"
])
// Custom event with parameters
await AppSprint.shared.sendEvent(.custom, name: "level_complete", params: [
"level": 5,
"score": 1200,
])

Read attribution

Once install registration completes, the SDK caches the attribution result from the install response. Native iOS and Android expose synchronous getters; React Native and Flutter bridge calls are asynchronous.

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)

Verify the connection

Send a test event to confirm end-to-end delivery. You should see it in the AppSprint dashboard within seconds.

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

Reference

Configuration options
OptionTypeDefaultDescription
apiKeyStringYour live API key (starts with as_live_).
apiURLURLhttps://api.appsprint.appOverride for staging or self-hosted environments.
enableAppleAdsAttributionBooltrueFetches the Apple AdServices token at install time and triggers a delayed attribution refresh 75 seconds later.
customerUserIdString?nilYour internal user ID. Persists across launches and replays automatically if the first send fails.
autoTrackSessionsBooltrueFires session_start on configure() and on foreground, debounced to one event per 30 minutes.
autoRefreshAttributionBooltrueRefetches /v1/sdk/attribution on configure(), foreground, and after a late AdServices PATCH.
googleAdsConsentGoogleAdsConsent?nilReserved for upcoming Google Ads uploads; not used while Google Ads is coming soon.
isDebugBoolfalseForces logLevel = .debug.
logLevelAppSprintLogLevel.warn.debug, .info, .warn, or .error. Routes through os.Logger on iOS 14+.
Supported event types
session_startloginsign_upregisterpurchasesubscribestart_trialadd_payment_infoadd_to_cartadd_to_wishlistinitiate_checkoutview_contentview_itemsearchsharetutorial_completeachieve_levellevel_startlevel_completecustom

Use custom with a name parameter for any event not in this list.

Attribution fields
FieldDescription
source"apple_ads", "tracking_link", or "organic"
isAttributedfalse for organic installs, true otherwise
matchTypeBackend match method: apple_ads, idfa, idfv, gaid, ttclid, gclid, gbraid, wbraid, ip_user_agent, or organic
campaignNameSignal Campaign name when available
linkSignal link object: id, name
appleAdsApple AdServices payload: campaignId, adGroupId, keywordId, countryOrRegion, conversionType
utmSourceUTM source from the signal link
utmMediumUTM medium from the signal link
utmCampaignUTM campaign value from the signal link
API reference

configure(_:)

async

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

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.

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.

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.

let attr = await AppSprint.shared.refreshAttribution()

setCustomerUserId(_:)

async

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

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

getAppSprintId()

String?

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

let id = AppSprint.shared.getAppSprintId()

getAttribution()

AttributionResult?

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

let attr = AppSprint.shared.getAttribution()

getAttributionParams()

[String: String]

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

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.

AppSprint.shared.enableAppleAdsAttribution()

isInitialized

Bool

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

AppSprint.shared.isInitialized

isSdkDisabled()

Bool

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

AppSprint.shared.isSdkDisabled()

sendTestEvent()

TestEventResult

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

let result = await AppSprint.shared.sendTestEvent()

clearData()

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

AppSprint.shared.clearData()
Offline behavior
  • Events that fail to send are queued in native storage (up to 100 events).
  • The queue persists across app restarts.
  • Queued events are retried when configure() completes, when another event is sent, when lifecycle flushes run, 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.

Next steps

Troubleshooting

ProblemWhat to try
getAppSprintId() returns nilconfigure() 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 dashboardConfirm 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/403A rejected key disables the SDK permanently. Call clearData(), then configure() with a valid key.
Attribution returns organic when an Apple Ads install was expectedBackend 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 sendA 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.