App Privacy Questionnaire Rejected / Inconsistent

Apple flagged your privacy nutrition labels as inconsistent with app behavior.

You finished the privacy questionnaire in App Store Connect, submitted the build, and the next morning the status flipped to Metadata Rejected with a message like “the data collection practices you described do not match the data your app collects.” Sometimes the rejection is more specific — “the app appears to collect Device ID for tracking, but Tracking is not declared” — and sometimes it’s a generic “please review your responses.”

Apple compares your declared answers against (a) what a reviewer sees when they run the binary through static analysis tools, (b) the SDKs they detect in the IPA, and (c) what your privacy manifest (PrivacyInfo.xcprivacy) says each SDK does. A mismatch on any of those three axes triggers the rejection.

Common causes

Ordered by hit rate. Most apps get hit by #1 or #2.

1. Third-party SDK collects data you forgot to declare

Analytics (Firebase, Amplitude, Mixpanel), ads (AdMob, Meta Audience Network), crash (Sentry, Crashlytics), and attribution (Adjust, AppsFlyer, Branch) all collect data on your behalf. Each one’s “data types” are listed in their privacy documentation, but you have to manually merge them into your own nutrition labels.

How to spot it: Open your Podfile.lock or Package.resolved, list every dependency, then visit each vendor’s privacy page (e.g., firebase.google.com/docs/ios/app-store-data-collection) and diff their declared types against your App Store Connect answers.

2. You declared “Not Collected” but the SDK proves otherwise

The most common version: you said you don’t collect Device ID, but AdMob/Meta SDK reads IDFA when the user grants ATT. Apple’s static scan catches the ASIdentifierManager call and the answer breaks.

How to spot it: Search the IPA’s symbols. nm -gU YourApp.app/YourApp | grep -i "ASIdentifierManager\|advertisingIdentifier". Any hit means IDFA is reachable and should be declared.

3. Missing “Linked to User” / “Used for Tracking” sub-answers

Each data type has two follow-ups: is it linked to the user’s identity, and is it used for tracking (cross-app/cross-website). People often answer the top-level type correctly but skip these. Apple rejects when the SDK’s manifest contradicts your sub-answer.

How to spot it: Open App Store Connect → App Privacy → for each data type, both follow-up toggles must match the SDK’s documented behavior.

4. Required reasons API not declared in PrivacyInfo.xcprivacy

Starting May 2024, certain APIs (UserDefaults, file timestamps, system boot time, disk space, active keyboards) need a declared reason in PrivacyInfo.xcprivacy. Missing manifest entries trigger a separate but related rejection (ITMS-91053 / ITMS-91056).

How to spot it: Xcode → Build phases → look for PrivacyInfo.xcprivacy in the resources. Open it and confirm NSPrivacyAccessedAPITypes covers every required-reason API your code or SDKs touch.

5. Contact info / health / financial data missed entirely

Apps with sign-in collect Email and Name. Apps with sharing collect “User Content.” Apps with subscriptions collect “Purchase History.” Skipping these because they “feel obvious” is a frequent rejection.

6. Privacy policy URL contradicts the questionnaire

Reviewer reads your policy URL and sees you mention Google Analytics, but the questionnaire says no analytics. Apple flags the mismatch even if the SDK isn’t bundled.

Before you change anything

  • Confirm whether the rejection is for the questionnaire only, the build, or both — the wording differs in App Store Connect.
  • Capture the exact reviewer message; some reasons (ITMS codes) require precise responses.
  • Back up your current questionnaire answers (screenshot every page) before editing — Apple does not version them.
  • Make sure you have access to App Store Connect with the “Account Holder” or “Admin” role; “Developer” cannot edit privacy.

Information to collect

  • Full reviewer message text and any ITMS-91xxx codes.
  • List of every SDK with version (from Podfile.lock, Package.resolved, or manual integrations).
  • Output of strings YourApp.app/YourApp | grep -i "tracking\|advertising\|idfa".
  • Your current privacy policy URL and the exact text mentioning data practices.
  • Build number and submission timestamp.

Shortest path to fix

Step 1: Build the SDK inventory

For each direct and transitive dependency:

# CocoaPods
cat Podfile.lock | grep -E "^  - " | sort -u

# SPM
cat YourApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved | jq '.pins[].identity'

For every entry, find the vendor’s “App Store Connect privacy guide” page. Build a table:

SDKData typesLinked to userUsed for trackingSource URL
Firebase AnalyticsIdentifiers, Usage DataYesNofirebase.google.com/…/data-collection
AdMobIdentifiers, DiagnosticsYesYesdevelopers.google.com/admob/…/privacy

Step 2: Reconcile with App Store Connect

Open App Store Connect → My Apps → your app → App Privacy → Edit. For each data type:

  1. Check “Yes, we collect this data.”
  2. Pick all uses (Analytics, App Functionality, Product Personalization, etc.).
  3. Set “Linked to User” Yes/No matching the SDK column.
  4. Set “Used for Tracking” Yes/No — if Yes, you must also call ATTrackingManager.requestTrackingAuthorization before reading IDFA.

Step 3: Update PrivacyInfo.xcprivacy for required-reason APIs

In Xcode, add a PrivacyInfo.xcprivacy file to your main target. Fill NSPrivacyAccessedAPITypes:

<key>NSPrivacyAccessedAPITypes</key>
<array>
  <dict>
    <key>NSPrivacyAccessedAPIType</key>
    <string>NSPrivacyAccessedAPICategoryUserDefaults</string>
    <key>NSPrivacyAccessedAPITypeReasons</key>
    <array>
      <string>CA92.1</string>
    </array>
  </dict>
</array>

The reason codes come from Apple’s Required Reason API list.

Step 4: Verify policy URL alignment

Open the privacy policy you submitted. Make sure every data category mentioned there appears in your questionnaire, and vice versa. If your policy says “we use Google Analytics,” analytics + identifiers must be ticked.

Step 5: Resubmit

In App Store Connect, after editing the questionnaire, click Save, then go to the build in App Store tab → Submit for Review. The questionnaire change does not require a new binary unless the rejection also cited the build.

How to confirm the fix

  • The App Privacy section in App Store Connect shows “Saved” timestamp matching today.
  • Reviewer status moves from Metadata Rejected back to Waiting for Review within 1-2 hours.
  • No new ITMS-91xxx warning emails arrive after upload.
  • For builds: TestFlight processing completes without privacy warning banner.

If it still fails

  1. Reply in Resolution Center quoting the exact SDK + data type pairs you reconciled, with vendor doc URLs.
  2. If the reviewer cites a specific symbol (e.g., advertisingIdentifier), search the IPA to confirm it’s actually called; if it’s dead code from an SDK, ask the vendor for a “data-collection-off” variant.
  3. Reduce to a minimal build: remove one SDK at a time and resubmit until the rejection clears, which identifies the culprit.
  4. Escalate via App Review Contact form with the IPA’s static analysis output as evidence.

Prevention

  • Maintain a PRIVACY.md in the repo listing every SDK and the data types it touches; update it in the same PR that adds the SDK.
  • Add a CI step that fails the build if PrivacyInfo.xcprivacy is missing or NSPrivacyTracking is true but no ATT prompt code exists.
  • Before each submission, run a 5-minute diff: questionnaire screenshots vs SDK inventory vs privacy policy text.
  • Pin SDK versions; new minor versions occasionally add new data collection without bumping major.
  • Subscribe to Apple’s Required Reason API changelog — the list grows quarterly.

Tags: #Troubleshooting #App Store #App review