iOS SDK
iOS SDK - Privacy & Consent
ATT helpers and GDPR / DMA consent for the FunnelMob iOS SDK.
The iOS SDK exposes two privacy primitives: App Tracking Transparency (ATT) helpers for Apple's IDFA prompt, and a setConsent API for GDPR / DMA. Both are opt-in — if you configure neither, the SDK tracks normally.
App Tracking Transparency (ATT)
Requesting authorization
import FunnelMob
import AppTrackingTransparency
FunnelMob.shared.requestTrackingAuthorization { status in
switch status {
case .authorized: print("ATT granted")
case .denied: print("ATT denied")
case .restricted: print("ATT restricted (parental controls)")
case .notDetermined: print("user dismissed without responding")
@unknown default: break
}
}This is a thin wrapper around ATTrackingManager.requestTrackingAuthorization. The SDK reads the resulting status itself — you do not need to manually pass it back.
Info.plist MUST contain a NSUserTrackingUsageDescription string. Without it, iOS will crash this call.
Reading the current status
let status = FunnelMob.shared.trackingAuthorizationStatusThe SDK reads ATT status fresh on every session payload it builds — so if the user changes the setting in iOS Settings, the next session reflects the new value automatically. Nothing to cache, nothing to re-set.
Waiting for ATT before the first session
The first session POST is the install/attribution event — sending it before the user has answered the ATT prompt means going out without IDFA, which weakens deterministic attribution. Set waitForATTAuthorization to defer:
let config = FunnelMobConfiguration(apiKey: "fm_live_abc123")
.with(waitForATTAuthorization: true)
FunnelMob.shared.initialize(with: config)
// later, e.g. after onboarding:
FunnelMob.shared.requestTrackingAuthorization { _ in
// SDK auto-resumes — events tracked while waiting flush now
}While waiting:
trackEvent(...)calls are buffered in memory (not dropped).- No session POST, no event flush, no remote config fetch happens.
- The wait ends as soon as ATT is determined (either via the SDK's wrapper above, or via the user toggling it in Settings — the SDK detects the change on
didBecomeActive). - There is no timeout. If you set
waitForATTAuthorization: trueand never present the prompt, the SDK waits indefinitely.
GDPR / DMA consent
Pass a FunnelMobConsent to record the user's decision. Field shape mirrors Google Consent Mode v2 and AppsFlyer's AppsFlyerConsent:
let consent = FunnelMobConsent(
isUserSubjectToGDPR: true,
hasConsentForDataUsage: true,
hasConsentForAdsPersonalization: false,
hasConsentForAdStorage: true
)
FunnelMob.shared.setConsent(consent)| Field | Effect on the SDK |
|---|---|
isUserSubjectToGDPR | When false, the per-dimension flags are advisory and the SDK tracks normally. |
hasConsentForDataUsage | When false and isUserSubjectToGDPR == true, the SDK stops dispatching events/sessions/identify and purges its local queue. |
hasConsentForAdsPersonalization | Forwarded to ad networks (Google ad_personalization). Does not gate dispatch. |
hasConsentForAdStorage | Forwarded to ad networks (Google ad_storage). Does not gate dispatch. |
setConsent can be called any time — before or after start(). When called, the SDK schedules a session re-fire so the backend learns the new consent state without waiting for the next app launch.
Consent revocation
When the user revokes consent (hasConsentForDataUsage: false with isUserSubjectToGDPR: true):
- New
trackEvent(...)calls are dropped. - Pending events are removed from memory and from
UserDefaultspersistence. - In-flight HTTP requests still complete (we can't recall data already on the wire). Server-side erasure is the deletion API's job, not the SDK's.
"Wait for everything" pattern (cookie banner / strict consent)
If you need the SDK to do absolutely nothing until the user has accepted a banner, use autoStart: false:
let config = FunnelMobConfiguration(apiKey: "fm_live_abc123")
.with(autoStart: false)
FunnelMob.shared.initialize(with: config)
// after the user accepts your banner:
FunnelMob.shared.setConsent(consent)
FunnelMob.shared.start()With autoStart: false, the SDK wires up internal state but performs no network activity until you call start(). This is stricter than setConsent alone and is the recommended pattern for hosts that need a pre-init consent gate.