Complete Guide to Implementing App Tracking Transparency (ATT) in iOS Apps
Apple's App Tracking Transparency (ATT) framework has fundamentally changed how iOS apps handle user tracking and advertising identifiers. Since iOS 14.5, apps must explicitly request user permission before accessing the Identifier for Advertisers (IDFA) or tracking users across apps and websites.
In this comprehensive tutorial, you'll learn how to properly implement ATT in your iOS app, handle different iOS versions, and navigate App Store submission requirements.
Prerequisites
Before implementing ATT in your app, ensure you have:
- Xcode 12 or later
- An iOS project targeting iOS 14.0 or later
- Basic understanding of Swift and iOS development
- Understanding of your app's tracking and advertising requirements
Understanding App Tracking Transparency
ATT requires apps to request explicit user consent before:
- Accessing the device's advertising identifier (IDFA)
- Tracking users across apps or websites owned by other companies
- Sharing user data with data brokers
Step 1: Configure Info.plist
First, add the tracking usage description to your Info.plist file. This message explains to users why your app needs tracking permission.
<key>NSUserTrackingUsageDescription</key>
<string>This app uses your advertising ID to provide personalized ads and improve your experience.</string>Important: Without this key, your app will likely be rejected during App Store review if it accesses IDFA.
Step 2: Import Required Frameworks
Import the necessary frameworks in your Swift files:
import AppTrackingTransparency
import AdSupportStep 3: Request Tracking Authorization
Here's the core implementation for requesting ATT permission:
func requestTrackingPermission() {
if #available(iOS 14, *) {
ATTrackingManager.requestTrackingAuthorization { status in
DispatchQueue.main.async {
switch status {
case .authorized:
// User granted permission
let idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
print("Tracking authorized. IDFA: \(idfa)")
// Initialize ad SDKs or analytics here
self.initializeAdServices()
case .denied:
print("User denied tracking permission")
// Handle denied state - use alternative attribution methods
self.handleTrackingDenied()
case .restricted:
print("Tracking permission restricted")
// Device or parental controls prevent tracking
self.handleTrackingRestricted()
case .notDetermined:
print("Tracking permission not determined")
// User dismissed the prompt without making a choice
@unknown default:
print("Unknown tracking authorization status")
}
}
}
} else {
// iOS 13 and below - ATT not required
handleLegacyIOSTracking()
}
}Step 4: Handle iOS 13 and Below
For devices running iOS versions prior to 14, ATT is not available, but you can still access IDFA directly:
func handleLegacyIOSTracking() {
if ASIdentifierManager.shared().isAdvertisingTrackingEnabled {
let idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
print("Legacy iOS IDFA: \(idfa)")
initializeAdServices()
} else {
print("User disabled ad tracking in settings")
handleTrackingDenied()
}
}Step 5: Check Authorization Status
You can check the current tracking authorization status at any time:
func checkTrackingStatus() {
if #available(iOS 14, *) {
let status = ATTrackingManager.trackingAuthorizationStatus
switch status {
case .authorized:
print("Tracking is authorized")
return true
case .denied, .restricted, .notDetermined:
print("Tracking is not authorized")
return false
@unknown default:
return false
}
} else {
// For iOS 13 and below
return ASIdentifierManager.shared().isAdvertisingTrackingEnabled
}
}Step 6: Handle Different Scenarios
When Permission is Granted
func initializeAdServices() {
// Initialize your ad SDKs here
// Example for Google AdMob:
// GADMobileAds.sharedInstance().start(completionHandler: nil)
// Example for Facebook SDK:
// Settings.shared.isAdvertiserTrackingEnabled = true
}When Permission is Denied
func handleTrackingDenied() {
// Use alternative attribution methods
// 1. SKAdNetwork for Apple's attribution framework
// 2. First-party data (user accounts, in-app behavior)
// 3. Device fingerprinting (where legally permitted)
// 4. Identifier for Vendor (IDFV) for same-publisher apps
let idfv = UIDevice.current.identifierForVendor?.uuidString
print("Using IDFV instead: \(idfv ?? "unavailable")")
}Step 7: Complete Implementation Example
Here's a complete implementation that you can use in your app:
import UIKit
import AppTrackingTransparency
import AdSupport
class TrackingManager {
static let shared = TrackingManager()
private init() {}
func requestPermissionIfNeeded() {
if #available(iOS 14, *) {
switch ATTrackingManager.trackingAuthorizationStatus {
case .notDetermined:
requestTrackingPermission()
case .authorized:
initializeAdServices()
default:
handleTrackingDenied()
}
} else {
handleLegacyIOSTracking()
}
}
private func requestTrackingPermission() {
ATTrackingManager.requestTrackingAuthorization { status in
DispatchQueue.main.async {
switch status {
case .authorized:
self.initializeAdServices()
case .denied, .restricted:
self.handleTrackingDenied()
case .notDetermined:
// User dismissed without choosing
break
@unknown default:
break
}
}
}
}
private func initializeAdServices() {
let idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
print("IDFA available: \(idfa)")
// Initialize your ad SDKs here
}
private func handleTrackingDenied() {
print("Using alternative tracking methods")
// Implement fallback strategies
}
private func handleLegacyIOSTracking() {
if ASIdentifierManager.shared().isAdvertisingTrackingEnabled {
initializeAdServices()
} else {
handleTrackingDenied()
}
}
}
// Usage in AppDelegate or ViewController
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Request permission when appropriate
TrackingManager.shared.requestPermissionIfNeeded()
}
}App Store Submission Considerations
If Your App Uses IDFA
- You must implement ATT
- You must include
NSUserTrackingUsageDescriptionin Info.plist - Apple's automated scanning will detect IDFA usage
If Your App Doesn't Use IDFA
- You don't need to implement ATT
- Don't include tracking-related code or frameworks
- Ensure third-party SDKs don't access IDFA
WebView and Cookies
Regarding your question about WebView and cookies: WebView content with cookies is generally separate from IDFA requirements. However:
- If your WebView content uses JavaScript to access device identifiers or cross-site tracking
- If you're sharing user data from WebView content with third parties for tracking purposes
- If you're using WebView in combination with native tracking
Then you might still need ATT implementation. The key factor is whether you're doing cross-app/cross-site tracking, not just the presence of cookies.
Best Practices and Tips
-
Timing Matters: Request permission at a natural point in your user journey, not immediately at app launch
-
Provide Context: Explain the benefits to users before showing the ATT prompt
-
Handle All Cases: Always have fallback strategies for when tracking is denied
-
Test Thoroughly: Test on different iOS versions and authorization states
-
SDK Integration: Ensure third-party SDKs are ATT-compliant and initialized after permission is granted
Summary
Implementing App Tracking Transparency involves several key steps:
- Configure Info.plist with usage description
- Import required frameworks (AppTrackingTransparency, AdSupport)
- Request authorization using ATTrackingManager
- Handle different authorization states appropriately
- Support legacy iOS versions (iOS 13 and below)
- Implement fallback strategies for denied permissions
Remember that ATT is about user privacy and transparency. By implementing it correctly, you're not only complying with Apple's requirements but also building trust with your users by giving them control over their data.
The key to successful ATT implementation is providing clear value propositions to users and having robust fallback strategies that don't compromise your app's core functionality when tracking is denied.
