← 所有文章

隐私清单深度解析:什么算作数据收集

自2024年5月1日起,每个上传至App Store Connect的应用及第三方SDK都必须附带隐私清单,声明其对”必需理由API”的使用、追踪行为及所收集的数据类型1。该清单是一份属性列表(PrivacyInfo.xcprivacy),App Store在提交时会对其进行解析,依照已知的隐私敏感API范围进行验证,并将其呈现于面向用户的隐私营养标签中。凡是省略清单,或在未声明已批准理由的情况下使用隐私敏感API的应用,都会在提交时被拒,并返回ITMS-91053或相关错误码2

多数团队将清单视为一次性的合规任务:写好、提交、再不过问。但清单远不止于此。它是应用与苹果之间的一份结构化契约,将应用对私人数据的处理方式编入规范,也是这份契约唯一以机器可读形式存在的载体。仔细研读清单可以揭示:苹果将哪些内容视为隐私敏感(其覆盖面比多数开发者预想的更广)、用户在营养标签中实际看到的内容(比多数团队意识到的更细粒度),以及第三方SDK必须披露的内容(这是一项实实在在的义务,而非礼节性要求)。

TL;DR

  • 隐私清单包含四个顶层章节:NSPrivacyTrackingNSPrivacyTrackingDomainsNSPrivacyCollectedDataTypesNSPrivacyAccessedAPITypes3
  • 必需理由API列表初始涵盖五类:文件时间戳、系统启动时间、磁盘空间、活跃键盘和User Defaults。每一类均有封闭的已批准理由代码列表;应用必须从中选择一项,或重写代码以规避该API4
  • 追踪的定义十分狭义:将用户数据与其他公司数据相关联,用于广告投放或与数据经纪商共享。若NSPrivacyTrackingtrue,则用于追踪的每个域名都必须出现在NSPrivacyTrackingDomains3
  • 第三方SDK必须自带清单。应用清单会在提交时与所有链接SDK的清单合并;若苹果发布列表中的SDK缺失清单,则会阻断应用提交5
  • 最大的陷阱:访问UserDefaults始终算作必需理由API调用。一个普通的UserDefaults.standard.set(...)也需要声明理由代码。

四个章节

一份完整的PrivacyInfo.xcprivacy文件包含四个顶层键3。其结构是属性列表,可以以XML原始格式编辑,或通过Xcode的隐私清单编辑器进行编辑。

NSPrivacyTracking(布尔值)

声明该应用或SDK是否在其他公司的应用与网站之间追踪用户。其语义定义与App Tracking Transparency一致:将用户数据与其他公司数据相关联,用于定向广告、投放定向广告或将用户数据与数据经纪商共享6

NSPrivacyTrackingtrue,则每个用于追踪的域名都必须出现在NSPrivacyTrackingDomains列表中。若为false,该列表可省略(或留空)。

这一狭义定义十分关键。许多展示广告的应用(通过苹果自家的SKAdNetwork或不进行追踪的广告SDK)可以将NSPrivacyTracking声明为false。追踪指的是与外部数据的关联,而非展示广告本身。

NSPrivacyTrackingDomains(字符串数组)

应用或SDK出于追踪目的所联系的域名列表。声明NSPrivacyTrackingtrue的应用必须在此处枚举每一个追踪端点。

该列表通过App Privacy Report在运行时强制执行:若应用联系了被标记为追踪但未声明的域名,App Privacy Report会向用户呈现这一差异。屡次违规将面临从App Store下架的风险。

NSPrivacyCollectedDataTypes(字典数组)

为App Store隐私营养标签提供数据来源的结构化数据收集声明3。每一条目描述应用所收集的一种数据类型,包含以下子键:

  • NSPrivacyCollectedDataType:来自苹果封闭列表的数据类型(例如NSPrivacyCollectedDataTypeNameNSPrivacyCollectedDataTypeEmailAddressNSPrivacyCollectedDataTypeCrashData)。
  • 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

  1. 出现在苹果要求隐私清单的SDK发布列表中的SDK。截至iOS 26,该列表包括Firebase Analytics、Google Mobile Ads、Adjust、Segment、AppsFlyer等约30个SDK。
  2. 以预编译二进制(XCFrameworks、静态库)形式分发、被应用链接的SDK。

提交一个链接了必需SDK但缺失该SDK清单的应用,会失败并返回ITMS-91xxx系列错误,涵盖隐私清单缺失、清单签名缺失或SDK命名空间内API声明缺失等问题2。修复方式是更新到附带清单的SDK版本,或移除该SDK。

对于第一方SDK(您的团队为自家应用所发布的内部框架),如果该框架不在苹果的列表中,您可以选择跳过清单。务实的做法仍是附带一份:未来苹果列表扩展、未来审计工作、未来第三方复用,都会因清单已就位而受益。

常见陷阱

来自App Store拒绝日志的五种常见失败模式:

1. 遗忘UserDefaults声明。 最常见的拒绝原因。应用在某个不起眼的地方使用了@AppStorage(其底层封装的是UserDefaults),但清单未予声明。修复:每个使用@AppStorageUserDefaults或其任何包装器的应用都需要带有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+应用意味着什么

三点要义。

  1. 将隐私清单视为结构化契约,而非勾选项。 清单是应用对私人数据处理方式的唯一机器可读记录。一次构建、永远忽略,等于在六个月后某个SDK更新悄然扩大清单所需范围时被拒。

  2. 每次发布时都审计您的UserDefaults和文件时间戳访问。 这是两个会随代码库静默扩张的类别。重构中新增的一个使用@AppStorage的SwiftUI视图,会把NSPrivacyAccessedAPICategoryUserDefaults拉入作用域;后续添加的文件列表功能则会带入NSPrivacyAccessedAPICategoryFileTimestamp。每次都要重新验证。

  3. 优先选择附带清单的SDK。 评估第三方SDK时,其隐私清单的存在与质量如今已成为质量信号。没有清单的SDK会迫使应用团队向上游施压;带清单的SDK则能干净地融入合并流程。

完整的Apple Ecosystem系列:类型化的App IntentsMCP服务器路由问题Foundation Models运行时与工具LLM之分三大表面单一事实来源模式两个MCP服务器Apple开发的hooksLive ActivitieswatchOS运行时SwiftUI内部机制RealityKit的空间心智模型SwiftData模式纪律Liquid Glass模式多平台发行平台矩阵Vision框架符号特效Core ML推理Writing Tools APISwift Testing我拒绝撰写的话题。系列入口位于Apple Ecosystem系列。如需更广义的iOS与AI智能体语境,请参阅iOS Agent Development指南

FAQ

NSPrivacyTrackingNSPrivacyCollectedDataTypeTracking有何区别?

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(既然没有跨应用追踪,提示也就没有必要)。

参考资料


  1. Apple Developer文档:Privacy manifest files。格式参考与2024年5月1日的强制执行时间线。 

  2. Apple Developer:App Store Connect submission errors,涵盖ITMS-91053(缺失API声明)及相关代码。 

  3. Apple Developer文档:App privacy configuration。四个顶层键(NSPrivacyTracking、NSPrivacyTrackingDomains、NSPrivacyCollectedDataTypes、NSPrivacyAccessedAPITypes)及其schema。 

  4. Apple Developer文档:Describing use of required reason API。五类必需理由API及其已批准理由代码。 

  5. Apple Developer:Third-party SDK privacy manifest requirements。必须附带隐私清单的SDK列表。 

  6. Apple Developer文档:User privacy and data use。追踪的狭义定义与App Tracking Transparency框架。 

  7. Apple Developer文档:NSPrivacyAccessedAPICategoryUserDefaults。涵盖所有UserDefaults访问的类别,包括基于@AppStorage的包装器。 

相关文章

Apple Foundation Models:端侧LLM框架详解

Apple 的 Foundation Models 框架:LanguageModelSession、@Generable 引导式生成、工具调用、可用性,以及何时该离开端侧模型。

3 分钟阅读

iOS 26 中的 App Intents 2.0:视觉智能、交互式片段与延迟属性

iOS 26 扩展了 App Intents,新增用于视觉智能的 IntentValueQuery、用于异步取值的 @DeferredProperty、交互式片段以及实体视图注解。

3 分钟阅读

两个MCP服务器让Claude Code成为iOS构建系统

XcodeBuildMCP与Apple的Xcode MCP为Claude Code提供对iOS构建、测试与调试的结构化访问。配置方法、实际效果与诚实的经验总结。

3 分钟阅读