Info
Content

[iOS] 4. Working with ATT (App Tracking Transparency / iOS)

Working with ATT (App Tracking Transparency) on iOS

This guide explains how to correctly integrate Apple’s App Tracking Transparency (ATT) framework with our CMP SDK. ATT is a platform-level requirement (Apple) while CMP manages legal/regulatory consent. Both must work together to ensure compliance and proper SDK behavior.

ATT and consent management are separate systems that you must coordinate manually. 

Step 1: Prerequisites

  • Minimum iOS version: iOS 14 (ATT not available on earlier versions)

  • Add the AppTrackingTransparency.framework to your project

  • In your Info.plist, add:

<key>NSUserTrackingUsageDescription</key> 
<string>This identifier will be used to deliver personalized ads and improve user experience.</string>
  • The string must clearly explain why you request tracking

Apple may reject vague wording.

All ATT handlig needs to be managed on the mobile app itself, and not through our SDK, as our SDK is not aware of the lifecycle of the mobile app integrating it. ATT request needs to be displayed and completely handled before our SDK, and the ATT dialog should not be loaded at the same time or on top of our consent layer. Make sure you only load our consent layer via checkAndOpen() or forceOpen() methods after handling the ATT request and making sure the user's choice regarding ATT was updated on the device already. Do not load our consent layer before that. 

Step 2: Check ATT Status

Check the current ATT authorization status before initializing tracking SDKs:

import AppTrackingTransparency
import AdSupport

if #available(iOS 14, *) {
    let status = ATTrackingManager.trackingAuthorizationStatus
    switch status {
    case .authorized:
        // IDFA available
    case .denied, .restricted:
        // IDFA blocked
    case .notDetermined:
        // Prompt will be shown later
    @unknown default:
        break
    }
}

 

Step 3: Request ATT Authorization

If status is .notDetermined, request authorization when the app is active:

if #available(iOS 14, *) {
    ATTrackingManager.requestTrackingAuthorization { status in
        DispatchQueue.main.async {
            self.didReceiveATTStatus(status)
        }
    }
} else {
    // iOS < 14: proceed to CMP directly
    openCMP()
}

Step 4: Handle ATT Result

Define how to process the ATT result and then launch CMP:

func didReceiveATTStatus(_ status: ATTrackingManager.AuthorizationStatus) {
    switch status {
    case .authorized:
        openCMP()
    case .denied, .restricted:
        rejectAdPurposesAndVendors {
            self.openCMP()
        }
    case .notDetermined:
        openCMP()
    @unknown default:
        openCMP()
    }
}

 

Step 5: Launch CMP

After ATT resolves, open the CMP UI to collect legal/regulatory consent:

private func openCMP() {
    CMPManager.shared.checkAndOpen()
}

Step 6: Decision Table — ATT × CMP Behavior × SDK Actions

ATT Status IDFA CMP Behavior SDK Actions
.notDetermined No Wait for ATT prompt, then launch CMP. Do not initialize trackers yet.
.authorized Yes Collect granular CMP consent. Enable ad/analytics only if CMP allows.
.denied No Treat advertising/profiling as declined. Reject ad purposes/vendors programmatically, then show CMP for non-tracking purposes.
.restricted No Same as Denied. Reject ad purposes/vendors, then open CMP.
iOS < 14 N/A Skip ATT, use CMP only. Block until CMP resolves.

Step 7: ATT-Aware API Recipes

Reject ad purposes/vendors when ATT denied or restricted

func rejectAdPurposesAndVendors(completion: @escaping () -> Void) {        
     let adPurposeIds = ["c52", "c53"]        // Example purposes        
     CMPManager.shared.rejectPurposes(adPurposeIds, updateVendor: true)  
}

(OR) Pre-grant ad purposes/vendors when ATT authorized

func pregrantAdPurposesAndVendorsThenOpenCMP() {
    let adPurposeIds = ["c52", "c53"]
    CMPManager.shared.acceptPurposes(adPurposeIds, updatePurpose: true)
}

 

Step 8: Re-check on App Resume

ATT can change in Settings. Always re-check on resume:


Step 9: Edge Cases and Fallbacks

  • Restricted: ATT unavailable (e.g. parental controls). Block tracking.

  • iOS < 14: No ATT → use CMP only.

  • Prompt unavailable: ATT must be requested when app is active.

  • User denial: Treat ad purposes/vendors as rejected.

Step 10: Testing & Debugging

  • Use a fresh install to trigger ATT prompt.

  • Test all states: .authorized, .denied, .restricted, .notDetermined.

  • Toggle ATT in Simulator/Physical device under Settings → Privacy & Security → Tracking.

  • Log outputs to confirm CMP launches only after ATT resolves.

Step 11: Best Practices

  • Never initialize trackers before ATT + CMP flow finishes.
  • Use pre-prompts wisely; never mislead users as it might lead to blocking.
  • Do not overlap CMP UI with ATT prompt.
  • Allow users to reopen CMP anytime.
  • Expect Apple Review rejection if tracking occurs before ATT permission.

Step 12: Developer Checklist

  • Add AppTrackingTransparency.framework.

  • Add NSUserTrackingUsageDescription to Info.plist.

  • Check ATT status at startup.

  • Optionally show pre-prompt.

  • Request ATT if .notDetermined.

  • Handle ATT result (accept/reject vendors/purposes accordingly).

  • Launch CMP after ATT completes.

  • Initialize/block SDKs only after CMP resolves.

  • Re-check ATT on app resume.

  • Provide CMP reopen option.

  • Test all flows.


References

Back to top