Vedran Burojevic
← WritingJuly 19, 2026

Privacy manifest (PrivacyInfo.xcprivacy): what Apple actually requires and how to avoid rejection

What belongs in PrivacyInfo.xcprivacy, how to audit third-party SDKs for required declarations, and common rejection patterns that stall release.

On this page

Privacy manifests are not hard because the file format is complex.

They are hard because the requirement sits at the intersection of app code, third-party SDKs, App Store Connect validation, privacy labels, dependency management, and build output. That is exactly where small release mistakes survive longest.

The practical job is simple: make the submitted app bundle honestly describe collected data, tracking domains, and required reason API usage across your app and the SDKs you ship.

If that statement sounds boring, good. Privacy manifest work should be boring. App Review drama is an inefficient substitute for a checklist.

1. Know what the privacy manifest is for

PrivacyInfo.xcprivacy is a property list bundled with an app or third-party SDK.

It can declare four top-level areas:

  1. whether the app or SDK uses data for tracking
  2. tracking domains used for tracking behavior
  3. data types collected by the app or SDK
  4. required reason API categories used by the app or SDK

Apple introduced this so App Store submissions have a machine-readable declaration for privacy-relevant behavior, especially behavior coming from dependencies. The point is not just the app target you wrote last week. It is the code you ship, including frameworks, Swift packages, binary SDKs, and libraries from other vendors.

For iOS apps, the part that most often blocks releases is required reason API usage. These are APIs Apple considers fingerprinting-sensitive if misused. If the app or an SDK accesses one of those API categories, the relevant bundle needs to declare the category and approved reason.

The key phrase is relevant bundle.

If your app code uses a required reason API, declare it in the app's privacy manifest. If a third-party SDK uses one, the SDK needs its own manifest. An SDK cannot rely on the app's manifest to report the SDK's usage. That detail is where many release surprises start.

2. Treat it as a release artifact, not a paperwork file

A privacy manifest is part of the build output.

That means it should be reviewed like any other release-critical artifact:

  • stored in source control
  • assigned to the correct target
  • included in the final bundle
  • checked when dependencies change
  • verified before App Store upload
  • updated when privacy behavior changes

Do not leave it as a last-minute Xcode file added by whoever was closest to the release button.

That is how teams end up with a manifest in the repository but not in the app target, a manifest in the app but not in the embedded framework, or a declaration copied from a blog post that does not match the code. The file exists. The release still fails. Everyone gets to enjoy the worst kind of progress: visible, confident, and wrong.

A useful review question is:

If we archive this exact build, what privacy manifests are actually inside the submitted bundle?

Not what should be there. Not what the package claims. What is inside the archive.

3. Understand the required reason API shape

Required reason API declarations live under NSPrivacyAccessedAPITypes.

Each entry declares:

  • NSPrivacyAccessedAPIType: the required reason API category
  • NSPrivacyAccessedAPITypeReasons: the approved reasons for using that category

The current required reason API categories include:

  1. NSPrivacyAccessedAPICategoryActiveKeyboards
  2. NSPrivacyAccessedAPICategoryDiskSpace
  3. NSPrivacyAccessedAPICategoryFileTimestamp
  4. NSPrivacyAccessedAPICategorySystemBootTime
  5. NSPrivacyAccessedAPICategoryUserDefaults

A simplified shape looks like this:

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

The exact reason code must match an approved reason for that API category. Do not treat reason codes as decorative constants. Pick the reason that describes the actual use.

If the approved reasons do not fit, that is not a copywriting problem. It means the API use may not be acceptable for that purpose, and the implementation needs to change.

4. Start with your own app code

Before blaming SDKs, audit the app target.

Common places to check:

  • UserDefaults and wrappers around preferences
  • file metadata APIs used for cache invalidation
  • disk space checks before downloads, imports, exports, or recording
  • system uptime or boot time logic used for timing, diagnostics, or session behavior
  • keyboard state checks in custom input handling

UserDefaults is the category many teams hit first because it is everywhere. That does not mean every use is suspicious. It means the app needs to declare the approved reason that fits the use.

Examples that usually have a clear product reason:

  • storing user preferences
  • remembering onboarding completion
  • keeping local feature configuration
  • caching non-sensitive app state
  • preserving interface settings

Examples that deserve more scrutiny:

  • combining defaults values with other device signals
  • using stored values as an identifier substitute
  • sharing defaults across unrelated components without ownership
  • letting analytics code read preference state without a clear purpose

The manifest should describe intended product behavior, but the audit should inspect actual code paths. If a helper wraps UserDefaults, search for the helper. If a vendor SDK abstracts persistence, inspect the vendor manifest and release notes. If app code reads file timestamps through a small utility, search call sites instead of the API name alone.

Release checks fail on what the binary uses, not on what the team remembered to search for.

5. Audit third-party SDKs like you own the shipment

Apple's position is straightforward: developers are responsible for all code included in their apps.

That includes third-party SDKs.

For each dependency, identify:

  1. whether it is on Apple's commonly used SDK list
  2. whether it ships a privacy manifest
  3. whether it is added as source, Swift package, XCFramework, static library, or dynamic framework
  4. whether it uses required reason APIs
  5. whether binary SDK signatures are required
  6. whether the version in your app is new enough to satisfy current requirements

The dangerous dependency is not always the one you import directly.

A direct SDK may bring transitive packages. A vendor may embed another framework. A Swift package may include a binary target. A dependency may have fixed privacy manifest support in a later patch release while your project remains pinned to a version from the previous geological era.

For small teams, a practical dependency inventory can be plain text:

Dependency privacy audit
├── FirebaseAnalytics
│   ├── source: Swift Package Manager
│   ├── version: 10.x
│   ├── manifest present: yes
│   └── notes: verify transitive packages after updates
├── RevenueCat
│   ├── source: Swift Package Manager
│   ├── manifest present: yes
│   └── notes: check release notes before App Store submission
└── VendorBinarySDK
    ├── source: XCFramework
    ├── manifest present: pending vendor confirmation
    ├── signature required: yes
    └── release risk: high

This is not ceremony. It is the difference between a controlled release and a Slack thread asking why App Store Connect blocked the build.

6. Check the archive, not just the project navigator

Xcode can show a file that never reaches the submitted archive.

Before release, inspect the built product:

  1. Archive the app.
  2. Open the archive in Organizer.
  3. Export or inspect the .xcarchive contents.
  4. Confirm the app bundle includes PrivacyInfo.xcprivacy where expected.
  5. Confirm embedded frameworks and dynamic libraries include their own manifests when required.
  6. Confirm the manifest content matches the current code and dependencies.

For command-line checks, inspect the archive or exported app bundle directly:

find MyApp.xcarchive -name PrivacyInfo.xcprivacy -print

That tells you which manifests made it into the artifact. It does not prove every declaration is correct, but it catches the embarrassing class of failure where the file was created, reviewed, discussed, and then not shipped.

You can also use plutil to validate the property list:

plutil -lint PrivacyInfo.xcprivacy

Run this for manifests you own. For generated or vendor-provided bundles, inspect the final artifact. Trust is nice. Verification is how releases survive contact with tooling.

7. Keep privacy labels and manifests aligned

Privacy manifests do not replace App Store privacy labels.

They support a different part of the privacy story. The manifest describes declared collection, tracking, and required reason API usage in bundled code. App Store privacy labels describe what the app collects and how data is linked or used in the App Store listing.

These two should not contradict each other.

When updating the manifest, check whether the App Store privacy answers also need attention:

  • new analytics data collected
  • new contact info, identifiers, diagnostics, location, or usage data
  • new tracking domains
  • a new SDK that collects data
  • a feature that changes whether data is linked to the user
  • a server-side change that changes how collected data is used

This is where release engineering and product responsibility overlap. If the app collects new data but only the code changed, the release is not done. The metadata changed too.

A good release checklist has a privacy row. Not a vague “privacy?” checkbox. A real one:

  1. App manifest updated.
  2. SDK manifests present.
  3. Required reason APIs declared.
  4. Tracking domains reviewed.
  5. App Store privacy labels reviewed.
  6. Vendor release notes checked.

Six boring checks beat one urgent rejection.

8. Common rejection and warning patterns

Most privacy manifest problems are not mysterious. They are operational misses.

Common patterns:

  1. Missing required reason declaration. The app or SDK uses a covered API category, but the manifest does not include the matching NSPrivacyAccessedAPITypes entry.
  2. Wrong bundle owns the declaration. The app manifest declares something used inside an SDK, but the SDK bundle needs its own manifest.
  3. Stale SDK version. A dependency added privacy manifest support later, but the app is pinned to an older release.
  4. Binary framework signature problems. A newly added commonly used third-party SDK is included as a binary dependency without the expected signature.
  5. Manifest not included in target membership. The file exists in Xcode but does not ship in the final app or framework bundle.
  6. Copied reason codes. The manifest contains a reason code that does not match the actual API category or use.
  7. Transitive dependency surprise. A package brings in another SDK that has its own privacy requirement.
  8. Privacy labels drift. The manifest and App Store privacy answers tell different stories after a feature or SDK change.

The fix is rarely to guess a new reason code and upload again. That is release roulette with a nicer UI.

The fix is to identify the binary or bundle App Store Connect is complaining about, map the API category to the code or dependency using it, then update the manifest at the correct ownership boundary.

9. Build a release checklist that catches this early

Privacy manifest work belongs before the release candidate, not after App Store Connect rejects the upload.

A practical checklist:

  1. Search app code for required reason API usage and wrappers.
  2. Review dependency changes since the last approved release.
  3. Check vendor release notes for privacy manifest updates.
  4. Confirm PrivacyInfo.xcprivacy target membership for app-owned targets.
  5. Archive and inspect the final .xcarchive for manifests.
  6. Validate app-owned manifests with plutil.
  7. Upload through App Store Connect or Transporter early enough to catch validation feedback.
  8. Update App Store privacy labels if collection or tracking changed.
  9. Record the result in the release notes or release checklist.

For teams with CI, add a lightweight archive inspection step for release branches. It does not need to solve privacy policy. It just needs to confirm expected manifests exist in the expected bundles.

Example output from a simple check might be enough:

Privacy manifests found:
- Products/Applications/MyApp.app/PrivacyInfo.xcprivacy
- Products/Applications/MyApp.app/Frameworks/AnalyticsSDK.framework/PrivacyInfo.xcprivacy
- Products/Applications/MyApp.app/Frameworks/PaymentsSDK.framework/PrivacyInfo.xcprivacy

If a required SDK disappears from that list after a dependency update, catch it before App Store Connect does. Machines are excellent at boring comparisons. Let them have this one.

10. The baseline I would ship

For a production iOS app, I would use this baseline:

  1. Keep one app-owned PrivacyInfo.xcprivacy in source control for the app target.
  2. Declare only the data collection, tracking, and required reason API usage the app actually performs.
  3. Maintain a dependency privacy inventory for SDKs and transitive packages.
  4. Pin SDK versions deliberately and review privacy-related release notes before updates.
  5. Inspect every release archive for privacy manifests in the app and required embedded frameworks.
  6. Treat App Store privacy labels as part of the same release surface.
  7. Run App Store validation early enough that feedback does not block the planned release window.

Privacy manifests are not a strategy. They are a compliance artifact that exposes whether the team understands what the app ships.

That is why the best process is not heroic. It is plain, repeatable, and slightly unforgiving. The manifest should tell the truth, the archive should contain the file, the SDKs should carry their own declarations, and the release should not depend on someone pasting reason codes into Xcode at 11 p.m. like a raccoon defusing a bomb.

Command menu

Navigate the site or run an action