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/getpostback/postback-ios-sdk", from: "2.0.1")// CocoaPods. In your Podfile:pod "PostbackSDK", "2.0.1"
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 PostbackSDK// Call as early as possible. App.init() (SwiftUI) or AppDelegate.didFinishLaunching.Task {await Postback.shared.configure(PostbackConfig(apiKey: "pb_ios_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 eventawait Postback.shared.sendEvent(.login)
// Revenue event. Currency must be exactly three ASCII letters.await Postback.shared.sendEvent(.purchase, params: ["revenue": 9.99,"currency": "USD"])
// Custom event with parametersawait Postback.shared.sendEvent(.custom, name: "level_complete", params: ["level": 5,"score": 1200,])
- Custom events require a trimmed, non-empty name of 1-255 UTF-16 code units with no U+0000 character. Invalid custom events are not queued.
- For standard event types, an invalid optional name is omitted and the event is still queued.
- Currency must be exactly three ASCII letters. Lowercase values are normalized to uppercase; an invalid currency field is omitted and the event is still queued.
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 = Postback.shared.getAttribution()print(attr?.source) // "apple_ads", "tracking_link", or "organic"print(attr?.isAttributed) // Boolprint(attr?.campaignName) // Campaign name when availableprint(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 Postback dashboard within seconds.
let result = await Postback.shared.sendTestEvent()print("\(result.success) — \(result.message)")
Reference
Configuration options
| Option | Type | Default | Description |
|---|---|---|---|
| apiKey | String | required | Your iOS live API key (starts with pb_ios_live_). |
| apiURL | URL | https://api.postback.sh | 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. |
| isDebug | Bool | false | Forces logLevel = .debug. |
| logLevel | PostbackLogLevel | .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_completecustomUse custom with a name parameter for any event not in this list.
Attribution fields
| Field | Description |
|---|---|
| source | "apple_ads", "tracking_link", or "organic" |
| isAttributed | false for organic installs, true otherwise |
| matchType | Backend match method: apple_ads, click_id, gaid, ttclid, fbclid, gclid, gbraid, wbraid, ip_user_agent (Android only), or organic |
| campaignName | Signal Campaign name when available |
| link | Signal link object: id, name |
| appleAds | Apple AdServices payload: campaignId, adGroupId, keywordId, countryOrRegion, conversionType |
| utmSource | UTM source from the signal link |
| utmMedium | UTM medium from the signal link |
| utmCampaign | UTM campaign value from the signal link |
API reference
configure(_:)
→ asyncInitializes the SDK. Returns immediately; install registration runs in the background with retry on failure.
await Postback.shared.configure(PostbackConfig(apiKey: "…"))
sendEvent(_:name:params:)
→ asyncEnqueues an event locally and schedules a flush. Custom events require a trimmed name of 1–255 UTF-16 code units.
await Postback.shared.sendEvent(.purchase, params: ["revenue": 9.99, "currency": "USD"])
flush()
→ asyncDrains the queue immediately. Safe to call repeatedly; concurrent flushes are deduplicated.
await Postback.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 Postback.shared.refreshAttribution()
setCustomerUserId(_:)
→ asyncUpdates the customer user ID. Sent immediately if install is registered; otherwise queued and retried automatically.
await Postback.shared.setCustomerUserId("user-123")
getPostbackId()
→ String?Returns the install ID, or nil before install registration completes.
let id = Postback.shared.getPostbackId()
getAttribution()
→ AttributionResult?Returns the cached AttributionResult. Available synchronously without a network call.
let attr = Postback.shared.getAttribution()
getAttributionParams()
→ [String: String]Flat attribution/debug payload for custom integrations. For RevenueCat, set only the postbackId subscriber attribute.
let params = Postback.shared.getAttributionParams()
enableAppleAdsAttribution()
→ BoolRe-enables Apple Ads at runtime and sends the AdServices token via PATCH. Returns whether the SDK was configured.
Postback.shared.enableAppleAdsAttribution()
isInitialized
→ BoolTrue after configure() returns. Does not imply install registration succeeded.
Postback.shared.isInitialized
isSdkDisabled()
→ BoolTrue if a 401 or 403 from the backend permanently disabled the SDK.
Postback.shared.isSdkDisabled()
sendTestEvent()
→ TestEventResultPosts a diagnostic event and returns (success, message). Use during development.
let result = await Postback.shared.sendTestEvent()
clearData()
Wipes local state and the event queue. Use to reset the SDK.
Postback.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 callflush(). - 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.
- The SDK never requests ATT permission. IDFA is read only when the host app already has authorized ATT status; IDFV and app-scoped device context may be used for attribution.
- Apple Ads attribution comes from Apple's AdServices token and works independently of IDFA.
- Host apps remain responsible for their own privacy notices, App Store answers, and any permissions required by their complete data practices.
- Apple AdServices token fetch (AAAttribution.attributionToken) runs on iOS 14.3+. Failure is non-fatal; a fresh token is fetched on retry.
- URLSession requests fail fast when connectivity is unavailable. Events stay in the persistent SDK queue and retry on lifecycle flushes, the next sendEvent(), or flush().
- Background flushes are wrapped in beginBackgroundTask so iOS does not kill the queue worker mid-loop.
Next steps
Troubleshooting
| Problem | What to try |
|---|---|
| getPostbackId() 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 pb_ios_live_. Call sendTestEvent() and inspect the returned message. Check Console.app for [Postback] 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. |