Skip to main content

Apple Search Ads Attribution

AppActor includes built-in Apple Search Ads (ASA) attribution tracking. When enabled, the SDK automatically captures attribution data, tracks purchases linked to ASA campaigns, and syncs user identity changes.

Overviewโ€‹

Enabling ASA Trackingโ€‹

Pass AppActorASAOptions when configuring the SDK in payment mode:

AppActor.configure(
apiKey: "pk_live_abc123",
options: .init(
asa: AppActorASAOptions(
autoTrackPurchases: true, // Automatically track purchase events
trackInSandbox: false, // Track sandbox events during development
debugMode: false // Enable verbose ASA logging
)
)
)
OptionDefaultDescription
autoTrackPurchasestrueAutomatically enqueue ASA purchase events when StoreKit transactions are processed
trackInSandboxfalseTrack sandbox/TestFlight/StoreKit test purchase events for debugging
debugModefalseEnable verbose ASA-specific logging

When options.asa is nil (the default), no ASA code runs and no ASA network calls are made.

How It Worksโ€‹

Attribution Flowโ€‹

  1. The SDK calls AAAttribution.attributionToken() from Apple's AdServices framework
  2. The token is sent to the AppActor backend along with device info
  3. The server resolves the attribution (campaign, ad group, keyword, etc.)
  • Organic installs (no token available): immediately marked as completed, no server call
  • Retries exhausted: retries on next app launch

Purchase Event Trackingโ€‹

When autoTrackPurchases is enabled, the SDK automatically links purchases to ASA campaigns:

Events are persisted to disk and survive app termination. The queue is flushed during bootstrap and when the app returns to the foreground.

User Identity Syncโ€‹

When users log in or out, the SDK syncs the identity change with the ASA system:

// This happens automatically when you call:
try await AppActor.shared.logIn(newAppUserId: "user_123")
// The SDK internally calls:
// asaManager.enqueueUserIdChange(oldUserId: "anon_abc", newUserId: "user_123")

Diagnosticsโ€‹

Check the status of ASA tracking:

// Point-in-time diagnostic snapshot
if let diag = await AppActor.shared.asaDiagnostics() {
print("Attribution completed: \(diag.attributionCompleted)")
print("Pending events: \(diag.pendingPurchaseEventCount)")
print("Debug mode: \(diag.debugMode)")

if let change = diag.pendingUserIdChange {
print("Pending identity: \(change.oldUserId) โ†’ \(change.newUserId)")
}
}

// Quick pending count
let count = await AppActor.shared.pendingASAPurchaseEventCount

Storageโ€‹

Key / FilePurpose
appactor_asa_attribution_completedPrevents redundant attribution calls
appactor_asa_pending_old_user_idOld user ID for pending identity change
appactor_asa_pending_new_user_idNew user ID for pending identity change
Library/Application Support/appactor/asa_events.jsonPurchase event retry queue (survives app termination)

Requirementsโ€‹

  • AdServices framework โ€” Available on iOS 14.3+. The SDK gracefully handles unavailability.
  • Payment mode only โ€” ASA tracking requires a backend connection.

Next Stepsโ€‹