Skip to main content
Tutorials

iOS Privacy Manifest: How to Write PrivacyInfo.xcprivacy

Last updated

An iOS privacy manifest is a file named PrivacyInfo.xcprivacy at the root of your app bundle. It declares what data the app collects, whether it tracks users, and why it calls “required reason APIs” such as UserDefaults and file timestamps. If your code uses any of these APIs, and nearly every app uses UserDefaults, the app needs a manifest or the upload is rejected with ITMS-91053. Write one reason code per API category your source uses, add the file to the app target, then check it inside the .ipa. Rules follow Apple’s documentation as of October 2026.

Before you start, have a project that builds a Release package, with KXBuild and the VS Code extension installed. If you haven’t installed them, follow the install page.

When do you need an iOS privacy manifest?

Apple introduced privacy manifests at WWDC23 and turned them into an App Store requirement in stages.

Effective date Requirement What happens otherwise Source
May 1, 2024 Apps that use required reason APIs must state the reasons in a privacy manifest App Store Connect rejects the upload with an ITMS-91053 email Describing use of required reason API
November 12, 2024 Privacy manifests in apps submitted for review must be valid ITMS-91056 email listing the invalid file paths TN3181
February 12, 2025 Commonly used third-party SDKs must include a valid privacy manifest ITMS-91061 email listing the SDKs that lack one Adding a privacy manifest

In short, any iOS app submitted in 2026 that uses UserDefaults, including SwiftUI’s @AppStorage, needs a privacy manifest. If your own code uses none of these APIs and collects no data, you can leave it out, but the third-party SDKs you use still need their own manifests.

The two halves of the manifest apply to different platforms. Collected data types are required on all platforms. Required reason APIs are required on iOS, iPadOS, tvOS, visionOS and watchOS.

What does PrivacyInfo.xcprivacy contain?

PrivacyInfo.xcprivacy is a plist and must have exactly that name. The top-level dictionary has four keys.

Key Type Purpose
NSPrivacyTracking Boolean Whether the app uses data for tracking as defined by App Tracking Transparency
NSPrivacyTrackingDomains Array (String) Domains used for tracking. Only include it when NSPrivacyTracking is true
NSPrivacyCollectedDataTypes Array (Dictionary) Collected data types, one dictionary per type
NSPrivacyAccessedAPITypes Array (Dictionary) Required reason API categories used, one dictionary per category

Requests to domains listed in NSPrivacyTrackingDomains fail when the user hasn’t granted tracking permission. List only domains or subdomains, with no path, query string or trailing slash.

Each dictionary in NSPrivacyAccessedAPITypes has two keys.

  • NSPrivacyAccessedAPIType, a String with the category name, for example NSPrivacyAccessedAPICategoryUserDefaults.
  • NSPrivacyAccessedAPITypeReasons, an Array of reason codes for that category, for example CA92.1.

Each dictionary in NSPrivacyCollectedDataTypes has four keys.

  • NSPrivacyCollectedDataType, a String with the data type, for example NSPrivacyCollectedDataTypeEmailAddress.
  • NSPrivacyCollectedDataTypeLinked, a Boolean for whether the data is linked to the user’s identity.
  • NSPrivacyCollectedDataTypeTracking, a Boolean for whether the data is used for tracking.
  • NSPrivacyCollectedDataTypePurposes, an Array of purposes, for example NSPrivacyCollectedDataTypePurposeAppFunctionality.

What are the five required reason API categories?

Apple groups APIs that could be used for device fingerprinting into five categories. The table lists the APIs in each category and the common reason codes, based on the NSPrivacyAccessedAPIType reference.

Category APIs (selection) Common reason codes
NSPrivacyAccessedAPICategoryUserDefaults UserDefaults, including @AppStorage CA92.1, 1C8F.1
NSPrivacyAccessedAPICategoryFileTimestamp creationDate, modificationDate, contentModificationDateKey, creationDateKey, stat, fstat, lstat, getattrlist and others C617.1, DDA9.1, 3B52.1
NSPrivacyAccessedAPICategorySystemBootTime systemUptime, mach_absolute_time() 35F9.1, 8FFB.1
NSPrivacyAccessedAPICategoryDiskSpace volumeAvailableCapacityKey, volumeAvailableCapacityForImportantUsageKey, volumeTotalCapacityKey, systemFreeSize, statfs, statvfs and others E174.1, 85F4.1
NSPrivacyAccessedAPICategoryActiveKeyboards activeInputModes 54BD.1, 3EC4.1

Choose reason codes

Pick codes that match how you actually use the API. You can list more than one, but each must match what the app does. These are the key points from Apple’s definitions.

Reason code Category Use case Restriction
CA92.1 UserDefaults Read and write data that only the app itself can access Can’t read data written by other apps or the system
1C8F.1 UserDefaults Read and write data shared by apps, extensions and App Clips in the same App Group Can’t read or write data outside the App Group
C617.1 File timestamp Read timestamps, size or other metadata of files in the app container, App Group container or the app’s CloudKit container Only files in those containers
DDA9.1 File timestamp Show file timestamps to the user The data can’t leave the device
3B52.1 File timestamp Access metadata of files or folders the user explicitly granted access to, for example through a document picker Only files the user granted
35F9.1 System boot time Measure time between events in the app, or implement timers Only the elapsed time can leave the device
8FFB.1 System boot time Calculate absolute timestamps for events in the app, such as UIKit or AVFAudio events The boot time itself can’t leave the device
E174.1 Disk space Check whether there’s enough space to write files, or delete files when space is low The app must behave differently based on the space in a way the user can notice
85F4.1 Disk space Show disk space to the user The data can’t leave the device
54BD.1 Active keyboards Show the correct custom UI for the current keyboard The app must have text input and change visibly based on the keyboard
3EC4.1 Active keyboards The app is a custom keyboard app Providing a system-wide keyboard must be its primary function

SDK wrapper reason codes such as C56D.1 and 0A2A.1 are only for third-party SDKs. Don’t use them in your app’s own manifest.

Most apps only need two or three entries. Use CA92.1 for remembering sign-in state and settings, C617.1 for reading file modification dates when clearing a cache, E174.1 for checking free space before a download, and 35F9.1 for measuring how long a screen stays open.

Find what your code uses

Search the source in the project root to see which APIs show up. On Windows, run this in PowerShell.

1Get-ChildItem -Recurse -Include *.swift,*.m,*.mm,*.c,*.cpp |
2  Select-String -Pattern 'UserDefaults|@AppStorage|creationDate|modificationDate|contentModificationDateKey|systemUptime|mach_absolute_time|volumeAvailableCapacity|volumeTotalCapacity|systemFreeSize|statfs|statvfs|activeInputModes|\bstat\(|\blstat\(|\bfstat\(' |
3  Select-Object Path, LineNumber, Line

On Linux or WSL, use grep.

1grep -rnE --include='*.swift' --include='*.m' --include='*.mm' --include='*.c' --include='*.cpp' \
2  'UserDefaults|@AppStorage|creationDate|modificationDate|contentModificationDateKey|systemUptime|mach_absolute_time|volumeAvailableCapacity|volumeTotalCapacity|systemFreeSize|statfs|statvfs|activeInputModes|\bstat\(|\blstat\(|\bfstat\(' .

Search only your own source folders. Third-party code in Pods, .build and node_modules is covered by each SDK’s own manifest. Add one dictionary to your manifest for each category that matches. If the email after upload lists more categories than you declared, the extra ones usually come from a third-party SDK.

What does a complete privacy manifest look like?

Here is a complete manifest for a typical app. It doesn’t track users, collects an email address for sign-in that is linked to the user, collects crash data for analytics, and uses UserDefaults, file timestamps, system boot time and disk space.

 1<?xml version="1.0" encoding="UTF-8"?>
 2<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 3<plist version="1.0">
 4<dict>
 5	<key>NSPrivacyTracking</key>
 6	<false/>
 7	<key>NSPrivacyCollectedDataTypes</key>
 8	<array>
 9		<dict>
10			<key>NSPrivacyCollectedDataType</key>
11			<string>NSPrivacyCollectedDataTypeEmailAddress</string>
12			<key>NSPrivacyCollectedDataTypeLinked</key>
13			<true/>
14			<key>NSPrivacyCollectedDataTypeTracking</key>
15			<false/>
16			<key>NSPrivacyCollectedDataTypePurposes</key>
17			<array>
18				<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
19			</array>
20		</dict>
21		<dict>
22			<key>NSPrivacyCollectedDataType</key>
23			<string>NSPrivacyCollectedDataTypeCrashData</string>
24			<key>NSPrivacyCollectedDataTypeLinked</key>
25			<false/>
26			<key>NSPrivacyCollectedDataTypeTracking</key>
27			<false/>
28			<key>NSPrivacyCollectedDataTypePurposes</key>
29			<array>
30				<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
31			</array>
32		</dict>
33	</array>
34	<key>NSPrivacyAccessedAPITypes</key>
35	<array>
36		<dict>
37			<key>NSPrivacyAccessedAPIType</key>
38			<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
39			<key>NSPrivacyAccessedAPITypeReasons</key>
40			<array>
41				<string>CA92.1</string>
42			</array>
43		</dict>
44		<dict>
45			<key>NSPrivacyAccessedAPIType</key>
46			<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
47			<key>NSPrivacyAccessedAPITypeReasons</key>
48			<array>
49				<string>C617.1</string>
50			</array>
51		</dict>
52		<dict>
53			<key>NSPrivacyAccessedAPIType</key>
54			<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
55			<key>NSPrivacyAccessedAPITypeReasons</key>
56			<array>
57				<string>35F9.1</string>
58			</array>
59		</dict>
60		<dict>
61			<key>NSPrivacyAccessedAPIType</key>
62			<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
63			<key>NSPrivacyAccessedAPITypeReasons</key>
64			<array>
65				<string>E174.1</string>
66			</array>
67		</dict>
68	</array>
69</dict>
70</plist>

Adjust it to your app.

  • If you only use UserDefaults, keep only the first dictionary in NSPrivacyAccessedAPITypes.
  • If you collect no data, set NSPrivacyCollectedDataTypes to an empty array, <array/>.
  • When you stop using a category, delete its whole dictionary. Don’t leave a dictionary with no reason codes. If NSPrivacyAccessedAPITypes ends up with no entries, remove the key too.
  • If an advertising or attribution SDK tracks users, change NSPrivacyTracking to <true/> and add NSPrivacyTrackingDomains with the tracking domains. The array can’t be empty.

Other common data type values are NSPrivacyCollectedDataTypeName, NSPrivacyCollectedDataTypePhoneNumber, NSPrivacyCollectedDataTypeUserID, NSPrivacyCollectedDataTypeDeviceID, NSPrivacyCollectedDataTypePreciseLocation and NSPrivacyCollectedDataTypeProductInteraction.

Other purposes are NSPrivacyCollectedDataTypePurposeThirdPartyAdvertising, NSPrivacyCollectedDataTypePurposeDeveloperAdvertising, NSPrivacyCollectedDataTypePurposeProductPersonalization and NSPrivacyCollectedDataTypePurposeOther. See Apple’s describing data use in privacy manifests for the full list.

How do you add the privacy manifest to the project?

The privacy manifest must be packaged as a resource of the app target, so that after the build it sits at the root of the .app.

Create the file

  1. In the VS Code Explorer, go to the app’s source folder (the one next to Info.plist and Assets.xcassets) and create a file named PrivacyInfo.xcprivacy.
  2. Paste the XML from the previous section, adjust it to your app and save.
  3. For later edits, double-click the file to open it in the Property List Editor as a table. Dictionaries and arrays expand and collapse, right-click an array and choose Add Child to add a reason code, and pick the type in each row’s type column. Keep the save format as XML so diffs are easy to read in code review.

Add it to the app target

The new file only goes into the package if it belongs to the app target.

  • If the project uses folder synchronization from Xcode 16 onward (shown as blue folders in Xcode), placing the file in the source folder already adds it. Nothing else to do.
  • KXBuild templates and older projects use groups. Register the file once with the command below. See the kxbuild command reference for the options.
1kxbuild project add-resource -f MyApp/PrivacyInfo.xcprivacy -t MyApp -P .\MyApp.xcodeproj

If the project has several app targets, for example separate production and beta apps, run it for every target you submit. If an extension such as a widget uses required reason APIs, the extension bundle needs its own manifest too. Register another copy with -t set to the extension target.

Manifests in Swift packages

If you maintain a Swift package that should ship with a manifest, put the file under Sources/<TargetName>/ and declare it as a resource in Package.swift. Otherwise it isn’t treated as a resource.

1targets: [
2    .target(
3        name: "SomeLibrary",
4        resources: [
5            .process("PrivacyInfo.xcprivacy")
6        ]
7    ),
8]

Check the build

Build a Release package with KXBuild: Build in VS Code, or on the command line.

1kxbuild build .\MyApp.xcodeproj -c Release

An .ipa is a zip archive, with the app under Payload/<App>.app/. Windows 10 and later include tar, so you can list the privacy manifests in the package directly.

1tar -tf .\build\MyApp.ipa | findstr PrivacyInfo.xcprivacy

On Linux or WSL, use unzip.

1unzip -l build/MyApp.ipa | grep PrivacyInfo.xcprivacy

The output must include Payload/MyApp.app/PrivacyInfo.xcprivacy, which is the app’s own manifest. Other lines are usually manifests that ship with frameworks and resource bundles, for example Payload/MyApp.app/Frameworks/Xxx.framework/PrivacyInfo.xcprivacy.

To confirm the packaged file is the one you wrote, extract it and look.

1mkdir .\ipa-check
2tar -xf .\build\MyApp.ipa -C .\ipa-check
3code .\ipa-check\Payload\MyApp.app\PrivacyInfo.xcprivacy

If the app root line is missing, check these three things in order.

  1. The file name is exactly PrivacyInfo.xcprivacy, with matching case and extension.
  2. The file belongs to the app target. In a group-based project, run kxbuild project add-resource again.
  3. The file isn’t in a folder excluded from the build. Move it back to the app’s source folder.

How do you check third-party SDK manifests?

Which SDKs must include one

Apple maintains a list of commonly used third-party SDKs. Every version of a listed SDK must include a privacy manifest, and SDKs added as binaries must also be signed.

The list includes Alamofire, AFNetworking, the Firebase family, FBSDKCoreKit, GoogleSignIn, Kingfisher, SDWebImage, Lottie, SnapKit, RxSwift, Realm, OneSignal, Flutter, hermes, Capacitor, Cordova, UnityFramework, and Flutter plugins such as shared_preferences_ios, path_provider, url_launcher and image_picker_ios.

When a new app includes a listed SDK, or an update adds one, a missing manifest triggers ITMS-91061.

SDKs that aren’t on the list should still ship their own manifest if they use required reason APIs, collect user data or connect to tracking domains.

How to check

  1. List every PrivacyInfo.xcprivacy in the package with the tar -tf command from the previous section.
  2. List the frameworks in the package and compare them with the SDK list to see which have no manifest.
1tar -tf .\build\MyApp.ipa | findstr /R "\.framework/Info\.plist$"
  1. Upgrade the ones without a manifest, depending on how you add them. For CocoaPods, change the version in the Podfile and run pod install again. For Swift packages, update the version requirement and run kxbuild project resolve --force to resolve again. For Flutter plugins, update the version in pubspec.yaml. For React Native and Expo, upgrade the npm package and regenerate the iOS project.

SDKs shipped as static libraries (.a) can’t carry resources. You have to wait for the vendor to switch to a static framework or XCFramework that includes a manifest. Apple recommends contacting the SDK vendor for an updated version instead of declaring the SDK’s privacy behavior in your own manifest.

How do you fix ITMS-91053 and ITMS-91056?

Code Meaning Fix
ITMS-91053 Missing required reason API declaration, with the API categories listed in the email If your code uses it, add the category and reason code to the app manifest. If it comes from an SDK, upgrade the SDK
ITMS-91061 A listed third-party SDK has no privacy manifest Upgrade the SDK named in the email to a version with a manifest
ITMS-91056 Invalid privacy manifest, with the file path in the email Check key spelling, value types and that each reason code belongs to its category
ITMS-90683 Info.plist is missing a usage description This is an Info.plist problem, not the manifest. Add the matching NS…UsageDescription

The most common ITMS-91056 mistakes, from TN3181, are these.

  • NSPrivacyTracking isn’t a Boolean.
  • NSPrivacyTracking is true but NSPrivacyTrackingDomains is an empty array.
  • NSPrivacyTracking is false but NSPrivacyTrackingDomains is present.
  • The category name in NSPrivacyAccessedAPIType is misspelled, or a reason code doesn’t belong to the category, for example C617.1 under UserDefaults.
  • NSPrivacyAccessedAPITypeReasons is an empty array or isn’t an array.

For other upload error codes, see the ITMS error code reference.

Privacy manifests and privacy nutrition labels

The App Privacy label on the App Store page, often called the privacy nutrition label, comes from the questionnaire you fill in on the App Privacy page in App Store Connect. It isn’t generated from the privacy manifest. Both describe the same thing, so their content must match.

Privacy manifest App Privacy questionnaire (nutrition label)
Where Packaged in the .ipa Filled in on the App Store Connect website
Who writes it The app and each SDK write their own You fill in one for the whole app
Content Data types, tracking, required reason APIs Data types, purposes, linked to identity, tracking
Who sees it Apple’s upload checks App Store users

When you fill in the questionnaire, combine NSPrivacyCollectedDataTypes from the app manifest and every SDK manifest, and map each entry to a data type and purpose in the questionnaire. Data collected by SDKs counts toward your app’s label. Required reason APIs only go in the manifest and have no counterpart in the questionnaire.

Pre-submission checklist

  • The app’s source folder contains PrivacyInfo.xcprivacy with the correct case.
  • Every required reason API category found in your source has a dictionary and reason code in the manifest, with no empty arrays.
  • NSPrivacyTracking and NSPrivacyTrackingDomains are consistent.
  • Every app target you submit has the manifest registered, and extensions that use these APIs have their own.
  • tar -tf shows Payload/<App>.app/PrivacyInfo.xcprivacy.
  • Every listed third-party SDK is on a version that includes a manifest.
  • The App Privacy questionnaire in App Store Connect matches the data collection in all manifests.

Once every item is checked, build with kxbuild build -c Release --export app-store and upload. With a complete iOS privacy manifest, ITMS-91053, ITMS-91056 and ITMS-91061 stop appearing.

FAQ

Can I submit to the App Store without PrivacyInfo.xcprivacy?

Not if your code uses required reason APIs such as UserDefaults. Since May 1, 2024, App Store Connect rejects apps that don’t declare reasons for these APIs. An app that truly uses none of them and collects no data can leave it out, but third-party SDKs still need their own manifests.

What does the privacy manifest need if I only use @AppStorage?

@AppStorage is built on UserDefaults. Add a dictionary to NSPrivacyAccessedAPITypes with the category NSPrivacyAccessedAPICategoryUserDefaults and the reason code CA92.1. If you share data with a widget through an App Group, add 1C8F.1 as well.

Do I declare a third-party SDK’s API usage in my own manifest?

No. Apple requires each SDK to declare its usage in its own manifest, and your app’s manifest can’t stand in for it. If you get ITMS-91053 or ITMS-91061 pointing at an SDK, upgrade that SDK to a version with a manifest.

How do I check on Windows that the manifest is in the .ipa?

Run tar -tf .\build\MyApp.ipa | findstr PrivacyInfo.xcprivacy in PowerShell. If the output includes Payload/MyApp.app/PrivacyInfo.xcprivacy, the manifest is at the app root. If not, check the file name and add it to the app target with kxbuild project add-resource.

What’s the difference between the privacy manifest and the App Privacy questionnaire?

The privacy manifest ships in the package for Apple’s checks, while the App Privacy questionnaire determines the privacy label users see on the App Store page. You fill in the questionnaire by hand, and it must match the data collection declared in the manifests of your app and all its SDKs.