FunnelMob

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.trackingAuthorizationStatus

The 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: true and never present the prompt, the SDK waits indefinitely.

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)
FieldEffect on the SDK
isUserSubjectToGDPRWhen false, the per-dimension flags are advisory and the SDK tracks normally.
hasConsentForDataUsageWhen false and isUserSubjectToGDPR == true, the SDK stops dispatching events/sessions/identify and purges its local queue.
hasConsentForAdsPersonalizationForwarded to ad networks (Google ad_personalization). Does not gate dispatch.
hasConsentForAdStorageForwarded 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.

When the user revokes consent (hasConsentForDataUsage: false with isUserSubjectToGDPR: true):

  • New trackEvent(...) calls are dropped.
  • Pending events are removed from memory and from UserDefaults persistence.
  • 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.

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.