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:
| SDK | Data types | Linked to user | Used for tracking | Source URL |
|---|---|---|---|---|
| Firebase Analytics | Identifiers, Usage Data | Yes | No | firebase.google.com/…/data-collection |
| AdMob | Identifiers, Diagnostics | Yes | Yes | developers.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:
- Check “Yes, we collect this data.”
- Pick all uses (Analytics, App Functionality, Product Personalization, etc.).
- Set “Linked to User” Yes/No matching the SDK column.
- Set “Used for Tracking” Yes/No — if Yes, you must also call
ATTrackingManager.requestTrackingAuthorizationbefore 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
- Reply in Resolution Center quoting the exact SDK + data type pairs you reconciled, with vendor doc URLs.
- 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. - Reduce to a minimal build: remove one SDK at a time and resubmit until the rejection clears, which identifies the culprit.
- Escalate via App Review Contact form with the IPA’s static analysis output as evidence.
Prevention
- Maintain a
PRIVACY.mdin 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.xcprivacyis missing orNSPrivacyTrackingistruebut noATTprompt 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.
Related
- App rejected for misleading claims
- App Review Guideline 2.1 info needed
- App Review 4.3(b) rejection
- Archive Upload Fails With Invalid Swift Support Error
- App Tracking Transparency Prompt Never Appears — Fix
- Fix ITSAppUsesNonExemptEncryption Missing Export Compliance
- App Store Screenshot Wrong Dimensions for iPad Pro — Fix
- App Crashes on Launch From Missing NSXxxUsageDescription