← 所有文章

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

自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的包装器。 

相关文章

SwiftData's Real Cost Is Schema Discipline

SwiftData's API is two macros. The cost is what happens after you ship. Optional fields are the cheap migration; non-opt…

15 分钟阅读

Liquid Glass in SwiftUI: Three Patterns From Shipping Return on iOS 26

Apple's Liquid Glass is a one-line SwiftUI API. Three patterns from Return go beyond .glassEffect(): glass on text via C…

19 分钟阅读

The Cleanup Layer Is the Real AI Agent Market

Charlie Labs pivoted from building agents to cleaning up after them. The AI agent market is moving from generation to pr…

15 分钟阅读