Privacy Manifest Required Reason API Audit
Privacy manifests can feel like one more mysterious file Xcode wants you to appease before a release. The calmer way to handle them is to treat the work like a small audit: find the APIs your app actually uses, match them to Apple’s allowed reasons, check your SDKs, archive the app, and verify the result.
This is not legal advice, and it is not a replacement for reading Apple’s current docs. The goal here is practical: make the manifest describe your real code instead of vibes and copy-paste.
What PrivacyInfo.xcprivacy is
A PrivacyInfo.xcprivacy file is a property list that you add to an app or SDK target’s resources. Apple uses privacy manifests to describe data collection, tracking domains, and required reason API usage in a standard format. In Xcode, create one with File > New File, then choose the App Privacy file type under Resources. Xcode should name it PrivacyInfo.xcprivacy by default, which is the required bundled file name.
There are a few different parts a manifest can describe, but this post focuses on required reason APIs. Apple documents the root keys in Privacy manifest files, and the values need to come from Apple’s lists. Do not invent a category, reason code, or purpose string because it reads nicely.
The manifest is not your privacy policy, not a prompt shown to the user, and not a magic permission slip. It is a structured declaration. Your app target can have one, extensions and SDK targets may need their own, third-party SDKs can ship their own, and Xcode can aggregate them into a privacy report.
Know the required reason API categories
Apple’s required reason APIs are API families that Apple says can potentially be used for device fingerprinting. If your app or third-party SDK uses one of these categories on iOS, iPadOS, tvOS, visionOS, or watchOS, the bundled privacy manifest needs to describe the category and one or more approved reasons.
Apple’s documented categories are:
- File timestamps
- System boot time
- Disk space
- Active keyboards
- User defaults
The exact API names and reason codes live in Apple’s Describing use of required reason API documentation. Use that page as the source of truth, especially if you are reading this months later.
Each entry in the manifest has two important pieces:
NSPrivacyAccessedAPIType, the categoryNSPrivacyAccessedAPITypeReasons, the approved reason code or codes
So your job is not “add every possible reason and move on.” Your job is “find what we use, then choose the reason that honestly matches why we use it.”
Start with an inventory
Before editing the manifest, make a quick list of everything that ships in the app: the main app target, extensions, widgets, App Clips, Swift packages, CocoaPods or Carthage dependencies, manually embedded frameworks, XCFrameworks, and internal shared frameworks. Then ask two questions for each item:
- Does this target or SDK use a required reason API?
- If it is a third-party SDK, does the SDK provide its own valid manifest?
For your own targets, you can add or update the manifest yourself. For third-party SDKs, prefer updating to a version from the SDK developer that includes a valid manifest.
Search the codebase
Static analysis may catch things you missed, but I still like doing a boring text search first. The commands below are starter searches, not a complete copy of Apple’s required reason API list. Keep Apple’s documentation open and add symbols from the current list that matter to your app.
From the root of your repo, start with UserDefaults:
rg -n "UserDefaults|NSUserDefaults|@AppStorage|AppStorage" .
@AppStorage is worth including because it is a SwiftUI wrapper around defaults-style storage. If the app stores a theme choice, onboarding flag, or last selected tab in app-only defaults, Apple’s CA92.1 reason may match. If the app shares defaults through an App Group, look at Apple’s App Group reason instead. If you read managed configuration keys from MDM, check Apple’s managed configuration reason. Those are different claims.
Next search for file metadata and timestamp APIs. This catches common Swift, Foundation, and POSIX spellings, but Apple’s list includes more variants than this one line:
rg -n "creationDate|modificationDate|contentModificationDateKey|fileModificationDate|getattrlist|\\bstat\\(|\\bfstat\\(|\\blstat\\(" .
File timestamp usage is easy to misread. Reading a file is not automatically the same as accessing file timestamps. The audit question is whether you call APIs from Apple’s file timestamp category, such as URL resource keys for creation or modification dates, or stat-style APIs.
The reason depends on why you access that metadata. Displaying a document’s last modified date, checking metadata inside your app container, and inspecting a user-selected document can all map differently.
Finally, search the other categories. Again, treat this as a starting point and compare against Apple’s current list before declaring the audit complete:
rg -n "systemUptime|mach_absolute_time|volumeAvailableCapacity|systemFreeSize|systemSize|statfs|statvfs|activeInputModes" .
These results need human review. A timer that uses system boot time is not the same privacy story as putting boot time into a user-submitted bug report. Checking disk space before a large download is not the same as displaying available storage in settings.
Add only the entries you can defend
Here is a tiny example for an app that uses UserDefaults only for app-internal settings and reads file metadata inside its own app container. Do not paste this blindly. It is an example shape, not a universal manifest.
<?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>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>
When editing in Xcode, I like turning on raw keys and values so I can see the exact NSPrivacy... keys instead of friendly labels.
The manifest is target-specific. If your widget extension uses UserDefaults(suiteName:) to read App Group settings, the extension target’s bundled manifest should tell that truth too.
Check third-party SDK manifests and signatures
Third-party SDKs are the part where I slow down. Apple’s third-party SDK requirements page lists commonly used SDKs that require a privacy manifest, and signatures are required for listed SDKs when used as binary dependencies. Apple also says apps are responsible for the code they include, so “the SDK did it” is not a great release strategy.
At a high level, your audit should include:
- checking whether any dependencies are on Apple’s listed SDK page
- updating listed SDKs to versions that include privacy manifests
- keeping a note of which SDKs collect data so App Store privacy answers do not drift from reality
Your app’s manifest does not need to duplicate the data collection declared by a third-party SDK’s own manifest, but the app’s App Store privacy details still need to account for your app and integrated third-party partners.
For binary XCFrameworks, Xcode can show signature information and warn when the signing identity changes between SDK versions. Treat that as a supply-chain check that is separate from whether the privacy manifest’s claims are accurate.
Review the built artifact
After updating manifests, archive the app instead of only trusting the project navigator.
In Xcode:
- Choose Product > Archive.
- Open the archive in the Organizer.
- Control-click the archive and choose Generate Privacy Report.
- Save the report and open it in Preview.
Apple documents this flow in its data-use manifest docs. The report aggregates privacy manifests from the app and linked SDKs. I like reviewing it before release because it catches a different class of mistake than code search: missing target membership, stale SDKs, or a manifest that did not end up in the archive.
You can also inspect the archive directly. In Organizer, control-click the archive, choose Show in Finder, then show package contents. For an iOS app, the app-level manifest should be at the root of the built .app bundle, for example:
Products/Applications/MyApp.app/PrivacyInfo.xcprivacy
If App Store Connect sends an email about an invalid privacy manifest, use the path in that email to find the exact file in the archive. Then fix the source target or SDK and create a new archive.
What not to overclaim
Here is the part that saves future-you from awkward release notes:
- Do not declare reasons for APIs you do not use.
- Do not use a reason because it sounds close enough.
- Do not assume
UserDefaultsalways means the app-only reason. - Do not invent values when Apple’s list does not include your exact case.
If you cannot find a reason that matches your behavior, stop and read Apple’s docs more carefully, ask the SDK vendor, or change the implementation. Sometimes the cleanest fix is replacing a questionable API use with something simpler.
My release checklist
Before submitting, I would run through this list:
- Search the repo for every required reason API category.
- Review each result and write down the actual purpose.
- Add
NSPrivacyAccessedAPITypesentries only for categories you use. - Use exact category and reason values from Apple’s current documentation.
- Check every app, extension, widget, App Clip, and internal framework target.
- Update third-party SDKs that are on Apple’s required SDK list.
- Archive the app and generate the privacy report from Organizer.
- Inspect the built
.appif something looks missing. - Keep a short note in your release checklist explaining why each reason is present.
Privacy manifests are much less intimidating when they stop being a mysterious plist and become a small, repeatable audit. Search the code, choose reasons that match the actual behavior, keep SDKs current, and verify the archive before you upload. That is not glamorous work, but it is exactly the kind of boring release hygiene that keeps submission day calm.