You archived a new build, uploaded it to App Store Connect, and the ingestion email returned ITMS-91053 with the phrase Missing API declaration or Missing Provider for Required Reason API. The body of the email names a category such as NSPrivacyAccessedAPICategoryUserDefaults or NSPrivacyAccessedAPICategoryFileTimestamp, and your upload sits in the Invalid Binary state. The question is what App Store Connect actually inspected, which entry the upload was missing, and the smallest manifest change that gets the next build back to a green ingestion.
Short answer
App Store Connect returns ITMS-91053 when the compiled binary references a symbol on Apple's required reason API list and no PrivacyInfo.xcprivacy in the upload declares an approved reason code for the matching category. Five categories sit under the rule: UserDefaults, FileTimestamp, SystemBootTime, DiskSpace, and ActiveKeyboards. The fix is one approved reason code per affected category, written into the manifest of whichever target or framework links the symbol, then a fresh build number before the next archive. Apple has enforced the audit on new App Store and TestFlight submissions since 1 May 2024 per the Apple privacy manifest documentation.
What you should know
- The audit reads the compiled bundle, not the source. App Store Connect compares the linked symbol table against Apple's published list, so a clean grep across your Swift or Objective-C code can still fail.
- Five categories are in scope. UserDefaults, FileTimestamp, SystemBootTime, DiskSpace, and ActiveKeyboards each carry their own short list of approved reason codes.
- Every approved reason is a four-character code. CA92.1 and 1C8F.1 cover common UserDefaults cases, 7D9E.1 covers disk-space queries, 35F9.1 covers elapsed-time measurement on the boot clock, 3B52.1 covers user-visible file timestamps, and 54BD.1 covers active keyboard inspection.
- The ingestion email surfaces a category, not a symbol. Identifying which framework ships the symbol is a separate inspection step, usually run with the nm tool against each .framework binary in the uploaded .app.
- Third-party SDKs own their own manifests. Apple ships a list of around 80 SDKs that require a signed PrivacyInfo.xcprivacy of their own; the app-level file does not always cover every symbol.
- The build number has to change. Re-uploading at the same number leaves the Invalid Binary in place even after the manifest is correct.
Why does the audit fire on a binary that compiled cleanly in Xcode?
App Store Connect inspects the Mach-O symbol table of every executable and embedded framework after the archive uploads, not the Swift or Objective-C source. The audit pulls each symbol that resolves to a required reason API and checks whether any PrivacyInfo.xcprivacy in the upload declares a reason code for the matching category. If no manifest covers the symbol, the audit returns ITMS-91053 and the build moves into the Invalid Binary state.
The mechanism explains why the email often arrives with no obvious cause in your own code. A call to NSUserDefaults objectForKey from a transitively linked SDK is the same trigger as the same call from your AppDelegate. The compiler emits the symbol either way, the linker preserves it, and the audit reads it from the same table. Apple's TN3183 technote on required reason API entries describes the check as an API-level audit on the bundle, with no behavioural test attached.
Apple began enforcing the audit on new App Store and TestFlight submissions on 1 May 2024. Before that date the same diagnostic arrived as informational email and processing continued. After the cutover the bundle stays in the Invalid Binary state until a fixed manifest ships under a new build number.
Which categories and approved reasons cover ITMS-91053?
ITMS-91053 fires for any of the five required reason API categories. Each category has its own short list of approved codes, and the manifest entry names both the category and one or more codes from that list. Apple's Describing use of required reason API page is the authoritative reference.
| Category | Common approved reasons | Typical symbols in the binary |
|---|---|---|
| NSPrivacyAccessedAPICategoryUserDefaults | CA92.1 (in-app settings), 1C8F.1 (shared App Group), AC6B.1 (third-party SDK helper) | NSUserDefaults, objectForKey:, setObject:forKey: |
| NSPrivacyAccessedAPICategoryFileTimestamp | 3B52.1 (user-visible timestamps), 0A2A.1 (file management), C617.1 (third-party SDK helper) | NSFileManager attributesOfItemAtPath, getattrlist, stat, lstat |
| NSPrivacyAccessedAPICategorySystemBootTime | 35F9.1 (elapsed-time measurement), 8FFB.1 (absolute event timestamps), 3D61.1 (opt-in bug report) | NSProcessInfo systemUptime, mach_absolute_time, CACurrentMediaTime |
| NSPrivacyAccessedAPICategoryDiskSpace | 7D9E.1 (display free space to user), 85F4.1 (free-space check before write), B728.1 (third-party SDK helper) | statfs, NSURL volumeAvailableCapacityForImportantUsageKey |
| NSPrivacyAccessedAPICategoryActiveKeyboards | 54BD.1 (custom keyboard owner inspection) | UITextInputMode activeInputModes |
A single dictionary inside NSPrivacyAccessedAPITypes can carry more than one code under the same category when the binary legitimately spans cases. A timing utility that drives both a debounce and a crash-report timestamp would list both 35F9.1 and 8FFB.1 under NSPrivacyAccessedAPICategorySystemBootTime.
How do I find which SDK or framework triggered the audit?
The ingestion email names the category, not the framework. To trace the symbol back to the binary that ships it, run the nm tool against each linked framework inside the .app bundle. Symbols imported from outside the framework are marked with the letter U; symbols the framework defines carry a letter such as T or D.
xcrun nm -u YourApp.app/YourApp \
| grep -E "NSUserDefaults|system_uptime|getattrlist|statfs|activeInputModes"
for fw in YourApp.app/Frameworks/*.framework; do
echo "== $fw =="
xcrun nm -u "$fw/$(basename "$fw" .framework)" \
| grep -E "NSUserDefaults|system_uptime|getattrlist|statfs|activeInputModes"
done
The pattern depends on which category the email returned. UserDefaults symbols include NSUserDefaults and its objectForKey: selector. FileTimestamp symbols include the BSD calls getattrlist, stat, lstat, plus NSFileManager attributesOfItemAtPath. SystemBootTime symbols include mach_absolute_time and the lowercased system_uptime label that NSProcessInfo systemUptime resolves to. DiskSpace symbols include statfs and the volumeAvailableCapacityForImportantUsage key. ActiveKeyboards is rarer and is usually only triggered by custom-keyboard SDKs.
Apple maintains a published list of SDKs commonly using required reason APIs that includes roughly 80 entries, among them FirebaseAnalytics, FirebaseCrashlytics, FirebaseRemoteConfig, GoogleSignIn, Capacitor, Cordova, AppsFlyer, OneSignal, Sentry, SDWebImage, Kingfisher, IQKeyboardManager, RxSwift, and the Flutter runtime. Any project pulling one of those is a candidate until the SDK ships its own manifest.
Where should the PrivacyInfo.xcprivacy file live in my project?
The cleaner answer is that the manifest lives wherever the symbol does. When a framework ships its own PrivacyInfo.xcprivacy with the right entries, the app does not have to know about it; the audit reads every manifest in the upload and accepts whichever one covers the symbol. Several major SDKs followed that path after the 1 May 2024 cutover, including FirebaseAnalytics, FirebaseCrashlytics, GoogleSignIn, AppsFlyer, OneSignal, and Sentry.
When a framework has not shipped a manifest yet, App Store Connect accepts a declaration in the app target that covers symbols compiled anywhere in the upload. The trade-off is that the app maintainer owns the accuracy of the declaration. A 35F9.1 entry covering a vendor that later starts shipping absolute crash timestamps may need to grow to include 8FFB.1.
For cross-platform builds the manifest belongs in the runtime layer that owns the symbol. Capacitor 6 ships a default manifest inside Capacitor.framework, Cordova relies on a project-level file under platforms/ios, Flutter pulls a manifest from the Flutter.xcframework on recent stable releases, and B4i wired the file through the build process from v8.30 onward. In every case the build system writes the file into the compiled .app at archive time, so the change must precede the archive step, not the export step.
For builders who want an external automated read across every embedded framework before the next upload, PTKD.com (https://ptkd.com) is one of the platforms that inspects compiled IPA bundles for required reason API symbols and reports which frameworks ship a PrivacyInfo.xcprivacy and which do not. It shortens the loop between an Invalid Binary email and a clean ingestion.
What is the smallest fix that clears the Invalid Binary state?
Once you know the category and the owning target, the manifest entry itself is small. The file uses the .xcprivacy extension and lives at the root of the bundle that links the symbol. Inside, NSPrivacyAccessedAPITypes is an array of dictionaries, each pairing a category with one or more approved reason codes.
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
</array>
</dict>
Drop the file into the Xcode project, then check Target Membership in the File Inspector for the app target and any extension target that links the same symbol. Increment the build number in the General tab of the target settings. Archive, then Distribute App, then Upload. The ingestion job runs the audit again on the new bundle and either returns a green status or surfaces any remaining symbols not covered by the manifest. Apple's adoption guide for privacy manifests walks the same steps in reference form.
What to watch out for
The most common loop after a fix is uploading at the same build number. App Store Connect treats the existing Invalid Binary as the binary for that number and does not re-ingest a second upload at the same number. Increment the build number in the target settings, archive, then upload, so the audit runs against the new bundle.
The second loop is target membership. Dragging PrivacyInfo.xcprivacy into the project navigator is not enough; the File Inspector has to show the target checked under Target Membership. A manifest sitting in the project but outside the target never reaches the .app folder, and the next upload returns the same ITMS-91053.
The third loop is extension targets. Each .appex bundle (widget, App Clip, watch extension, intents extension) is audited independently from the main .app, so a manifest in the app alone does not cover the symbols compiled into the extension binary. Every extension target needs its own PrivacyInfo.xcprivacy with the matching entries.
A myth worth retiring is that removing first-party calls to the API clears the audit. The compiled binary keeps the symbol whenever any linked framework references it. Stripping the call from your own Swift or Objective-C code without updating the framework or the manifest leaves the rejection in place.
A final caveat applies to no-code platforms. FlutterFlow, Bubble Native, Adalo, and Thunkable each ship their own runtime stacks, and the audit fires when one of those stacks links a symbol the platform has not yet covered with a base manifest. The fix in those cases is either a platform release that updates the runtime PrivacyInfo.xcprivacy, or a project-level manifest added to the exported Xcode project, depending on which surface the platform exposes.
Key takeaways
- ITMS-91053 is an API-level audit on the compiled Mach-O, so the missing entry belongs in a PrivacyInfo.xcprivacy carried by the target or framework that links the symbol, not necessarily in your own code.
- Five categories sit under the rule (UserDefaults, FileTimestamp, SystemBootTime, DiskSpace, ActiveKeyboards), each with a short list of approved codes such as CA92.1, 3B52.1, 35F9.1, 7D9E.1, and 54BD.1.
- A manifest in the app target covers symbols from frameworks that have not shipped their own, but a vendor-shipped manifest is the cleaner long-term answer.
- Increment the build number after every manifest edit, confirm Target Membership in the File Inspector, and audit each extension bundle separately.
- Some teams outsource the pre-submission scan to an external platform such as PTKD.com, which inspects each embedded framework for required reason API symbols and reports the missing providers before App Store Connect does.



