Privacy

    How do I fix ITMS-91053 Missing API declaration on upload?

    App Store Connect ingestion email showing the ITMS-91053 Missing API declaration error with a required reason API category listed in the body

    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.

    CategoryCommon approved reasonsTypical symbols in the binary
    NSPrivacyAccessedAPICategoryUserDefaultsCA92.1 (in-app settings), 1C8F.1 (shared App Group), AC6B.1 (third-party SDK helper)NSUserDefaults, objectForKey:, setObject:forKey:
    NSPrivacyAccessedAPICategoryFileTimestamp3B52.1 (user-visible timestamps), 0A2A.1 (file management), C617.1 (third-party SDK helper)NSFileManager attributesOfItemAtPath, getattrlist, stat, lstat
    NSPrivacyAccessedAPICategorySystemBootTime35F9.1 (elapsed-time measurement), 8FFB.1 (absolute event timestamps), 3D61.1 (opt-in bug report)NSProcessInfo systemUptime, mach_absolute_time, CACurrentMediaTime
    NSPrivacyAccessedAPICategoryDiskSpace7D9E.1 (display free space to user), 85F4.1 (free-space check before write), B728.1 (third-party SDK helper)statfs, NSURL volumeAvailableCapacityForImportantUsageKey
    NSPrivacyAccessedAPICategoryActiveKeyboards54BD.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.
    • #itms-91053
    • #privacy manifest
    • #ios
    • #required reason api
    • #app store connect
    • #privacyinfo.xcprivacy
    • #invalid binary

    Frequently asked questions

    Why did the ITMS-91053 email arrive when my own code never calls those APIs?
    The audit reads the Mach-O symbol table of every executable and embedded framework, not your Swift or Objective-C source. A required reason symbol pulled in by a transitively linked SDK fires the rule the same way a direct call would. The email names the category that matched, leaving the inspection of which framework links the symbol as a separate step usually run with the nm tool against each .framework binary inside the uploaded .app.
    Is ITMS-91053 the same audit as ITMS-91055?
    They share the same enforcement source but differ in cause. ITMS-91053 fires when no PrivacyInfo.xcprivacy in the upload covers a required reason symbol. ITMS-91055 fires when a manifest declares a reason code that Apple does not recognise, often because of a typo such as CA92.1 written as CA921 or a category name spelt as NSPrivacyAccessedAPICategoryUserDefault without the trailing s. Both leave the build in Invalid Binary until the next upload.
    Do I have to wait for the SDK vendor to ship a manifest before I can submit?
    No. App Store Connect accepts a declaration in the app target that covers symbols compiled anywhere in the upload, so an app-side entry under the matching category passes the audit. The trade-off is that the app maintainer owns the accuracy of the declaration. A vendor-shipped PrivacyInfo.xcprivacy is the cleaner long-term answer because the framework knows which reason codes match its own calls.
    Why did App Store Connect keep returning ITMS-91053 after I added the manifest?
    Three causes account for almost all repeats. The build number was not incremented, so the audit re-read the same Invalid Binary record. Target Membership in the File Inspector was unchecked, so the manifest never reached the .app folder. An extension target (widget, App Clip, watch app) shipped without its own PrivacyInfo.xcprivacy, and each .appex bundle is audited independently from the main app binary.
    Does macOS need a privacy manifest for the same APIs?
    Not at the moment. Apple's required reason API rule applies to iOS, iPadOS, tvOS, visionOS, and watchOS submissions. A macOS app upload that calls NSUserDefaults or statfs does not currently trigger ITMS-91053. The same project can carry a PrivacyInfo.xcprivacy without harm, which is what cross-platform packages tend to do, but the audit only enforces it on the mobile surfaces.

    Keep reading

    Scan your app in minutes

    Upload an APK, AAB, or IPA. PTKD returns an OWASP-aligned report with copy-paste fixes.

    Try PTKD free