跳到主要内容
教程目录

iOS 隐私清单怎么写?PrivacyInfo.xcprivacy 隐私清单示例

最后更新

iOS 隐私清单是放在 App 包根目录的 PrivacyInfo.xcprivacy 文件,用来声明 App 收集哪些数据、是否跟踪用户,以及为什么调用 UserDefaults、文件时间戳这类「必需原因 API」。只要代码里用了这类 API(几乎所有 App 都会用 UserDefaults),上架时就必须有它,否则上传会收到 ITMS-91053 并被拒收。做法是按源码里命中的 API 类别逐类写原因码,加入 App 的 target 后解压 ipa 核对。规则以 Apple 官方文档为准,截至 2026 年 10 月。

开始前准备好能构建 Release 包的工程,装好 KXBuild 和 VS Code 插件,还没装的按安装页操作。

什么时候必须要 iOS 隐私清单?

隐私清单在 WWDC23 推出,之后 Apple 分几步把它变成上架要求。

生效日期 要求 不满足时 官方出处
2024 年 5 月 1 日 用到必需原因 API 的 App 要在隐私清单里写明原因 App Store Connect 不接受上传,邮件提示 ITMS-91053 Describing use of required reason API
2024 年 11 月 12 日 提交审核的 App 里的隐私清单必须有效 邮件提示 ITMS-91056,列出无效文件路径 TN3181
2025 年 2 月 12 日 常用第三方 SDK 必须带有效的隐私清单 邮件提示 ITMS-91061,列出缺清单的 SDK Adding a privacy manifest

简单说,2026 年上架的 iOS App,只要用了 UserDefaults(包括 SwiftUI 的 @AppStorage),就要有隐私清单。自己的代码一个这类 API 都没用、也不收集数据的,可以不放,但用到的第三方 SDK 仍要各自带清单。

清单里两部分的适用范围不同。收集的数据类型在所有平台都要写;必需原因 API 在 iOS、iPadOS、tvOS、visionOS、watchOS 上要写。

PrivacyInfo.xcprivacy 文件结构是什么样的?

PrivacyInfo.xcprivacy 是一个 plist,文件名必须是这个,顶层字典里有四个键。

键 类型 作用
NSPrivacyTracking Boolean App 是否按 App Tracking Transparency 的定义把数据用于跟踪
NSPrivacyTrackingDomains Array(String) 用于跟踪的网络域名。只有 NSPrivacyTracking 为 true 时才写
NSPrivacyCollectedDataTypes Array(Dictionary) 收集的数据类型,每种一个字典
NSPrivacyAccessedAPITypes Array(Dictionary) 用到的必需原因 API 类别,每类一个字典

NSPrivacyTrackingDomains 里列出的域名,在用户没有授权跟踪时请求会失败。域名只写主域或子域,不带路径、查询参数和末尾斜杠。

NSPrivacyAccessedAPITypes 里每个字典有两个键。

  • NSPrivacyAccessedAPIType:String,API 类别名,例如 NSPrivacyAccessedAPICategoryUserDefaults。
  • NSPrivacyAccessedAPITypeReasons:Array,这个类别下的原因码,例如 CA92.1。

NSPrivacyCollectedDataTypes 里每个字典有四个键。

  • NSPrivacyCollectedDataType:String,数据类型,例如 NSPrivacyCollectedDataTypeEmailAddress。
  • NSPrivacyCollectedDataTypeLinked:Boolean,这项数据是否和用户身份关联。
  • NSPrivacyCollectedDataTypeTracking:Boolean,这项数据是否用于跟踪。
  • NSPrivacyCollectedDataTypePurposes:Array,收集目的,例如 NSPrivacyCollectedDataTypePurposeAppFunctionality。

必需原因 API 有哪五类?

Apple 把可能被用来做设备指纹的 API 分成五类,下表是每类包含的 API 和常用原因码,原因码的含义按官方 NSPrivacyAccessedAPIType 文档整理。

类别 包含的 API(节选) 常用原因码
NSPrivacyAccessedAPICategoryUserDefaults UserDefaults,包括 @AppStorage CA92.1、1C8F.1
NSPrivacyAccessedAPICategoryFileTimestamp creationDate、modificationDate、contentModificationDateKey、creationDateKey、stat、fstat、lstat、getattrlist 等 C617.1、DDA9.1、3B52.1
NSPrivacyAccessedAPICategorySystemBootTime systemUptime、mach_absolute_time() 35F9.1、8FFB.1
NSPrivacyAccessedAPICategoryDiskSpace volumeAvailableCapacityKey、volumeAvailableCapacityForImportantUsageKey、volumeTotalCapacityKey、systemFreeSize、statfs、statvfs 等 E174.1、85F4.1
NSPrivacyAccessedAPICategoryActiveKeyboards activeInputModes 54BD.1、3EC4.1

原因码怎么选

按你实际的用法选,可以写多个,但每个都要和 App 的功能一致。下面是官方原文的要点。

原因码 类别 适用场景 限制
CA92.1 UserDefaults 读写只有 App 自己能访问的数据 不能读其他 App 或系统写的数据
1C8F.1 UserDefaults 读写同一 App Group 内 App、扩展、App Clip 共享的数据 不能读写 App Group 以外的数据
C617.1 文件时间戳 读取 App 容器、App Group 容器或 App 的 CloudKit 容器内文件的时间戳、大小等元数据 只限这些容器内的文件
DDA9.1 文件时间戳 把文件时间戳显示给用户 数据不能发出设备
3B52.1 文件时间戳 访问用户明确授权的文件或目录(例如通过文档选择器)的元数据 只限用户授权的文件
35F9.1 系统启动时间 计算 App 内事件之间的间隔,或实现计时器 只有事件间隔可以发出设备
8FFB.1 系统启动时间 为 App 内事件(例如 UIKit、AVFAudio 事件)计算绝对时间戳 启动时间本身不能发出设备
E174.1 磁盘空间 检查空间是否够写文件,或空间不足时删除文件 App 的行为要随空间变化且用户能察觉
85F4.1 磁盘空间 把磁盘空间显示给用户 数据不能发出设备
54BD.1 活动键盘 根据当前键盘给用户展示正确的自定义界面 App 要有文本输入框,并随键盘变化表现出可察觉的差异
3EC4.1 活动键盘 App 本身是自定义键盘 App 提供系统级键盘必须是主要功能

C56D.1、0A2A.1 这类「SDK 包装函数」原因码只能由第三方 SDK 声明,App 自己的清单不要用。

多数 App 只需要两三项。记住登录状态、设置项用 CA92.1;清理缓存时读文件修改时间用 C617.1;下载前检查剩余空间用 E174.1;统计页面停留时长用 35F9.1。

找出自己用了哪些

在工程根目录搜一遍源码,看命中哪些 API。Windows 上在 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

Linux 或 WSL 上用 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\(' .

搜索范围只放你自己的源码目录,Pods、.build、node_modules 里的第三方代码由各 SDK 自己的清单负责。命中一类就在清单里加一个字典。上传后邮件提示的类别如果比你声明的多,多出来的通常来自某个第三方 SDK。

隐私清单完整示例怎么写?

下面是一个常见 App 的完整清单:不跟踪用户;收集邮箱用于登录,和用户身份关联;收集崩溃数据用于分析;代码里用到 UserDefaults、文件时间戳、系统启动时间和磁盘空间。

 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>

按自己的情况删改。

  • 只用了 UserDefaults 的,NSPrivacyAccessedAPITypes 里只留第一个字典。
  • 什么数据都不收集的,NSPrivacyCollectedDataTypes 写成空数组 <array/>。
  • 某个类别不再使用,就删掉整个字典,不要留下原因码为空的字典。NSPrivacyAccessedAPITypes 一项都不剩时,连这个键一起删掉。
  • 用了广告或归因 SDK 跟踪用户的,NSPrivacyTracking 改成 <true/>,并加上 NSPrivacyTrackingDomains 列出跟踪域名,数组不能为空。

常用的数据类型值还有 NSPrivacyCollectedDataTypeName(姓名)、NSPrivacyCollectedDataTypePhoneNumber(电话)、NSPrivacyCollectedDataTypeUserID(用户 ID)、NSPrivacyCollectedDataTypeDeviceID(设备 ID)、NSPrivacyCollectedDataTypePreciseLocation(精确位置)、NSPrivacyCollectedDataTypeProductInteraction(产品交互)。

收集目的还有 NSPrivacyCollectedDataTypePurposeThirdPartyAdvertising、NSPrivacyCollectedDataTypePurposeDeveloperAdvertising、NSPrivacyCollectedDataTypePurposeProductPersonalization、NSPrivacyCollectedDataTypePurposeOther。

完整列表见官方数据类型说明。

怎么把隐私清单加进工程?

隐私清单要作为 App target 的资源打进包,构建后位于 .app 根目录。

新建文件

  1. 在 VS Code 资源管理器里,到 App 的源码文件夹(和 Info.plist、Assets.xcassets 同级的那个)新建文件 PrivacyInfo.xcprivacy。
  2. 把上一节的 XML 粘进去,按自己的情况删改后保存。
  3. 以后再改,双击这个文件,用 plist 编辑器以表格方式编辑。字典和数组能展开折叠,右键「添加子项」给数组加原因码,类型在每行的类型列里选。保存格式保持 XML,方便代码评审时看差异。

加入 App 的 target

新文件要属于 App 的 target 才会编进包里。

  • 工程用的是 Xcode 16 起的文件夹同步(Xcode 里显示为蓝色文件夹)时,文件放进源码文件夹就已经加入,不用额外操作。
  • KXBuild 模板和较老的工程用的是分组,执行下面的命令登记一次。参数说明见 kxbuild 命令参考。
1kxbuild project add-resource -f MyApp/PrivacyInfo.xcprivacy -t MyApp -P .\MyApp.xcodeproj

工程里有多个 App target(例如正式版和测试版分开的),每个要上架的 target 都执行一次。小组件等扩展里用到必需原因 API 的,扩展的 bundle 也要有自己的清单,用 -t 指定扩展的 target 再登记一份。

Swift Package 里的清单

你自己维护的 Swift Package 要随包带清单时,把文件放在 Sources/<Target名>/ 下,并在 Package.swift 里显式声明为资源,否则不会被当作资源处理。

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

构建后核对

构建 App Store 包,在 VS Code 运行「KXBuild: 生成 App Store ipa」,或用命令行。

1kxbuild build .\MyApp.xcodeproj -c Release --export app-store

ipa 本质是 zip 包,应用在 Payload/<App>.app/ 下。Windows 10 及以上自带 tar,可以直接列出包内的隐私清单。

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

Linux 或 WSL 上用 unzip。

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

输出里要有 Payload/MyApp.app/PrivacyInfo.xcprivacy 这一行,这是 App 自己的清单。其他行通常是各框架和资源包自带的清单,例如 Payload/MyApp.app/Frameworks/Xxx.framework/PrivacyInfo.xcprivacy。

想确认打进包的内容就是你写的那份,解压出来查看。

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

没有出现 App 根目录那一行时,按顺序检查三处。

  1. 文件名是否正好是 PrivacyInfo.xcprivacy,大小写和扩展名都要一致。
  2. 文件是否已加入 App 的 target,分组工程重新执行一次 kxbuild project add-resource。
  3. 文件是否放在了构建排除的目录里,挪回 App 源码文件夹。

第三方 SDK 的隐私清单怎么检查?

哪些 SDK 必须带

Apple 维护一份常用第三方 SDK 名单,名单上的 SDK 不论哪个版本都要带隐私清单,以二进制形式引入的还要带签名。

名单里包括 Alamofire、AFNetworking、Firebase 系列、FBSDKCoreKit、GoogleSignIn、Kingfisher、SDWebImage、Lottie、SnapKit、RxSwift、Realm、OneSignal、Flutter、hermes、Capacitor、Cordova、UnityFramework,以及 shared_preferences_ios、path_provider、url_launcher、image_picker_ios 等一批 Flutter 插件。

新 App 包含名单上的 SDK,或更新版本新加入名单上的 SDK 时,缺清单会收到 ITMS-91061。

名单以外的 SDK,只要用了必需原因 API、收集用户数据或连接跟踪域名,也应该自带清单。

怎么查

  1. 用上一节的 tar -tf 命令列出包里所有的 PrivacyInfo.xcprivacy。
  2. 再列出包里的框架,对照名单看哪些框架下面没有清单。
1tar -tf .\build\MyApp.ipa | findstr /R "\.framework/Info\.plist$"
  1. 缺清单的,按依赖方式升级。CocoaPods 改 Podfile 里的版本后重新 pod install;Swift Package 改好版本要求后用 kxbuild project resolve --force 重新解析;Flutter 插件更新 pubspec.yaml 里的版本;React Native 和 Expo 升级对应的 npm 包后重新生成 iOS 工程。

以静态库(.a)形式提供的 SDK 无法携带资源,只能等提供方改成带清单的静态框架或 XCFramework。Apple 明确建议联系 SDK 提供方获取新版本,不要把 SDK 的隐私行为写进你自己的清单。

上传报 ITMS-91053、ITMS-91056 怎么办?

错误码 含义 处理
ITMS-91053 缺少必需原因 API 声明,邮件列出 API 类别 自己代码用的,在 App 清单里加对应类别和原因码;来自 SDK 的,升级 SDK
ITMS-91061 名单上的第三方 SDK 缺少隐私清单 按邮件给出的 SDK 名升级到带清单的版本
ITMS-91056 隐私清单无效,邮件给出文件路径 检查键名拼写、值类型、原因码是否属于对应类别
ITMS-90683 Info.plist 缺少权限用途说明 这是 Info.plist 的问题,不是隐私清单,补上对应的 NS…UsageDescription

ITMS-91056 最常见的写法错误来自 TN3181。

  • NSPrivacyTracking 不是 Boolean。
  • NSPrivacyTracking 为 true,但 NSPrivacyTrackingDomains 是空数组。
  • NSPrivacyTracking 为 false,却写了 NSPrivacyTrackingDomains。
  • NSPrivacyAccessedAPIType 的类别名拼错,或原因码不属于这个类别,例如把 C617.1 写进了 UserDefaults 类别。
  • NSPrivacyAccessedAPITypeReasons 是空数组,或不是数组。

其他上传错误码见 ITMS 错误码速查表。

隐私清单和隐私营养标签

App Store 页面上的「App 隐私」标签(常叫隐私营养标签)来自你在 App Store Connect「App 隐私」页填写的问卷,不会从隐私清单自动生成。两者要说的是同一件事,内容必须一致。

隐私清单 App 隐私问卷(营养标签)
在哪里 随 ipa 打进包里 App Store Connect 网页上填写
谁来写 App 和每个 SDK 各写一份 你为整个 App 填一份
内容 数据类型、跟踪、必需原因 API 数据类型、用途、是否关联身份、是否跟踪
谁会看到 Apple 的上传检查 App Store 用户

填问卷时,把 App 清单和每个 SDK 清单里的 NSPrivacyCollectedDataTypes 合并起来,逐项对应到问卷里的数据类型和用途。SDK 的数据也要算进 App 的标签里。必需原因 API 只写在清单里,问卷里没有对应项。

上架前检查清单

  • App 源码文件夹里有 PrivacyInfo.xcprivacy,文件名大小写正确。
  • 源码里命中的每类必需原因 API,清单里都有对应字典和原因码,没有空数组。
  • NSPrivacyTracking 和 NSPrivacyTrackingDomains 搭配正确。
  • 每个要上架的 App target 都登记了清单,用到这类 API 的扩展也有自己的清单。
  • tar -tf 能看到 Payload/<App>.app/PrivacyInfo.xcprivacy。
  • 名单上的第三方 SDK 都已升级到带清单的版本。
  • App Store Connect 的「App 隐私」问卷和所有清单里的数据收集一致。

这几项都勾完,再用 kxbuild build -c Release --export app-store 打包上传,iOS 隐私清单相关的 ITMS-91053、ITMS-91056、ITMS-91061 就不会再出现。

常见问题

不加 PrivacyInfo.xcprivacy 能上架 App Store 吗?

代码里用了 UserDefaults 等必需原因 API 的不行。自 2024 年 5 月 1 日起,App Store Connect 不接受没有声明这类 API 原因的 App。确实一个都没用、也不收集数据的 App 可以不放,但第三方 SDK 仍要自带清单。

只用了 @AppStorage,隐私清单要写什么?

@AppStorage 底层就是 UserDefaults。在 NSPrivacyAccessedAPITypes 里加一个字典,类别写 NSPrivacyAccessedAPICategoryUserDefaults,原因码写 CA92.1。和小组件通过 App Group 共享数据的,再加上 1C8F.1。

第三方 SDK 的 API 用法要写进我自己的隐私清单吗?

不要。Apple 规定 SDK 的用法由 SDK 自己的清单声明,App 的清单不能代替。收到指向 SDK 的 ITMS-91053 或 ITMS-91061,把这个 SDK 升级到带清单的版本。

在 Windows 上怎么确认隐私清单打进了 ipa?

在 PowerShell 执行 tar -tf .\build\MyApp.ipa | findstr PrivacyInfo.xcprivacy,输出里有 Payload/MyApp.app/PrivacyInfo.xcprivacy 就说明已打进 App 根目录。没有的话,检查文件名并用 kxbuild project add-resource 把它加入 App 的 target。

隐私清单和 App Store Connect 的 App 隐私问卷有什么区别?

隐私清单随包提交给 Apple 检查,App 隐私问卷决定商店页面上用户看到的隐私标签。问卷要手动填写,内容要和 App 及所有 SDK 清单里声明的数据收集保持一致。