Apple has been enforcing privacy manifests on new and updated App Store submissions since May 1, 2024. Almost two years later I still see indie developers rejected on their first submission because a SwiftUI-only app linked a Swift Package that uses a required-reason API without a proper manifest. The rules themselves are not complicated. The rejection emails are vague. This is the guide that compresses the whole story into a submission-ready checklist you can run through in 20 minutes.
Short version: your app needs a PrivacyInfo.xcprivacy file, every third-party SDK listed in Apple's Commonly Used list needs its own manifest (plus a signature on binary distributions), every required-reason API you call must declare a reason code, and you need to truthfully list what data your app collects and for what purpose. If you do all four, you pass. Details, the reason-code table, the SDK compliance matrix, and the common rejection causes below.
What Apple actually checks in 2026
App Review runs two automated scans against your build plus the submission metadata you provide.
- Binary scan for required reason APIs. Apple knows the symbols for every restricted API (file timestamps, disk space, system boot time, user defaults, active keyboards). If your binary or a framework inside your binary calls one and the corresponding manifest does not declare an approved reason code, you get rejected.
- Third-party SDK manifest + signature check. If your app links one of the SDKs on Apple's Commonly Used list and the SDK does not ship with a privacy manifest (and a valid signature on binary distributions), you get rejected. The list includes Firebase, Google Analytics, Sentry, Adapty, RevenueCat, Branch, and a few dozen others.
- Privacy details match your data collection. The Data Collection section in App Store Connect has to reflect what your app actually collects. If you log analytics events that include user identifiers but your App Privacy is "Data Not Collected," that is a rejection category on its own.
The good news: all three checks are deterministic. If you prepare the manifest correctly, sign it, and report honestly, App Review will not flag it.
Anatomy of a PrivacyInfo.xcprivacy
The manifest is a plist with four top-level keys. You add a file namedPrivacyInfo.xcprivacy to your target, Xcode 16 gives you a template with the keys already scaffolded.
NSPrivacyTracking: a boolean. True if your app or any SDK you link does tracking (IDFA, cross-site linking of user data). Most indie apps should set this carefully and only to true if they actually run tracking.NSPrivacyTrackingDomains: an array of domains your app contacts for tracking purposes. Only required ifNSPrivacyTrackingis true.NSPrivacyCollectedDataTypes: an array of structured entries. Each entry names a data type, whether it is linked to the user, whether it is used for tracking, and the purposes for which it is collected.NSPrivacyAccessedAPITypes: an array of required-reason API categories with the reason codes that apply to your use.
The four required-reason API categories
These are the only categories you need to think about in 2026. Every required-reason API you call falls into one of them, and each category has a finite set of approved reason codes. If none of the codes fit your use case, you cannot use the API, full stop.
| Category key | Covers | Representative APIs |
|---|---|---|
NSPrivacyAccessedAPICategoryFileTimestamp | File creation, modification, or access timestamps | .creationDate, .contentModificationDate, stat() |
NSPrivacyAccessedAPICategorySystemBootTime | Time since last reboot (device fingerprinting risk) | mach_absolute_time(), systemUptime |
NSPrivacyAccessedAPICategoryDiskSpace | Free or total disk space | systemFreeSize, volumeAvailableCapacity |
NSPrivacyAccessedAPICategoryActiveKeyboards | List of installed keyboards | UITextInputMode.activeInputModes |
NSPrivacyAccessedAPICategoryUserDefaults | Reading from UserDefaults | UserDefaults.standard.value(forKey:) |
Complete reason-code lookup table
These are the reason codes Apple accepts per category. Paste the matching code into your manifest. If your usage does not match any code, you need to remove the API call.
| Category | Code | When to use it |
|---|---|---|
| File Timestamp | C617.1 | Accessing timestamps on files your app created. |
| File Timestamp | 3B52.1 | Timestamps for files the user explicitly opened or shared with the app. |
| File Timestamp | 0A2A.1 | Displaying timestamps to the user in your UI (for files they own). |
| File Timestamp | DDA9.1 | Implementing sync protocols that compare modification dates. |
| System Boot Time | 35F9.1 | Measuring time intervals using boot-time-derived clocks. |
| System Boot Time | 8FFB.1 | Using boot time to invalidate caches or expire timers. |
| System Boot Time | 3D61.1 | Crash reporting (Sentry, Crashlytics internals). |
| Disk Space | E174.1 | Writing large assets and wanting to fail gracefully if disk is full. |
| Disk Space | 85F4.1 | Displaying disk space to the user (file manager UI). |
| Disk Space | B728.1 | Optimizing file operations based on available space. |
| Disk Space | 7D9E.1 | Caching downloaded content, adapting cache size. |
| Active Keyboards | 3EC4.1 | Custom keyboard extensions that inspect their own configuration. |
| Active Keyboards | 54BD.1 | Apps that provide features depending on the set of installed keyboards. |
| User Defaults | CA92.1 | Standard app configuration (this covers most indie apps). |
| User Defaults | 1C8F.1 | Sharing configuration across app extensions. |
| User Defaults | C56D.1 | Reading system-provided values (AppleLanguages, etc.). |
| User Defaults | AC6B.1 | Apps that provide defaults for managed configuration (EMM/MDM). |
Most indie SwiftUI apps end up with two declarations: CA92.1 (UserDefaults for app config) and either C617.1 or 3B52.1 (file timestamps). Third parties that crash-report or measure performance will cause additional entries, which their own manifests cover.
Top SDK compliance matrix
Apple maintains a Commonly Used list that currently includes roughly 80 SDKs. When you integrate one, you are responsible for confirming that the version you linked ships its own privacy manifest. Here is the state of the most popular ones for iOS indies in April 2026.
| SDK | Manifest status | Latest minimum version | Notes |
|---|---|---|---|
| Firebase (iOS SDK) | Compliant | 10.24.0+ | Each module (Auth, Firestore, Analytics) ships its own manifest since 10.22. |
| RevenueCat | Compliant | 4.31.0+ | Ships signed manifest; no action needed. |
| Adapty | Compliant | 2.9.0+ | Manifest plus signed XCFramework distribution. |
| OneSignal | Compliant | 5.1.0+ | Confirm you upgraded from 3.x, which does not ship a manifest. |
| Sentry Cocoa | Compliant | 8.33.0+ | Crash reporting uses System Boot Time with reason 3D61.1. |
| Mixpanel | Compliant | 4.2.5+ | Only if you also disable IDFA collection in init. |
| PostHog (iOS SDK) | Compliant | 3.14.0+ | Fully compliant manifest shipped early 2025. |
| TelemetryDeck | Compliant | 2.8.0+ | Minimal tracking footprint, minimal manifest. |
| Branch | Compliant | 3.2.0+ | Recent updates, pinning older versions is a rejection risk. |
| Facebook SDK for iOS | Compliant | 17.0.0+ | Sets NSPrivacyTracking to true; you must, too. |
| Superwall | Compliant | 4.0.0+ | Manifest shipped with their major v4 release. |
| Stripe iOS | Compliant | 23.28.0+ | Manifest covers file timestamps and user defaults. |
| Singular | Compliant | 12.3.0+ | Attribution SDKs require tracking declarations. |
| AppsFlyer | Compliant | 6.12.0+ | Same tracking declarations apply. |
| Anthropic Swift SDK | Compliant | 0.3.0+ | Thin HTTP client, minimal manifest surface. |
The pattern: most maintained SDKs shipped manifests through 2024. The risk is unmaintained packages, forks, and anything pinned to an old version. If you use a community SPM package that has not been updated in 18 months, check its manifest before submitting.
Generating the manifest in Xcode 16
Xcode 16 ships a "Privacy Manifest" file template under File, New, File. Add it to your app target (not to frameworks, unless you are shipping a framework). The template gives you the four keys with empty arrays. You fill them in based on what your app actually does.
For a typical SwiftUI indie app, a correct starter manifest looks like this.
<?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>NSPrivacyTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypes</key>
<array>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeCrashData</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<false/>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
</array>
</dict>
</array>
<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>
</array>
</dict>
</array>
</dict>
</plist>That covers UserDefaults for app config, file timestamps for files your app creates, and crash data collection. If your app also does analytics on user-linked data, you add an entry for NSPrivacyCollectedDataTypeProductInteraction with NSPrivacyCollectedDataTypeLinked set to true. The App Store Connect privacy questionnaire should mirror this manifest exactly.
The seven most common rejection reasons (and how to fix them)
- "ITMS-91053: Missing API declaration." A required-reason API is used without a matching category in your manifest. Find the API (usually indirectly called by a dependency), add the right category + reason code, resubmit.
- "ITMS-91056: Invalid reason code." You added a category but used a code that is not in the approved list for that category. Cross-reference the table above and replace.
- "ITMS-91065: Missing signature." A third-party SDK in binary form (XCFramework) is not signed with the required signature for its privacy manifest. Upgrade the SDK to the signed version. Most 2025-and-later versions are signed by default.
- "ITMS-91061: Missing privacy manifest." A third-party SDK on the Commonly Used list does not ship a manifest. Upgrade, switch to a compliant alternative, or contact the SDK vendor.
- "App Privacy declaration does not match data collection." You claimed "Data Not Collected" in App Store Connect while shipping an analytics SDK that collects user-linked identifiers. Update the App Privacy questionnaire to match reality.
- Tracking disclosure mismatch.
NSPrivacyTrackingis false but you link an SDK (Facebook, AppsFlyer) that tracks. Flip the bit and add tracking domains. - App Tracking Transparency missing. You track with
IDFAbut do not callATTrackingManager.requestTrackingAuthorizationbefore reading it. Add the ATT prompt with a usage description.
App extensions, widgets, and app clips
Every embedded target needs its own manifest. Your app target gets one, your Share Extension gets one, your Widget Extension gets one, your App Clip gets one. Xcode does not generate them automatically when you add a new target, so this is a common rejection cause.
For most extensions the manifest is a strict subset of the parent app's manifest. A Widget Extension almost certainly needs UserDefaults (1C8F.1 for shared defaults), no tracking, and no data collection. A Share Extension rarely needs anything beyond file timestamps. Copy the relevant parts of the parent and trim.
An audit script you can run before every submission
I keep a short Ruby script in every iOS project that scans the built app bundle for common required-reason API calls and compares against the manifest. It is not a substitute for Apple's validation, but it catches most issues pre-upload.
#!/usr/bin/env ruby
# scripts/privacy_audit.rb
# Runs in under a second on a release build.
require 'plist'
APP = ARGV[0] || "build/Release-iphoneos/YourApp.app"
MANIFEST = "#{APP}/PrivacyInfo.xcprivacy"
SYMBOLS = %x(nm -gU "#{APP}/YourApp" 2>/dev/null)
checks = {
"UserDefaults" => [/_OBJC_CLASS_._NSUserDefaults/,
"NSPrivacyAccessedAPICategoryUserDefaults"],
"FileTimestamp" => [/_getattrlist|_stat\b|_fstat\b/,
"NSPrivacyAccessedAPICategoryFileTimestamp"],
"BootTime" => [/_sysctl\b|_mach_absolute_time/,
"NSPrivacyAccessedAPICategorySystemBootTime"],
"DiskSpace" => [/_statvfs|volumeAvailableCapacity/,
"NSPrivacyAccessedAPICategoryDiskSpace"],
"ActiveKeyboards" => [/activeInputModes/,
"NSPrivacyAccessedAPICategoryActiveKeyboards"],
}
manifest = Plist.parse_xml(MANIFEST)
declared = (manifest["NSPrivacyAccessedAPITypes"] || []).map { |e|
e["NSPrivacyAccessedAPIType"]
}
checks.each do |label, (pattern, key)|
used = SYMBOLS =~ pattern
has = declared.include?(key)
if used && !has
puts "MISSING: #{label} API used but '#{key}' not in manifest"
elsif !used && has
puts "UNUSED: '#{key}' declared but no matching API detected"
end
endRun it as a post-build phase in your Archive scheme. It catches the case where a dependency upgrade silently introduces a new required-reason API without a corresponding manifest update. I have caught two rejections this way in the last 18 months.
What The Swift Kit ships
The Swift Kit ships a pre-built PrivacyInfo.xcprivacy template for the main app plus one for the included App Intents extension and the Widget Extension. Every bundled SDK (RevenueCat, Supabase Swift, TelemetryDeck, PostHog, OpenAI-Swift, Firebase Auth if enabled) is pinned to a version that ships a compliant manifest. The privacy audit Ruby script above is a post-build phase in the Archive scheme by default. You fill in your app's specific data collection, set NSPrivacyTracking honestly, and submit.
It is the submission-ready iOS foundation I wish I had before my first rejection email. $99 one-time, unlimited commercial projects. See every integration on the features page or jump to pricing.
Final checklist (print this before every submission)
PrivacyInfo.xcprivacyexists in the main app target, added to the app bundle in Build Phases.- Each App Extension, Widget, and App Clip has its own manifest.
- Every required-reason API your app or its dependencies call has a matching category and reason code.
- Every third-party SDK on the Commonly Used list is pinned to a version that ships a privacy manifest and a valid signature.
- App Store Connect App Privacy questionnaire matches your manifest exactly.
- If
NSPrivacyTrackingis true,NSPrivacyTrackingDomainslists the domains. - ATT prompt is wired and the usage description string is user-readable if you track.
- Audit script runs clean on the Archive build.
Final recommendation
Privacy manifests are not a high-risk feature if you treat them as a submission checklist item rather than an afterthought. Spend the 20 minutes per project to get the manifest right the first time, keep dependencies current, and treat rejection emails as actionable checklists not panic moments. Every single rejection cause I listed above has a clean one-line fix.
The pattern that goes wrong in 2026 is not new: indie developers upgrade an old project to iOS 26 without touching the manifest, pin a 2023-era SDK version, and get rejected. Keep the manifest alive as a living file with every release, and you never see a rejection from this category.