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 — whether the user responds to the prompt or changes the setting elsewhere, the SDK detects it automatically.
  • 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. The four fields map directly to Google Consent Mode v2 — the industry-standard signal set every major ad network (Google Ads, Meta CAPI, TikTok Events) consumes for EEA conversions:

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.
  • Any pending events are purged from local storage.
  • Requests already in flight will finish — recalling data already on the wire requires server-side erasure, which is handled by the deletion API.

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.