Privacy

    How do I fix Missing API declaration for system_boot_time?

    App Store Connect rejection email referencing ITMS-91053 and NSPrivacyAccessedAPICategorySystemBootTime

    You uploaded a new build to App Store Connect, the processing email came back a few minutes later, and the body called out NSPrivacyAccessedAPICategorySystemBootTime. The previous build cleared TestFlight without complaint, you did not touch the parts of the code that read the clock, and the rejection still references ITMS-91053. The question is which APIs the system boot time category actually covers, which dependency in your project is calling them, and the smallest change that lets the next upload process.

    Short answer

    App Store Connect rejects builds that call system boot time APIs such as systemUptime, mach_absolute_time, and kern.boottime without declaring them in a PrivacyInfo.xcprivacy file. Add a single NSPrivacyAccessedAPICategorySystemBootTime entry to the manifest with one or more approved reason codes: 35F9.1 for in-app timers, 8FFB.1 for absolute event timestamps, or 3D61.1 for opt-in bug reports. Apple has enforced this for new submissions and TestFlight builds since 1 May 2024, per the Apple Developer privacy manifest documentation.

    What you should know

    • System boot time is one of four required reason API categories. The others are disk space, file timestamps, and user defaults. ITMS-91053 is the same error code for all four; only the category name in the email changes.
    • The scanner looks at symbols, not Swift source. A project that compiles cleanly on a Mac can fail on upload because a linked framework references the API at the Mach-O level.
    • CACurrentMediaTime counts. It is a wrapper over mach_absolute_time, so even purely visual code that drives animation timing falls inside the category.
    • Three approved reason codes apply. 35F9.1, 8FFB.1, and 3D61.1, and Apple permits more than one in the same NSPrivacyAccessedAPITypeReasons array.
    • The manifest belongs to the target that links the API. If your code calls mach_absolute_time, the app target needs the entry. If a vendor framework calls it, the framework needs its own manifest.
    • Adding the manifest changes no runtime behavior. It is metadata read during App Store Connect ingestion and surfaced in the Privacy Nutrition Label, with no new permission prompt.

    What does Apple count as a system boot time API call?

    The short answer is that Apple scans the compiled Mach-O for a set of symbols bound to this category and refuses to process a build that references any of them without a matching declaration. The category covers systemUptime on ProcessInfo / NSProcessInfo, the BSD function mach_absolute_time (and mach_continuous_time on platforms that expose it), and reads of kern.boottime through sysctlbyname. CoreAnimation's CACurrentMediaTime resolves to mach_absolute_time at runtime, so it is in scope by transitivity.

    The audit is static. App Store Connect inspects the binary, not your source files, which is why a project with no visible boot time call can still fail. Apple's TN3183 technote describes the rule as a symbol-level check and lists the entry points covered by each category. That distinction matters because it tells you where to look when the error arrives: the linker has pulled in a framework that calls one of these functions, and the framework either has no manifest or has one without the category entry.

    Which approved reason code matches my use?

    Apple publishes three approved reasons for NSPrivacyAccessedAPICategorySystemBootTime. Each reason name is a four-character code followed by a decimal index, and the Describing use of required reason API page defines the scope of each. You list one or several inside NSPrivacyAccessedAPITypeReasons depending on what your code actually does. Picking the right reason matters because two of the three explicitly limit where the value can travel.

    Reason codePurposeOff-device transmission allowed?
    35F9.1Measure elapsed time within the app, drive timers or animationsNo
    8FFB.1Calculate absolute timestamps of in-app events (UIKit, AVFAudio)No
    3D61.1Attach boot time to an optional, user-shown bug reportOnly with explicit user consent

    The practical rule of thumb is that 35F9.1 fits almost every animation, layout, scroll, or stopwatch path. 8FFB.1 fits audio engines, video pipelines, and analytics that stamp in-app events with a precise on-device timestamp. 3D61.1 is narrow and applies only when the user sees the bug report payload before sending it. If an SDK declares 35F9.1 and you can find a server endpoint that receives the value, the declaration is incorrect, which is what the Mysk research on the Required Reason API documented for several major apps in 2024.

    How do I find which third-party SDK triggered the rejection?

    A build that compiles cleanly but fails on upload usually carries the call inside a CocoaPods, Swift Package Manager, or vendored xcframework dependency. Three techniques narrow the suspect list quickly.

    The first is symbol inspection. After archiving, open the .ipa, navigate to the Frameworks folder, and run nm -u against each binary to list undefined symbols. Hits on _mach_absolute_time, _systemUptime, or _sysctlbyname flag a candidate. Apple's TN3183 technote recommends this exact workflow when the source of the call is unclear.

    The second is the SDK changelog. Major vendors began shipping privacy manifests in late April 2024 after Apple's hard cutover. The Firebase iOS SDK tracking issue shows when Firebase added a NSPrivacyAccessedAPICategorySystemBootTime entry to its core manifest. Sentry, AppsFlyer, Adjust, Datadog, Branch, and Bugsnag followed similar timelines. If your Podfile is pinned to a release dated before April 2024, bumping the version usually clears the rejection with no manual plist work in your own target.

    The third is process of elimination. Comment out a suspect SDK initialization in the AppDelegate, archive, and upload to a separate test app record. If the rejection disappears, the SDK is responsible. Slow, but useful for closed-source dependencies without a public changelog.

    What does the PrivacyInfo.xcprivacy entry look like in plist?

    The smallest valid manifest for a system boot time fix is a single dictionary inside the NSPrivacyAccessedAPITypes array. The keys are NSPrivacyAccessedAPIType, set to NSPrivacyAccessedAPICategorySystemBootTime, and NSPrivacyAccessedAPITypeReasons, set to an array of one or more reason strings. The example below declares 35F9.1 and 8FFB.1 together, which fits an app that measures animation time and stamps in-app events.

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
      <key>NSPrivacyAccessedAPITypes</key>
      <array>
        <dict>
          <key>NSPrivacyAccessedAPIType</key>
          <string>NSPrivacyAccessedAPICategorySystemBootTime</string>
          <key>NSPrivacyAccessedAPITypeReasons</key>
          <array>
            <string>35F9.1</string>
            <string>8FFB.1</string>
          </array>
        </dict>
      </array>
    </dict>
    </plist>
    

    If the app also touches disk space, file timestamps, or user defaults, those go into the same NSPrivacyAccessedAPITypes array as sibling dictionaries with their own reason codes. Keep the file flat and short. There is no benefit to declaring reasons the binary does not actually need, and any code that the linker drops can have its category removed later. After saving the file, open the File Inspector, scroll to Target Membership, and confirm the app target box is ticked. A manifest in the project navigator but outside the target membership is the most common reason ITMS-91053 returns after a fix.

    What to watch out for

    • A manifest in the wrong target. PrivacyInfo.xcprivacy in the project tree but not ticked for the app target means the bundle ships without it. App Store Connect sees no manifest and continues to reject. Verify by unzipping the .ipa and looking at the .app root.
    • A vendor framework that ships its own manifest, but a stale version. Vendors sometimes correct an incomplete declaration in a later release. If your Pods are locked, you may still ship the older, incomplete framework manifest, and ITMS-91053 returns even though the rest of your build is clean.
    • Reusing the same build number. App Store Connect deduplicates by version plus build. After fixing the manifest, increment CFBundleVersion before uploading, otherwise the Transporter call may succeed at the network layer while the same rejected binary stays in processing.
    • Picking 3D61.1 for an analytics SDK. 3D61.1 is for user-shown bug reports with explicit consent. Declaring it for a background analytics path is a misdeclaration, and the Mysk research showed several apps doing exactly that in 2024.
    • Treating the privacy manifest as a security audit. The manifest is a disclosure. It does not check whether secrets are hardcoded, whether the binary leaks API keys, or whether storage and network layers meet OWASP MASVS. Those live in a separate pass over the compiled artifact. For an external read of the IPA against MASVS before submission, PTKD.com (https://ptkd.com) is one platform focused on pre-submission scanning of compiled iOS and Android builds.

    Key takeaways

    • ITMS-91053 with NSPrivacyAccessedAPICategorySystemBootTime is a missing privacy manifest entry, not a code defect. Add the PrivacyInfo.xcprivacy file with one or more approved reason codes (35F9.1, 8FFB.1, or 3D61.1) and the upload will process again.
    • Match the reason code to the actual on-device use. 35F9.1 is the default for timers and animations, 8FFB.1 fits absolute timestamps inside the app, and 3D61.1 is only for opt-in user-shown bug reports.
    • Confirm the manifest is in the right target and actually shipped inside the .app bundle. The most common repeat-rejection cause is a file in the project tree but outside target membership.
    • If your own code does not call boot time APIs, look at third-party SDKs. Bump them to a release dated after April 2024, when major vendors began shipping their own manifests, before writing manual plist entries.
    • The privacy manifest is a disclosure, not an audit. For developers who want an independent read of the compiled binary against OWASP MASVS before submission, platforms like PTKD.com (https://ptkd.com) run that check on the IPA itself and flag manifest gaps along with secret storage and network issues.
    • #itms-91053
    • #privacy manifest
    • #ios
    • #app store connect
    • #system boot time
    • #required reason api
    • #nsprivacyaccessedapicategorysystemboottime

    Frequently asked questions

    Is the system_boot_time rejection a hard block or just a warning email?
    Since 1 May 2024 it is a hard block for new App Store submissions and TestFlight builds, per Apple's published enforcement timeline. Before that cutover the same diagnostic arrived as an informational email and the build still processed. Today the build is held in App Store Connect with the status Invalid Binary, and the developer cannot ship until the manifest is corrected and a new build is uploaded with a higher build number.
    Which reason code do I pick if my app only uses CACurrentMediaTime for animations?
    Use 35F9.1, which covers measuring elapsed time within the app. CACurrentMediaTime is a thin wrapper over mach_absolute_time, so the scanner flags it under the system boot time category. The 35F9.1 reason text says the value or anything derived from it must not be sent off-device. If your animation code never logs the timestamp to a server, you are within the rule.
    Can I declare more than one reason code in the same SystemBootTime entry?
    Yes. NSPrivacyAccessedAPITypeReasons is an array, and Apple's documentation states that more than one approved reason may be listed when the app or framework uses the category for several purposes. A common pattern is 35F9.1 paired with 8FFB.1 when the same code path measures elapsed time and also stamps event timestamps for in-app analytics that stay on device.
    Why does the rejection still mention system_boot_time after I added the manifest?
    Three common reasons. First, the PrivacyInfo.xcprivacy file sits in the project tree but is not ticked for the app target in File Inspector, so it never ships in the bundle. Second, the call lives inside a vendor framework that needs its own manifest, not yours. Third, the build number was not incremented, so App Store Connect kept the old binary in processing and reported the same error.
    Do I need this manifest if my app only links Apple frameworks?
    Yes if any first-party or system framework path you call resolves to mach_absolute_time, systemUptime, or kern.boottime. CACurrentMediaTime, DispatchTime.now(), and several CoreAnimation entry points reach those symbols. The required reason rule applies to your own code, not only to vendor SDKs. A single NSPrivacyAccessedAPICategorySystemBootTime entry in the app target manifest covers any combination of these symbols.

    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