隐私清单深度解析:什么算作数据收集
自2024年5月1日起,每个上传至App Store Connect的应用及第三方SDK都必须附带隐私清单,声明其对”必需理由API”的使用、追踪行为及所收集的数据类型1。该清单是一份属性列表(PrivacyInfo.xcprivacy),App Store在提交时会对其进行解析,依照已知的隐私敏感API范围进行验证,并将其呈现于面向用户的隐私营养标签中。凡是省略清单,或在未声明已批准理由的情况下使用隐私敏感API的应用,都会在提交时被拒,并返回ITMS-91053或相关错误码2。
多数团队将清单视为一次性的合规任务:写好、提交、再不过问。但清单远不止于此。它是应用与苹果之间的一份结构化契约,将应用对私人数据的处理方式编入规范,也是这份契约唯一以机器可读形式存在的载体。仔细研读清单可以揭示:苹果将哪些内容视为隐私敏感(其覆盖面比多数开发者预想的更广)、用户在营养标签中实际看到的内容(比多数团队意识到的更细粒度),以及第三方SDK必须披露的内容(这是一项实实在在的义务,而非礼节性要求)。
TL;DR
- 隐私清单包含四个顶层章节:
NSPrivacyTracking、NSPrivacyTrackingDomains、NSPrivacyCollectedDataTypes和NSPrivacyAccessedAPITypes3。 - 必需理由API列表初始涵盖五类:文件时间戳、系统启动时间、磁盘空间、活跃键盘和User Defaults。每一类均有封闭的已批准理由代码列表;应用必须从中选择一项,或重写代码以规避该API4。
- 追踪的定义十分狭义:将用户数据与其他公司数据相关联,用于广告投放或与数据经纪商共享。若
NSPrivacyTracking为true,则用于追踪的每个域名都必须出现在NSPrivacyTrackingDomains中3。 - 第三方SDK必须自带清单。应用清单会在提交时与所有链接SDK的清单合并;若苹果发布列表中的SDK缺失清单,则会阻断应用提交5。
- 最大的陷阱:访问
UserDefaults始终算作必需理由API调用。一个普通的UserDefaults.standard.set(...)也需要声明理由代码。
四个章节
一份完整的PrivacyInfo.xcprivacy文件包含四个顶层键3。其结构是属性列表,可以以XML原始格式编辑,或通过Xcode的隐私清单编辑器进行编辑。
NSPrivacyTracking(布尔值)
声明该应用或SDK是否在其他公司的应用与网站之间追踪用户。其语义定义与App Tracking Transparency一致:将用户数据与其他公司数据相关联,用于定向广告、投放定向广告或将用户数据与数据经纪商共享6。
若NSPrivacyTracking为true,则每个用于追踪的域名都必须出现在NSPrivacyTrackingDomains列表中。若为false,该列表可省略(或留空)。
这一狭义定义十分关键。许多展示广告的应用(通过苹果自家的SKAdNetwork或不进行追踪的广告SDK)可以将NSPrivacyTracking声明为false。追踪指的是与外部数据的关联,而非展示广告本身。
NSPrivacyTrackingDomains(字符串数组)
应用或SDK出于追踪目的所联系的域名列表。声明NSPrivacyTracking为true的应用必须在此处枚举每一个追踪端点。
该列表通过App Privacy Report在运行时强制执行:若应用联系了被标记为追踪但未声明的域名,App Privacy Report会向用户呈现这一差异。屡次违规将面临从App Store下架的风险。
NSPrivacyCollectedDataTypes(字典数组)
为App Store隐私营养标签提供数据来源的结构化数据收集声明3。每一条目描述应用所收集的一种数据类型,包含以下子键:
NSPrivacyCollectedDataType:来自苹果封闭列表的数据类型(例如NSPrivacyCollectedDataTypeName、NSPrivacyCollectedDataTypeEmailAddress、NSPrivacyCollectedDataTypeCrashData)。NSPrivacyCollectedDataTypeLinked:布尔值,标识该数据是否与用户身份关联。NSPrivacyCollectedDataTypeTracking:布尔值,标识该数据是否用于追踪。NSPrivacyCollectedDataTypePurposes:数组,列出收集该数据的用途(分析、应用功能、广告等)。
此章节是隐私营养标签的权威依据。营养标签中”用于追踪您的数据”和”与您关联的数据”两类内容均根据这些标志计算得出。声明不准确将导致营养标签失实,并面临App Store的执法风险。
NSPrivacyAccessedAPITypes(字典数组)
必需理由API声明。每一条目将一个必需理由API类别与一组已批准的理由代码配对4。
<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>
这些理由均为封闭列表。应用须从苹果发布的列表中按类别选择一项或多项;任意字符串都将在提交时被拒绝。
五类必需理由API
苹果于2024年发布的初始列表定义了五类需要声明理由的API4:
NSPrivacyAccessedAPICategoryFileTimestamp
任何返回文件创建、修改或访问时间的API都会触发此项。常见的入口点包括:URL.resourceValues(forKeys: [.creationDateKey, ...])、FileManager.attributesOfItem(atPath:)、stat()、fstat()、lstat()。四项已批准的理由4:
- DDA9.1. 向设备用户展示文件时间戳。出于此理由访问的信息不得发送至设备外。
- C617.1. 访问应用容器、应用组容器或CloudKit容器内文件的元数据(时间戳、大小等)。
- 3B52.1. 访问用户已明确授予访问权限的文件或目录的元数据(通过文档选择器等方式)。
- 0A2A.1. 仅在应用调用第三方SDK包装器时,访问文件时间戳API。仅限SDK使用。
多数应用使用C617.1(自有容器访问)进行内部文件管理。在用户界面中显示文件日期的应用使用DDA9.1。读取用户授予访问权限文件(文档选择器、共享扩展目标)的应用使用3B52.1。SDK作者只能使用0A2A.1。
NSPrivacyAccessedAPICategorySystemBootTime
由mach_absolute_time()、mach_continuous_time()及相关运行时长API触发。隐私顾虑在于:系统启动时间是一项可指纹化的信号,可在重装后用于识别设备。三项已批准的理由4:
- 35F9.1. 访问系统启动时间,用于测量应用事件之间的时间间隔或启用计时器。
- 8FFB.1. 访问系统启动时间,用于计算应用内事件的绝对时间戳。
- 3D61.1. 在用户自愿提交的可选错误报告中加入系统启动时间信息。
多数应用使用35F9.1进行性能测量。8FFB.1涵盖应用从启动时间推导事件时间戳的场景(相对于设备会话)。请注意,Date()和Date.now不会触发此类别;它们使用不同的时间源。该类别专门涵盖mach_*_time系列调用。
NSPrivacyAccessedAPICategoryDiskSpace
由返回磁盘容量或可用空间的API触发,包括URL.resourceValues(forKeys: [.volumeAvailableCapacityKey, ...])和NSFileManager的文件系统属性API。隐私顾虑在于:磁盘空间模式是一项可指纹化的信号。四项已批准的理由4:
- 854F.1. 以数据单位或媒体时长单位向设备用户展示磁盘空间信息。
- E174.1. 在写入文件时检查磁盘空间,或管理磁盘空间不足的情况(决定是否下载资源、清理缓存文件)。
- 7D9E.1. 在用户自愿提交的可选错误报告中加入磁盘空间信息。
- B728.1. 健康研究类应用检测并告知参与者磁盘空间不足影响研究数据收集。
NSPrivacyAccessedAPICategoryActiveKeyboards
由UITextInputMode.activeInputModes触发。读取该列表的应用(通常用于本地化行为或检测输入语言)必须声明理由。两项已批准的理由4:
- 3EC4.1. 自定义键盘应用确定设备上的活跃键盘。
- 54BD.1. 访问活跃键盘信息以相应地定制用户界面(通常用于感知输入语言的UX)。
多数应用无需该类别。如果您的代码通过第三方库间接调用了activeInputModes,则由该库的清单进行声明。
NSPrivacyAccessedAPICategoryUserDefaults
这是几乎所有应用都会触及的类别。任何对UserDefaults的访问(UserDefaults.standard、通过init(suiteName:)访问的应用组defaults,以及单次的set/get调用)都会触发此要求7。四项已批准的理由4:
- CA92.1. 访问user defaults以读写应用本身(或仅与该应用配对的应用扩展)专有的数据。
- 1C8F.1. 访问user defaults以读写仅在同一App Group成员的应用、应用扩展和App Clips之间共享的数据。
- C56D.1. 仅在应用调用第三方SDK包装器时,访问user defaultsAPI。仅限SDK使用。
- AC6B.1. 访问user defaults以获取由MDM/IT管理员设置的托管应用配置,或为管理员存储反馈信息。
几乎每个iOS应用都会用UserDefaults.standard存储至少一项状态(用户最近选择的标签页、偏好的排序方式、功能开关)。CA92.1这一理由即可覆盖。通过应用组在应用扩展之间共享状态的应用还需添加1C8F.1。SDK作者使用C56D.1。通过MDM部署、读取管理员设置键(例如com.apple.configuration.managed)的应用使用AC6B.1。
陷阱在于:触及UserDefaults的第三方SDK会代表应用触发该要求。SDK的清单必须声明理由。如果该SDK出现在苹果的必须附带隐私清单的SDK列表中,缺失SDK清单将阻断应用提交。
追踪域名:运行时验证
NSPrivacyTrackingDomains通过运行时的App Privacy Report强制执行6。当应用声明NSPrivacyTracking = true时,系统会追踪每一次网络请求并将目标地址与已声明列表进行匹配。被联系但未声明的追踪域名会出现在App Privacy Report的”频繁位置”和”其他已联系域名”等区域。
运行时验证形成了一种有趣的激励结构。声明NSPrivacyTracking = false(不追踪)但被观察到联系已知追踪域名的应用,会在App Privacy Report中被标记,并面临用户投诉的风险。正确的做法是诚实声明。
对于SDK,由SDK的隐私清单声明其追踪行为。应用清单无需枚举SDK的追踪域名;SDK清单已经做了这件事。提交时,苹果会合并清单并检查一致性。
SDK清单:一项实实在在的义务
以下两类第三方SDK必须附带隐私清单5:
- 出现在苹果要求隐私清单的SDK发布列表中的SDK。截至iOS 26,该列表包括Firebase Analytics、Google Mobile Ads、Adjust、Segment、AppsFlyer等约30个SDK。
- 以预编译二进制(XCFrameworks、静态库)形式分发、被应用链接的SDK。
提交一个链接了必需SDK但缺失该SDK清单的应用,会失败并返回ITMS-91xxx系列错误,涵盖隐私清单缺失、清单签名缺失或SDK命名空间内API声明缺失等问题2。修复方式是更新到附带清单的SDK版本,或移除该SDK。
对于第一方SDK(您的团队为自家应用所发布的内部框架),如果该框架不在苹果的列表中,您可以选择跳过清单。务实的做法仍是附带一份:未来苹果列表扩展、未来审计工作、未来第三方复用,都会因清单已就位而受益。
常见陷阱
来自App Store拒绝日志的五种常见失败模式:
1. 遗忘UserDefaults声明。 最常见的拒绝原因。应用在某个不起眼的地方使用了@AppStorage(其底层封装的是UserDefaults),但清单未予声明。修复:每个使用@AppStorage、UserDefaults或其任何包装器的应用都需要带有CA92.1理由的NSPrivacyAccessedAPICategoryUserDefaults声明。
2. 通过URLResourceValues隐式访问文件时间戳。 通过URL.resourceValues(forKeys: [.contentModificationDateKey])读取文件修改日期的代码会触发该类别,尽管调用现场看起来并不像是stat调用。
3. 对SDK清单的预期失误。 应用团队会假定SDK清单是SDK作者的事。确实如此,但在SDK发布清单之前,应用团队的提交会一直失败。
4. 链接了追踪SDK却声明NSPrivacyTracking = false。 以默认设置链接Firebase Analytics或Google Mobile Ads通常会触发追踪行为。应用清单声明NSPrivacyTracking = false,而SDK清单声明为true,会产生合并器标记的矛盾。
5. 过时的理由代码。 苹果会不定期更新已批准的理由代码。2024年有效的代码可能已被替换或扩展。修复方式是在每个大版本发布时重新核对当前发布列表,而非沿用旧清单。
验证工具
提交前值得运行的三项检查:
Xcode的”生成隐私报告”归档工作流。 在Product > Archive后,Organizer会提供”Generate Privacy Report”操作,从应用及每个链接SDK的清单生成汇总报告。该报告即App Store Connect所摄取的内容;本地运行它是最接近提交前预演的方式。
对必需理由API进行静态分析。 开源工具可扫描项目中触发五大类别的API调用现场。社区维护的stelabouras/privacy-manifest CLI可解析Swift源代码和xcframework二进制文件,呈现已声明和未声明的理由代码;crasowas/app_store_required_privacy_manifest_analyser提供类似的扫描功能。两者都能在提交前有效捕获遗漏的声明。
App Privacy Report交叉验证。 在iOS的App Privacy Report模式下运行应用(设置 > 隐私与安全 > App隐私报告)。应用所联系的域名会出现在报告中;将其与清单的追踪声明进行交叉核对。
这一模式对iOS 26+应用意味着什么
三点要义。
-
将隐私清单视为结构化契约,而非勾选项。 清单是应用对私人数据处理方式的唯一机器可读记录。一次构建、永远忽略,等于在六个月后某个SDK更新悄然扩大清单所需范围时被拒。
-
每次发布时都审计您的
UserDefaults和文件时间戳访问。 这是两个会随代码库静默扩张的类别。重构中新增的一个使用@AppStorage的SwiftUI视图,会把NSPrivacyAccessedAPICategoryUserDefaults拉入作用域;后续添加的文件列表功能则会带入NSPrivacyAccessedAPICategoryFileTimestamp。每次都要重新验证。 -
优先选择附带清单的SDK。 评估第三方SDK时,其隐私清单的存在与质量如今已成为质量信号。没有清单的SDK会迫使应用团队向上游施压;带清单的SDK则能干净地融入合并流程。
完整的Apple Ecosystem系列:类型化的App Intents;MCP服务器;路由问题;Foundation Models;运行时与工具LLM之分;三大表面;单一事实来源模式;两个MCP服务器;Apple开发的hooks;Live Activities;watchOS运行时;SwiftUI内部机制;RealityKit的空间心智模型;SwiftData模式纪律;Liquid Glass模式;多平台发行;平台矩阵;Vision框架;符号特效;Core ML推理;Writing Tools API;Swift Testing;我拒绝撰写的话题。系列入口位于Apple Ecosystem系列。如需更广义的iOS与AI智能体语境,请参阅iOS Agent Development指南。
FAQ
NSPrivacyTracking与NSPrivacyCollectedDataTypeTracking有何区别?
NSPrivacyTracking是应用级布尔值:该应用是否在追踪用户(按苹果的狭义定义)?NSPrivacyCollectedDataTypeTracking则是按数据类型而定:对于应用所收集的某一数据类型,该项数据是否用于追踪?应用可以在不追踪的情况下收集数据(不跨公司关联的分析);按类型的标志用于刻画每一具体数据类型是否参与追踪行为。
仅供内部测试的TestFlight应用是否也需要隐私清单?
需要。隐私清单的强制执行发生在向App Store Connect提交时,包括TestFlight构建。无清单的内部测试版与公开发布一样,会因ITMS-91053检查而失败。
如果我声明了一个并未使用的必需理由API,会发生什么?
不会有任何功能性影响。清单声明的是API范围的上限;声明一个未使用的类别本身无害。代价在于文档漂移:未来的维护者可能会假定声明反映了当前代码,而事实上相关调用早已移除。正确的做法是声明应用实际使用的内容,并在每个大版本发布时进行审计。
除了最初的五类之外,还有其他必需理由API类别吗?
截至iOS 26,最初的五个类别(文件时间戳、系统启动时间、磁盘空间、活跃键盘、User Defaults)仍是规范列表4。苹果会不时在类别内追加已批准的理由代码,但尚未扩展类别本身。请关注苹果的必需理由API文档页以获悉新增内容。
隐私清单与App Tracking Transparency如何相互作用?
App Tracking Transparency(ATT)管理跨应用追踪的运行时权限提示。隐私清单则静态地声明应用的意图。两者互为补充:进行跨应用追踪的应用会在清单中声明NSPrivacyTracking = true,同时在运行时请求ATT权限。声明NSPrivacyTracking = false的应用不应请求ATT(既然没有跨应用追踪,提示也就没有必要)。
参考资料
-
Apple Developer文档:Privacy manifest files。格式参考与2024年5月1日的强制执行时间线。 ↩
-
Apple Developer:App Store Connect submission errors,涵盖ITMS-91053(缺失API声明)及相关代码。 ↩↩
-
Apple Developer文档:App privacy configuration。四个顶层键(NSPrivacyTracking、NSPrivacyTrackingDomains、NSPrivacyCollectedDataTypes、NSPrivacyAccessedAPITypes)及其schema。 ↩↩↩↩
-
Apple Developer文档:Describing use of required reason API。五类必需理由API及其已批准理由代码。 ↩↩↩↩↩↩↩↩↩
-
Apple Developer:Third-party SDK privacy manifest requirements。必须附带隐私清单的SDK列表。 ↩↩
-
Apple Developer文档:User privacy and data use。追踪的狭义定义与App Tracking Transparency框架。 ↩↩
-
Apple Developer文档:
NSPrivacyAccessedAPICategoryUserDefaults。涵盖所有UserDefaults访问的类别,包括基于@AppStorage的包装器。 ↩