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