Ship a privacy manifest and declare required-reason APIs
This is an enforced App Store submission gate, not best-practice advice. Since May 1, 2024, App Store Connect rejects uploads that use a "required-reason" API without a declared reason, or that bundle a listed third-party SDK lacking a signed manifest. Treat a missing or incomplete PrivacyInfo.xcprivacy as a build-breaking error, not a warning.
What the manifest is
- A property-list resource named exactly
PrivacyInfo.xcprivacy, added to the app target (and to each framework/SDK target that needs its own). - For an app, place it at the top level of the app bundle. For a framework or SDK, place it inside the bundle so it ships with the binary.
- The file declares four things via these top-level keys:
NSPrivacyTracking(Boolean) — whether the app/SDK uses data for tracking as defined by App Tracking Transparency.NSPrivacyTrackingDomains(array) — internet domains the app connects to for tracking; required ifNSPrivacyTrackingistrue.NSPrivacyCollectedDataTypes(array) — each data type collected, its purposes, whether linked to identity, and whether used for tracking.NSPrivacyAccessedAPITypes(array) — required-reason APIs the code calls, each paired with an approved reason code.
Required-reason API categories
An agent can call these unknowingly through ordinary Foundation usage. Each category has its own NSPrivacyAccessedAPIType string and a fixed set of allowed reason codes (NSPrivacyAccessedAPITypeReasons):
| Category | Common triggers an agent writes | Manifest API type |
|---|---|---|
| File timestamp | attributesOfItem, contentModificationDate, creationDate, stat, getattrlist |
NSPrivacyAccessedAPICategoryFileTimestamp |
| System boot time | systemUptime, mach_absolute_time for boot-relative timing |
NSPrivacyAccessedAPICategorySystemBootTime |
| Disk space | volumeAvailableCapacityKey, statfs, free-space checks |
NSPrivacyAccessedAPICategoryDiskSpace |
| Active keyboard | reading the user's active keyboard list | NSPrivacyAccessedAPICategoryActiveKeyboards |
| User defaults | UserDefaults / NSUserDefaults access |
NSPrivacyAccessedAPICategoryUserDefaults |
- You MUST declare every category your own code reaches, with at least one valid reason code per category. Invented reason codes are rejected — use only codes Apple lists for that category (see TN3183).
- You MUST NOT add a category you do not actually use to "be safe"; declarations are scoped to real usage.
Required practice
- The app MUST ship a
PrivacyInfo.xcprivacymanifest declaring required-reason API usage and tracking/data-collection before submission. - Before opening a PR that touches Foundation file/disk/uptime calls,
UserDefaults, or adds a dependency, you MUST verify the manifest still covers all reached categories. - You MUST match
NSPrivacyCollectedDataTypesto the data the app actually collects; it SHOULD stay consistent with the App Store Connect privacy "nutrition label". - Third-party SDKs on Apple's list of commonly used SDKs MUST ship their own signed
PrivacyInfo.xcprivacy; you MUST NOT declare on their behalf. If a pinned SDK lacks one, update to a version that includes it or remove the dependency.
Finding required-reason usage in code
- Search the source and dependencies for the trigger symbols, e.g.
cc-grep(orgrep -rE) for:contentModificationDate|creationDate|attributesOfItem|systemUptime|volumeAvailableCapacity|statfs|UserDefaults|NSUserDefaults. - Inspect compiled SDKs: a manifest's reasons must cover transitively linked binaries, so scan vendored
.framework/.xcframeworksymbols, not just first-party Swift. - After a clean Archive build, confer the Privacy Report in Xcode's Organizer (or
xcrunprivacy tooling) to aggregate manifests across the app and its bundles, and reconcile gaps before submitting.
Notes
- The manifest is additive and durable: declaring an unused category or a missing reason both cause rejection, so keep it minimal and accurate.
- Apple may extend the required-reason API list over time; re-check the official docs when adding new low-level Foundation calls. Treat any not-yet-published category change as a forecast until it appears in Apple's documentation.