Privacy

    Which APIs are on Apple's privacy manifest required reason list?

    Side-by-side view of an Xcode PrivacyInfo.xcprivacy property list and an App Store Connect rejection email listing NSPrivacyAccessedAPICategoryUserDefaults under the required reason API list

    You uploaded an iOS build to App Store Connect, the processing screen sat for a minute, and an email landed in your inbox titled ITMS-91053. The body of the email lists one of five API categories: file timestamp, system boot time, disk space, user defaults, or active keyboard. Apple is telling you that your app called a system function on the required reason API list without a matching reason code in your privacy manifest. This page is the full read of that list, the approved codes for each category, and what you write inside PrivacyInfo.xcprivacy to satisfy the upload check.

    Short answer

    The required reason API list contains five categories: file timestamp APIs, system boot time APIs, disk space APIs, user defaults APIs, and active keyboard APIs. Each category has a small set of approved reason codes Apple defines. Declarations sit inside the NSPrivacyAccessedAPITypes array of PrivacyInfo.xcprivacy, one dictionary per category, each carrying an NSPrivacyAccessedAPIType string and an NSPrivacyAccessedAPITypeReasons array. According to Apple's news announcement on privacy updates for App Store submissions, the upload check has been mandatory since May 1, 2024.

    What you should know

    • The list has five categories. File timestamp, system boot time, disk space, user defaults, and active keyboards. Apple describes the list as open, so new categories can be added, but none have been added since the original cut in February 2024.
    • Each category needs at least one reason code. A reason code is a short alphanumeric string like CA92.1. Each code corresponds to a permitted use case. You pick the codes that match the call sites your binary actually contains.
    • The check runs on upload, not at runtime. App Store Connect inspects your binary and PrivacyInfo.xcprivacy together. The rejection email is automated and arrives within minutes.
    • Third-party SDKs declare their own. Per the App Store privacy manifest documentation, each SDK on Apple's published list ships its own PrivacyInfo.xcprivacy at the root of its bundle.
    • The ITMS-91053 email names the category. The body of the email contains the exact NSPrivacyAccessedAPICategory string the upload check could not match. That string tells you which dictionary is missing.
    • Many false positives come from CocoaPods. Per the Expo privacy manifest guide, Apple does not always parse static CocoaPods sub-manifests correctly, so codes from dependencies often have to be aggregated into the app's own manifest.
    • The codes are not interchangeable. Picking the wrong reason can pass the upload check but still violate Apple's rules of use for the API, which surfaces in later review.

    Why did Apple introduce the required reason API list?

    The required reason list addresses a tracking technique called fingerprinting. Per the Apple privacy manifest files documentation, a handful of system APIs return values that vary enough across devices (for example, the timestamp of the last system boot, or the creation date of a long-lived system file) that, combined, they can reconstruct a stable identifier without any access to the Identifier for Advertisers. The list groups the APIs Apple's privacy team observed being used this way in practice.

    The mechanism does not forbid the calls. It forces the developer to declare a non-tracking use case in advance. If the call sites match a declared reason, the build passes. If a call site exists with no declaration, the build is rejected at upload time. The check is enforced at upload rather than at runtime because the runtime cannot tell a fingerprinting use of, say, Date.modificationDate from a legitimate one.

    Per Apple's news post from February 29, 2024, Apple began sending warning emails on March 13, 2024, and the upload block began on May 1, 2024. Apps that shipped before that date keep working, but any new build (including an unrelated bug fix to a live app) has to pass the check.

    Which five API categories sit on the required reason list?

    The five categories carry these exact identifier strings inside NSPrivacyAccessedAPIType. The identifier is the value you put in the privacy manifest dictionary; it is also the string the ITMS-91053 email returns.

    API categoryManifest identifierWhat it covers
    File timestampNSPrivacyAccessedAPICategoryFileTimestampReading creationDate, modificationDate, attributesOfItem, file attribute methods on FileManager, and the stat() family.
    System boot timeNSPrivacyAccessedAPICategorySystemBootTimemach_absolute_time, systemUptime, clock_gettime(CLOCK_UPTIME_RAW), and similar uptime sources.
    Disk spaceNSPrivacyAccessedAPICategoryDiskSpacevolumeAvailableCapacityKey, statfs, and any volume free-space query.
    User defaultsNSPrivacyAccessedAPICategoryUserDefaultsAny read or write through UserDefaults, NSUserDefaults, or CFPreferences.
    Active keyboardsNSPrivacyAccessedAPICategoryActiveKeyboardsUITextInputMode.activeInputModes and any call returning the user's installed keyboards.

    Per Apple's documentation on required reason API entries, the list is described as open. Apple can add categories in the future. Since the cut on February 29, 2024, no additional category has been added.

    What are the approved reason codes for each category?

    Each category carries a short list of reason codes. You pick the code that matches your call site. Picking more than one is allowed and expected when a single category covers several distinct use cases inside the app. The codes most builds end up using are below.

    CategoryReason codePermitted use
    File timestampDDA9.1Display file timestamps to the device user. Information accessed for this reason cannot leave the device.
    File timestampC617.1Inspect file metadata inside the app's container, app group container, or CloudKit container.
    File timestamp3B52.1Use timestamps for file access policy or caching logic, on the device only.
    File timestamp0A2A.1Vendor declaration: a bundled third-party SDK uses the value internally and does not return it to your app process.
    System boot time35F9.1Calculate intervals between user events on the device.
    System boot time8FFB.1Read uptime inside an SDK that returns the value only to the app process.
    System boot time3D61.1Compute event timing for on-device analytics that does not leave the device.
    Disk space85F4.1Write content to disk only if enough space is available.
    Disk spaceE174.1Display disk space to the user.
    Disk space7D9E.1Detect a low-storage condition before a large download.
    Disk spaceB728.1Verify cache eviction thresholds inside the app.
    User defaultsCA92.1Read or write defaults that belong to the app only, no App Group involved.
    User defaults1C8F.1Read or write defaults shared via an App Group with apps the same team controls.
    User defaultsC56D.1Read defaults the system makes available globally.
    User defaultsAC6B.1Read or write defaults for managed configuration in MDM-deployed builds.
    Active keyboards3EC4.1Provide a custom keyboard inside the app.
    Active keyboards54BD.1Adjust UI based on the user's active keyboard, on the device only.

    The canonical list lives on Apple's developer site under "Describing use of required reason API". Each code is paired with a usage statement that constrains what the data can be used for after it is read. Reading the statement matters: picking DDA9.1 (display to the user) and then sending the timestamp to an analytics endpoint violates the declared use even though the upload check accepts it. App Review can flag the mismatch later.

    How do you declare each entry inside PrivacyInfo.xcprivacy?

    The file is a property list at the root of the app bundle: MyApp.app/PrivacyInfo.xcprivacy. The structure for the required reason section is a single NSPrivacyAccessedAPITypes key whose value is an array of dictionaries, one dictionary per category.

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

    For builds produced by Expo Application Services, the equivalent lives in app.json under expo.ios.privacyManifests. Per the Expo privacy manifest guide, the build pipeline emits the property list from that configuration during prebuild. For Capacitor projects, the file ships inside ios/App/App/PrivacyInfo.xcprivacy and Capacitor's project template includes a starter version. For a vanilla Xcode project, add the file through File, New, File, Privacy, and add the resulting file to the target's Copy Bundle Resources phase.

    Reason codes do not need to be listed in any particular order. Duplicates are accepted but ignored. The build check looks for at least one valid code per category your binary calls into.

    What if a third-party SDK does not include its own manifest?

    This is the most common failure mode at upload time. The category named in the ITMS-91053 email is being called by an SDK, not by code the developer wrote. Per Apple's third-party SDK requirements page, each SDK on Apple's published list (Firebase, Reachability.swift, Alamofire, OneSignal, and many others) is required to ship its own PrivacyInfo.xcprivacy at the root of its binary. SDKs added through Swift Package Manager and dynamic frameworks usually parse correctly. Static CocoaPods are the case where Apple does not always pick up the sub-manifest.

    The recovery is to add the SDK's declared reason codes into the app's own privacy manifest. Open the SDK's manifest inside node_modules/<package>/ios/PrivacyInfo.xcprivacy or inside the framework bundle, copy the dictionaries, and merge them into the app's NSPrivacyAccessedAPITypes array. The upload check is forgiving about duplication: aggregating codes from multiple sources into the app manifest is the recommended path.

    For developers shipping builds from no-code or AI-coded toolchains, the same logic applies. The compiled IPA contains whatever SDKs the builder injects, and the privacy manifest the builder generates may not enumerate every call site those SDKs use. PTKD.com (https://ptkd.com) is one of the platforms that scan an uploaded IPA against Apple's required reason list and report missing categories before the build reaches App Store Connect, which is useful for builders (FlutterFlow, Bubble, Capacitor wrappers around Replit Agent or Lovable exports) that do not surface their bundled SDKs in a readable way.

    What to watch out for

    Two patterns cause repeat rejections after a developer thinks the manifest is fixed.

    The first is reason-code drift. A developer adds CA92.1 for user defaults, ships, then later adds an App Group for shared widgets. The new call site needs 1C8F.1. The build passes locally but the upload check now sees a call without a matching reason. The fix is to read each code's usage statement on the canonical Apple page when you add a feature, not when you first set up the file.

    The second is sub-manifest aggregation. After a Pod update, an SDK adds a new call site under an existing category. The SDK's own manifest is updated, but if the build uses static CocoaPods, Apple's parser may not see the new code. The upload fails with the same category name as before. Per the Expo privacy manifest guide, the recommendation is to aggregate codes upward into the app manifest rather than rely on SDK manifests being parsed transitively.

    Two myths to reject. The required reason list does not extend to data collection types like location, contacts, or photos. Those sit in a separate key, NSPrivacyCollectedDataTypes, with their own rules. And declaring a category in the manifest does not exempt the call from App Tracking Transparency: if the data participates in cross-app tracking, ATT consent still applies separately.

    Key takeaways

    • The required reason list is five categories: file timestamp, system boot time, disk space, user defaults, active keyboards. Each category has between three and four approved reason codes you select from.
    • The check runs at App Store Connect upload, not at runtime. The rejection email (ITMS-91053) names the category. Match it to a dictionary inside NSPrivacyAccessedAPITypes, re-upload, and the check passes.
    • Pick the reason code by reading its usage statement on Apple's documentation page, not by guessing. Picking the wrong code passes the upload check but breaks Apple's rules of use, which surfaces later in App Review.
    • For builds where the developer cannot inspect every SDK source (FlutterFlow, Bubble, Capacitor wrappers around AI-coded outputs), aggregating codes upward into the app manifest is the path that survives Pod and SDK updates.
    • For an automated read of which categories a compiled IPA actually calls before submission, PTKD.com (https://ptkd.com) is one of the platforms focused on pre-submission scanning aligned with OWASP MASVS, useful when an SDK list is not transparent in the builder output.
    • #privacy-manifest
    • #required-reason-api
    • #itms-91053
    • #ios
    • #app-store-connect
    • #privacyinfo-xcprivacy
    • #ios-submission

    Frequently asked questions

    What is ITMS-91053 and how does it relate to the required reason list?
    ITMS-91053 is the email Apple sends when an App Store Connect upload includes a call into a required reason API category without a matching reason code in PrivacyInfo.xcprivacy. The body of the email names one or more NSPrivacyAccessedAPICategory strings. Each named string corresponds to a missing dictionary inside the NSPrivacyAccessedAPITypes array. The upload is blocked until you add the dictionary and re-upload the build.
    Do I need a privacy manifest if my app only uses Swift standard library and no third-party SDKs?
    Almost certainly yes. A SwiftUI app that reads UserDefaults for a setting, or a UIKit app that calls attributesOfItem to display a download timestamp, already triggers two of the five categories. The upload check does not care whether the call sits in your code or an SDK's code. If the compiled binary contains the symbol, you need a declaration inside the privacy manifest, and the check rejects the upload without it.
    Are the reason codes case-sensitive?
    Yes. The codes (CA92.1, 1C8F.1, 35F9.1) must be reproduced exactly as Apple publishes them, including the period and the trailing .1. The upload check string-matches against the canonical list. A code in lowercase or with a missing suffix is treated as an unknown code, which Apple's check counts as no code at all, and the build is rejected with the same ITMS-91053 email.
    Can I declare a category I do not actually use?
    Yes, but avoid it. Overdeclaration does not block the upload check. It does, however, signal call sites that Apple's reviewers may ask you to justify when a manual review happens. Pick the codes that match real call sites in your binary. If a feature is removed in a release, remove the corresponding code along with it so the manifest stays a faithful map of what the binary does.
    What is the difference between the required reason list and the privacy nutrition labels?
    The required reason list is a binary check at upload, covering five system API categories that need pre-declared use cases. The nutrition labels (the App Privacy section on the App Store product page) declare what data your app collects from the user, like location or contacts. The two systems share a file (PrivacyInfo.xcprivacy) but use different keys: NSPrivacyAccessedAPITypes for the required reason list, NSPrivacyCollectedDataTypes for nutrition labels.

    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