← 所有文章

隱私清單深度剖析:什麼算是資料收集

自2024年5月1日起,每個上傳至App Store Connect的應用程式與第三方SDK都必須附帶隱私清單,以宣告其對「必要原因API」的使用、追蹤行為,以及收集的資料類型1。此清單為一份屬性列表(PrivacyInfo.xcprivacy),App Store會在提交時擷取,依據已知的隱私敏感API範圍進行驗證,並將其呈現於使用者可見的隱私營養標籤中。若應用程式未附隱私清單,或在未宣告核可原因的情況下使用隱私敏感API,將在提交時被以ITMS-91053或相關代碼退件2

多數團隊將清單視為一次性的合規任務:寫好、出貨、再也不看。但清單遠不止於此。它是應用程式與Apple之間的結構化合約,以機器可讀的形式編纂應用程式如何處理私人資料,而這也是此合約唯一存在的形式。仔細閱讀清單可揭示Apple認定為隱私敏感的範圍(其涵蓋面比多數開發者預期更廣)、使用者在營養標籤中實際看到的內容(比多數團隊以為的更細緻),以及第三方SDK必須揭露的事項(這是真正的義務,並非禮貌性質)。

TL;DR

  • 隱私清單包含四個頂層區段:NSPrivacyTrackingNSPrivacyTrackingDomainsNSPrivacyCollectedDataTypes,以及NSPrivacyAccessedAPITypes3
  • 必要原因API清單涵蓋五個初始類別:檔案時間戳記、系統開機時間、磁碟空間、啟用中的鍵盤,以及User Defaults。每個類別都有封閉式的核可原因代碼清單;應用程式必須擇一使用,或重寫程式碼以避免該API4
  • 追蹤的定義相當狹義:將使用者資料連結至其他公司的資料,以用於廣告或與資料仲介商共享。若NSPrivacyTrackingtrue,則所有用於追蹤的網域都必須出現在NSPrivacyTrackingDomains3
  • 第三方SDK必須提供自己的清單。應用程式的清單會在提交時與所有連結SDK的清單合併;若Apple公布清單上的SDK缺少清單,將阻擋應用程式提交5
  • 最大的陷阱:UserDefaults存取一律算作必要原因API呼叫。標準的UserDefaults.standard.set(...)需要宣告原因代碼。

四個區段

完整的PrivacyInfo.xcprivacy檔案有四個頂層鍵3。其結構為屬性列表,可以XML原始碼形式編輯,或透過Xcode的隱私清單編輯器處理。

NSPrivacyTracking(布林值)

宣告應用程式或SDK是否跨其他公司的應用程式與網站追蹤使用者。其語意定義與App Tracking Transparency一致:將使用者資料連結至其他公司的資料,以用於目標廣告投放、傳遞目標廣告,或與資料仲介商共享使用者資料6

NSPrivacyTrackingtrue,則所有用於追蹤的網域都必須出現在NSPrivacyTrackingDomains清單中。若為false,則此清單可省略(或留空)。

這個狹義定義很重要。許多顯示廣告的應用程式(透過Apple自家的SKAdNetwork或非追蹤性質的廣告SDK)可宣告NSPrivacyTrackingfalse。追蹤指的是與外部資料的連結,而非廣告的顯示行為。

NSPrivacyTrackingDomains(字串陣列)

應用程式或SDK基於追蹤目的而聯繫的網域清單。宣告NSPrivacyTrackingtrue的應用程式必須在此列舉每一個追蹤端點。

此清單在執行階段透過App Privacy Report強制執行:若應用程式聯繫被標記為追蹤但未宣告的網域,App Privacy Report會將此差異呈現給使用者。重複違規可能導致應用程式從App Store下架。

NSPrivacyCollectedDataTypes(字典陣列)

結構化的資料收集宣告,提供給App Store隱私營養標籤使用3。每筆條目描述應用程式收集的一種資料類型,包含以下子鍵:

  • NSPrivacyCollectedDataType:來自Apple封閉式清單的資料類型(例如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>

原因為封閉式清單。應用程式從Apple每個類別公布的清單中選擇一個或多個;任意字串將在提交時被拒絕。

五個必要原因API類別

Apple在2024年的初始清單中定義了五個需要宣告原因的API類別4

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原因即可涵蓋。透過app group跨應用程式擴充功能共享狀態的應用程式則加上1C8F.1。SDK作者使用C56D.1。透過MDM部署、讀取管理員設定鍵(例如com.apple.configuration.managed)的應用程式使用AC6B.1

陷阱在於:碰觸UserDefaults的第三方SDK會代表應用程式觸發此要求。SDK的清單必須宣告原因。若該SDK在Apple的必須提供隱私清單的SDK清單上,缺少SDK清單將阻擋應用程式提交。

追蹤網域:執行階段驗證

NSPrivacyTrackingDomains透過App Privacy Report在執行階段強制執行6。當應用程式宣告NSPrivacyTracking = true時,系統會追蹤每個網路請求,並比對目的地與宣告的清單。為追蹤目的而聯繫但未宣告的網域,會出現在App Privacy Report的「常見位置」與「其他聯繫的網域」區段中。

執行階段驗證創造了有趣的誘因結構。宣告NSPrivacyTracking = false(無追蹤)但被觀察到聯繫已知追蹤網域的應用程式,會在App Privacy Report中被標記,並可能引發使用者投訴。正確的做法是誠實宣告。

對於SDK,由SDK本身的隱私清單宣告其追蹤行為。應用程式的清單不需要列舉SDK的追蹤網域;SDK的清單已經做了。在提交時,Apple會合併清單並檢查一致性。

SDK清單:真正的義務

兩種類別的第三方SDK必須提供隱私清單5

  1. 在Apple公布的需要隱私清單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(您的團隊提供供自家應用程式使用的內部框架),若該框架不在Apple清單上,您可以選擇略過清單。務實的做法是無論如何都提供清單:未來Apple清單的擴充、未來的稽核工作、未來的第三方重用,都能因清單就位而受益。

常見陷阱

來自App Store退件記錄的五種反覆出現的失敗模式:

1. 遺忘UserDefaults宣告。 最常見的退件原因。應用程式在某處不起眼地使用了@AppStorage(其包裝了UserDefaults),但清單未宣告。修正方式:所有使用@AppStorageUserDefaults,或任何相關包裝的應用程式,都需要NSPrivacyAccessedAPICategoryUserDefaults搭配CA92.1

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. 過時的原因代碼。 Apple會不時更新核可原因代碼。2024年有效的代碼可能被取代或擴充。修正方式是在每個重大版本發布時重新查看目前公布的清單,而非沿用舊清單。

驗證工具

提交前值得執行的三項檢查:

Xcode的「產生隱私報告」封存工作流程。 執行Product > Archive後,Organizer會提供「產生隱私報告」動作,從應用程式與每個連結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 Privacy Report)。應用程式聯繫的網域會出現在報告中;可與清單的追蹤宣告交叉比對。

此模式對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框架Symbol EffectsCore ML推論Writing Tools APISwift Testing我拒絕撰寫的主題。中樞位於Apple Ecosystem系列。如需更廣泛的iOS搭配AI代理脈絡,請參閱iOS Agent Development指南

FAQ

NSPrivacyTrackingNSPrivacyCollectedDataTypeTracking有何差別?

NSPrivacyTracking是應用程式層級的布林值:此應用程式是否完全追蹤使用者(依Apple的狹義定義)?NSPrivacyCollectedDataTypeTracking則是針對每個資料類型:對於應用程式收集的某項資料類型,資料是否用於追蹤?應用程式可以在不追蹤的情況下收集資料(不跨公司連結的分析);每類型的旗標捕捉每個特定資料類型是否對追蹤行為有所貢獻。

內部專用的TestFlight應用程式需要隱私清單嗎?

需要。隱私清單的強制執行在提交至App Store Connect時運作,包括TestFlight建置版本。沒有清單的內部測試版會與公開發行版同樣未通過ITMS-91053檢查。

若我宣告了未使用的必要原因API會發生什麼事?

功能上沒有任何影響。清單宣告API範圍的上限;宣告未使用的類別並無害處。代價是文件漂移:未來的維護者可能假設宣告反映目前的程式碼,但實際上呼叫已被移除。正確的做法是宣告應用程式實際使用的項目,並在每次重大發布時稽核。

除了原始的五個類別,還有其他必要原因API類別嗎?

截至iOS 26,原始的五個類別(檔案時間戳記、系統開機時間、磁碟空間、啟用中的鍵盤、User Defaults)仍是標準清單4。Apple隨著時間在類別內新增了核可原因代碼,但並未擴充類別清單。請關注Apple的必要原因API文件頁以掌握新增項目。

隱私清單如何與App Tracking Transparency互動?

App Tracking Transparency(ATT)管理跨應用程式追蹤的執行階段權限提示。隱私清單則靜態地宣告應用程式的意圖。兩者互補:執行跨應用程式追蹤的應用程式在清單中宣告NSPrivacyTracking = true並且在執行階段請求ATT權限。宣告NSPrivacyTracking = false的應用程式不應請求ATT(缺乏跨應用程式追蹤代表此提示是不必要的)。

參考資料


  1. Apple Developer Documentation: Privacy manifest files. 格式參考與2024年5月1日的強制執行時程。 

  2. Apple Developer: App Store Connect submission errors 涵蓋ITMS-91053(缺少API宣告)與相關代碼。 

  3. Apple Developer Documentation: App privacy configuration. 四個頂層鍵(NSPrivacyTracking、NSPrivacyTrackingDomains、NSPrivacyCollectedDataTypes、NSPrivacyAccessedAPITypes)及其綱要。 

  4. Apple Developer Documentation: Describing use of required reason API. 五個必要原因API類別與核可原因代碼。 

  5. Apple Developer: Third-party SDK privacy manifest requirements. 必須提供隱私清單的SDK清單。 

  6. Apple Developer Documentation: User privacy and data use. 追蹤的狹義定義與App Tracking Transparency框架。 

  7. Apple Developer Documentation: 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 分鐘閱讀