Special Year-End Offer: AI Review Toolkit $29.99 $49.99 Get it now →

iOS Submission Guide

Technical Guide iOS 14.5+

App Tracking Transparency: The Complete Implementation Guide

Since iOS 14.5, every app that wants to track users across other apps or websites needs explicit permission. Get this wrong and Apple will reject you. Get it right and you might actually get users to say yes.

📋 TL;DR - ATT Essentials

  • • ATT is required if you access IDFA or track users across apps/websites
  • • You must add NSUserTrackingUsageDescription to Info.plist
  • • Call requestTrackingAuthorization() before accessing IDFA
  • • Don't ask on first launch—build context first
  • • Industry average opt-in rate is around 25-35%

What is App Tracking Transparency?

App Tracking Transparency (ATT) is Apple's framework that requires apps to get user permission before tracking their activity across other companies' apps and websites. It was introduced in iOS 14.5 (April 2021) and fundamentally changed mobile advertising.

Before ATT, apps could freely access the IDFA (Identifier for Advertisers) to track users. Now, that identifier returns all zeros unless the user explicitly grants permission.

What Counts as "Tracking"

  • • Linking user data with third-party data for ads
  • • Sharing user data with data brokers
  • • Using IDFA for cross-app attribution
  • • Passing device identifiers to ad networks
  • • Building user profiles for targeted advertising

What's NOT Tracking

  • • First-party analytics (your own app data)
  • • Fraud detection
  • • Credit/lending eligibility checks
  • • Linking with data the user shared directly
  • • Contextual advertising (based on content, not user)

When Do You Need ATT Permission?

Not every app needs ATT. Here's a quick decision tree:

Do you need ATT?

Question 1: Does your app show ads from an ad network (AdMob, Facebook Audience Network, etc.)?

→ If yes, and you want personalized ads or attribution, you need ATT.

Question 2: Do you use Facebook SDK, Google Analytics for Firebase, or similar SDKs that access IDFA?

→ Check their documentation. Many still request IDFA for attribution even if you don't use ads.

Question 3: Do you share user behavior data with any third parties?

→ If that data could be linked with other data to identify users, you need ATT.

Question 4: Is your app purely offline, or only uses first-party analytics?

→ You probably don't need ATT. But double-check your SDK dependencies.

Common Gotcha

Many developers don't realize their analytics SDK requests IDFA behind the scenes. Run a privacy report in Xcode (Product → Analyze → Generate Privacy Report) to see what APIs your app and its dependencies access.

Implementation Guide

Step 1: Add the Usage Description

Add NSUserTrackingUsageDescription to your Info.plist. This is the text that appears in the permission dialog.

<key>NSUserTrackingUsageDescription</key>
<string>We use this to show you relevant ads and measure their effectiveness. Your data is never sold.</string>

Step 2: Request Permission

Use ATTrackingManager to request authorization. This must be called before you access IDFA.

import AppTrackingTransparency
import AdSupport

func requestTrackingPermission() {
    // Only request on iOS 14+
    guard #available(iOS 14, *) else {
        // Pre-iOS 14: IDFA is available without permission
        let idfa = ASIdentifierManager.shared().advertisingIdentifier
        initializeAds(with: idfa)
        return
    }

    // Check current status first
    let status = ATTrackingManager.trackingAuthorizationStatus

    switch status {
    case .notDetermined:
        // User hasn't been asked yet - request permission
        ATTrackingManager.requestTrackingAuthorization { newStatus in
            DispatchQueue.main.async {
                self.handleAuthorizationStatus(newStatus)
            }
        }
    case .authorized:
        // Already authorized
        let idfa = ASIdentifierManager.shared().advertisingIdentifier
        initializeAds(with: idfa)
    case .denied, .restricted:
        // User denied or restricted - use limited tracking
        initializeAdsWithoutTracking()
    @unknown default:
        initializeAdsWithoutTracking()
    }
}

func handleAuthorizationStatus(_ status: ATTrackingManager.AuthorizationStatus) {
    switch status {
    case .authorized:
        let idfa = ASIdentifierManager.shared().advertisingIdentifier
        print("IDFA: \(idfa.uuidString)")
        initializeAds(with: idfa)
    case .denied:
        print("Tracking denied by user")
        initializeAdsWithoutTracking()
    case .restricted:
        print("Tracking restricted (parental controls, etc.)")
        initializeAdsWithoutTracking()
    case .notDetermined:
        print("Status not determined")
        initializeAdsWithoutTracking()
    @unknown default:
        initializeAdsWithoutTracking()
    }
}

Step 3: Handle the Response

Your app needs to work regardless of whether the user grants permission. Plan for both scenarios:

If Authorized

  • • Pass IDFA to your ad SDK
  • • Enable personalized ads
  • • Allow full attribution

If Denied/Restricted

  • • Use contextual ads instead
  • • Rely on SKAdNetwork for attribution
  • • Don't degrade user experience

When to Show the ATT Prompt

Timing is everything. Ask too early and users will instinctively tap "Ask App Not to Track" without reading. Here's what actually works:

Don't: Ask on First Launch

Users don't trust apps they just downloaded. They haven't seen your value yet. Showing a tracking permission as the first thing screams "we want your data." Opt-in rates for first-launch prompts are typically below 15%.

Do: Wait for a Natural Moment

Show the prompt after the user has experienced value. Good moments include:

  • • After completing onboarding
  • • After their first meaningful action (saved something, completed a level)
  • • When they're about to see their first ad anyway
  • • After a few sessions, not the first one

💡 Pro Move: Pre-Permission Screen

Show your own custom screen BEFORE the system dialog. Explain why you're asking and what they get. "Allow tracking for personalized recommendations" converts better than a cold system popup. Just don't be manipulative or Apple will reject you.

App Review Warning

Apple explicitly prohibits "offering incentives for granting the request." You cannot offer coins, premium features, or discounts for allowing tracking. This will get you rejected under Guideline 5.1.1(v).

Writing Effective Permission Strings

Your NSUserTrackingUsageDescription text appears in the system dialog. It's limited to about 200 characters before truncation. Make them count.

Bad Examples

  • ❌ "Your data helps us improve." (Too vague)
  • ❌ "We need this for the app to work properly." (Misleading - app should work without it)
  • ❌ "Tap Allow to continue." (Will get you rejected)

Good Examples

  • ✓ "This helps us show you ads that match your interests instead of random ones."
  • ✓ "We use this identifier to measure ad effectiveness and show relevant content."
  • ✓ "Allow tracking to see personalized offers and recommendations tailored to you."

Key principles for high-converting permission strings:

  • Focus on user benefit — What do THEY get? Not what do you get.
  • Be specific — "Personalized ads" is better than "improve experience."
  • Be honest — Don't claim you need it if you don't.
  • Avoid fear — Don't say "or we can't show you anything."

Handling Users Who Decline

Expect most users to decline. Industry data shows about 65-75% will tap "Ask App Not to Track." Your app needs to handle this gracefully.

1

Don't Degrade the Experience

Users who decline tracking should still get a great app. Don't lock features, nag repeatedly, or make them feel punished.

2

Use SKAdNetwork for Attribution

Apple's SKAdNetwork provides privacy-preserving ad attribution. It's limited compared to IDFA, but it's what you've got.

3

Switch to Contextual Ads

Show ads based on content, not user profiles. A recipe app shows food ads. A fitness app shows workout gear. CPMs are lower but it works.

4

Don't Ask Again

You can only show the system dialog once. After that, the user must go to Settings to change it. You can remind them (gently), but you can't trigger the dialog again.

// Check if user can still be asked
func canRequestTracking() -> Bool {
    if #available(iOS 14, *) {
        return ATTrackingManager.trackingAuthorizationStatus == .notDetermined
    }
    return true // Pre-iOS 14, no permission needed
}

// Gently remind user they can enable tracking in Settings
func showTrackingReminderIfNeeded() {
    guard #available(iOS 14, *) else { return }

    if ATTrackingManager.trackingAuthorizationStatus == .denied {
        // Show a non-intrusive banner or message
        // "Want personalized ads? Enable in Settings > Privacy > Tracking"
        // But don't be annoying about it
    }
}

Frequently Asked Questions

Do I need ATT if I only use Firebase Analytics?

It depends on your configuration. Firebase Analytics can work without IDFA using first-party identifiers. But if you have Google Analytics for Firebase linked with Google Ads, or use Firebase's advertising features, you likely need ATT. Check the "Analytics data collection" settings in Firebase Console.

What happens if I access IDFA without requesting permission?

On iOS 14.5+, the IDFA will return all zeros (00000000-0000-0000-0000-000000000000). Your app won't crash, but your tracking won't work. Apple may also reject your app update if they detect you're accessing IDFA without proper ATT implementation.

Can I delay showing the ATT prompt indefinitely?

Yes, but if you ever want to use IDFA, you'll need to ask eventually. Some apps wait until the user engages with an ad-related feature. Just don't try to use IDFA before asking—it won't work.

Does ATT apply to my own first-party data?

No. ATT is specifically about cross-company tracking. You can still collect analytics about how users use your app, track in-app events, and personalize based on their behavior within your own app—all without ATT permission.

Why does my ATT prompt show before my pre-permission screen?

The system dialog appears when you call requestTrackingAuthorization(). Make sure you're presenting your custom pre-permission screen first, waiting for user interaction, and only THEN calling the API. Also check that you're not calling it in applicationDidFinishLaunching—it might fire before your UI is ready.

What if the user has "Allow Apps to Request to Track" disabled in Settings?

If the user has disabled tracking at the system level (Settings > Privacy & Security > Tracking), the authorization status will be .restricted. The system dialog will never show, and you'll never get IDFA access. Handle this the same as a .denied response.

Get Your App ATT-Ready

Our AI Review Toolkit includes prompts to audit your tracking implementation and ensure you're compliant with Apple's privacy requirements before submission.

Get the AI Toolkit

Want AI to audit your app before submission?

Get our AI Review Toolkit with prompts that catch guideline violations automatically.

Get the AI Toolkit