App Store Connect 隱私清單無效:2026 怎麼修?

App Store Connect 隱私清單無效時,不要先在主 App 補一份通用清單,也不要盲目刪除依賴檔案;先依 App Store Connect 錯誤郵件中的 Bundle 路徑,在最終 xcarchive 定位責任 Target,再分別校驗格式、鍵值、Required Reason API、第三方 SDK 來源與簽名,最後重新 Archive、重簽名並完成一次真實上傳驗收。

這篇適合收到 ITMS-91056 或類似隱私清單錯誤、急著恢復 TestFlight 或 App 提交的獨立開發者。若你使用 Swift Package Manager、CocoaPods 或 XCFramework,並且透過遠端 Mac 或持續整合環境打包,下面的指標式排查可以幫你分清楚:問題在原始碼、依賴套件,還是最終產物。

01 先把錯誤郵件變成可驗證的路徑證據

App Store Connect 隱私清單無效,通常不是「專案裡沒有任何 PrivacyInfo.xcprivacy」這麼單純。郵件中的錯誤代碼、檔案名稱和 Bundle 路徑,會告訴你 Apple 實際檢查了哪個產物。

先建立一份不含專案名稱、Bundle ID、帳號和私有 SDK 名稱的紀錄:

  • 錯誤代碼,例如 ITMS-91056
  • Apple 指出的檔案名稱。
  • 完整 Bundle 或 Framework 相對路徑。
  • 被拒的是無效、缺失、API 理由不符,還是 SDK 簽名問題。
  • 對應的 Archive 名稱與建立時間。

Apple 的 TN3181 隱私清單排障說明要求排查時關注最終 App 內容,而不是只看 Xcode 專案導覽器。因為原始碼中的清單可能正常,但 Archive 內嵌的 Framework 仍然帶著另一份無效檔案。

注意: 不要把 App 隱私標籤、隱私清單、Required Reason API 和 App Review 當成同一項檢查。它們處理的資料與審核階段不同,修錯其中一項不代表其它項目已通過。

第一個決策分支:先判斷你要修哪一層

  • 若郵件指向某個 .framework.xcframework 內的清單,先追查依賴來源,不要只修改主 App。
  • 若郵件指向主 App Bundle,檢查清單是否被加入正確 Target,以及 Archive 是否真的包含它。
  • 若訊息提到 Required Reason API,把宣告回溯到實際程式碼和依賴程式碼,不要用任意理由填滿欄位。
  • 若訊息提到簽名或官方要求的第三方 SDK,除了清單內容,也要檢查二進位檔的簽名狀態。
  • 若路徑在 Extension、App Clip、Mac Catalyst 或 macOS Bundle,按責任 Bundle 個別驗證,不能以主 App 的結果代替。

02 第一步:在不改動原始 Archive 的前提下檢查格式

PrivacyInfo.xcprivacy 本質上是 Property List。你可以用 Xcode 的屬性列表編輯器查看,也可以用 plutil 做語法檢查。檢查前先複製 Archive,將所有指令指向副本;不要直接在準備上傳的原始產物內修改。

以下使用占位路徑,請替換成你的實際副本路徑:

ARCHIVE_COPY="/path/to/archive-copy.xcarchive"

find "$ARCHIVE_COPY/Products" \
  -name "PrivacyInfo.xcprivacy" \
  -print

plutil -lint \
  "$ARCHIVE_COPY/Products/Applications/YourApp.app/PrivacyInfo.xcprivacy"

Apple 的 隱私清單放置說明指出,App 和第三方 SDK 都可能各自提供隱私清單。因此,find 找到多份檔案不是異常;重點是每一份檔案所在的 Bundle 是否合理,內容是否符合該 Bundle 的實際用途。

檢查指標 證據入口 通過標準 不通過時的下一步
根物件 Xcode 編輯器、plutil 根層是有效的 Property List 物件 修正檔案格式,再重新產生 Archive
資料型別 NSPrivacyAccessedAPITypes 等欄位 陣列、字典、字串和布林值型別正確 對照 Apple 文件逐欄檢查,不要只刪除報錯欄位
空值與多餘欄位 清單全文 沒有不必要的空陣列或非預期鍵值 移除無用途內容,保留與實際 API 使用一致的宣告
產物位置 Products 下的 Bundle 清單位於實際責任 Bundle 回到 Target 或套件資源設定修正
Archive 完整性 Archive 內檔案與簽名 修復後由全新 Archive 產生 不要重複使用已修改或已上傳的舊產物

plutil -lint 通過,只代表檔案能被解析。它不會判斷 approved reason 是否與 API 用途相符,也不會替你確認第三方 SDK 是否需要自己的清單。這正是「本機檢查成功,但 App Store Connect 仍然拒絕」最常見的原因。

為什麼 plutil 通過後仍可能被拒? 因為語法檢查和 Apple 的規則檢查是兩個層次。你仍要核對鍵名、陣列與字典層級、允許的 reason 值、實際 Bundle 路徑,以及官方要求的 SDK 簽名條件。

03 第二步:把 Required Reason API 宣告連回真實呼叫

Required Reason API 的正確做法不是找一個「最容易通過」的理由,而是確認程式或依賴套件實際使用了哪一類 API,再選擇與功能一致的 approved reason。

Apple 的 Required Reason API 官方說明TN3183 設定指南可用作兩個核對入口。你需要留下以下證據:

  1. 從隱私報告、編譯警告或 Apple 錯誤郵件確認 API 類別。
  2. 在自有程式碼中搜尋相關呼叫。
  3. 檢查依賴清單,判斷是否由 SDK 引入。
  4. 以 Apple 文件中的允許理由比對實際功能。
  5. 將宣告放在真正使用該 API 的 Bundle 對應清單中。

可以先用程式碼搜尋縮小範圍:

grep -R "NSUserDefaults\|mach_absolute_time\|systemUptime" \
  /path/to/source /path/to/dependencies

這些只是搜尋示例,不代表每個專案都應宣告相同 API。你不能因為搜尋到一個相似字串,就直接替整個 App 或所有 SDK 填入同一理由。Apple 的 隱私報告說明則可用來交叉檢視資料使用與清單內容是否一致。

多個 Target 和 Extension 要不要分別處理?

若主 App、Extension、App Clip、Mac Catalyst 或 macOS Bundle 是獨立的發布產物,就要按各自的責任 Bundle 驗證。 不能假設主 App 的 PrivacyInfo.xcprivacy 會自動覆蓋嵌套 Framework 或 Extension。

是否需要新增檔案,取決於實際使用者和打包位置:

  • 主 App 使用 API,宣告應進入主 App 的清單。
  • Extension 自己或其依賴使用 API,檢查 Extension 最終 Bundle。
  • Framework 帶有自己的 API 使用和清單,先確認維護者提供的內容。
  • Swift Package 若以資源方式提供清單,確認資源已被正確納入建置。
  • Mac Catalyst 和 macOS Target 即使共用部分原始碼,也要查看各自 Archive 結構。

04 第三步:依賴來源決定升級、替換或暫時處理

第三方 SDK 隱私清單無效時,是否可以自己修改,要看你是否擁有該 SDK 的原始碼、建置責任和重新簽名條件。長期方案通常是升級到維護者已提供有效清單的版本,而不是每次打包後手改依賴產物。

依賴形式 優先檢查位置 建議處置 需要保留的證據
Swift Package Manager Package 資源宣告與 Archive 內 Bundle 先鎖定版本,再升級到含有效清單的版本 Package.resolved、建置日誌、清單路徑
CocoaPods Pods 產物與 Copy Resources 設定 檢查套件版本和整合腳本 Podfile.lock、安裝輸出、Archive 內容
動態 Framework .framework 內部清單與簽名 向維護者索取修正版或更新二進位檔 Framework 路徑、簽名驗證結果
二進位 XCFramework 所選平台 slice 和內嵌 Bundle 確認實際使用的 slice,不只看壓縮包根目錄 XCFramework 結構、平台、版本與簽名資訊

Apple 的第三方 SDK 要求同時涉及官方要求名單中的 SDK、隱私清單和相應簽名。你不能只把主 App 的清單複製到 SDK,也不能用主 App 的宣告代替 SDK 對自身 API 使用的責任。

第三方 SDK 的清單可以自己改嗎? 如果只是為了緊急驗證,且你確定修改內容符合實際 API 使用,可以在 Archive 副本中測試;但這會改變已簽名產物。之後必須依發布流程重新簽名,否則可能在上傳或執行階段出現新的簽名錯誤。這不是可長期依賴的修復,正式版本應改用供應方修正版或替換依賴。

經驗: 不要刪掉 SDK 清單來「讓檢查重新開始」。刪除檔案可能掩蓋 SDK 真正的 API 使用,也可能使官方要求的第三方 SDK 條件更難滿足。先保存原始 Archive,再對副本做任何實驗。

05 第四步:用 Archive 結構確認清單真的進入產物

原始碼中有檔案,不代表它已進入最終 App。常見原因包括:

  • 檔案只加入了某個 Target,沒有加入實際 Archive 使用的 Target。
  • Swift Package 宣告了檔案,但沒有將它作為資源打包。
  • 建置腳本把清單複製到錯誤的 Bundle。
  • 多個 Configuration 使用不同的資源路徑。
  • XCFramework 選取了另一個平台 slice。
  • 你檢查的是新原始碼,但上傳的是舊 Archive。

可用以下方式定位 Bundle:

find "/path/to/YourApp.xcarchive/Products" \
  \( -name "*.app" -o -name "*.appex" -o -name "*.framework" \) \
  -print

find "/path/to/YourApp.xcarchive/Products" \
  -name "PrivacyInfo.xcprivacy" \
  -exec shasum -a 256 {} \;

不要只記錄檔案名稱。請把每份清單和它的父層 Bundle、建置版本、依賴版本、Configuration 一起保存。若同一個檔名在主 App 和嵌套 Framework 各出現一次,兩者仍然是不同的檢查對象。

你也應在 Xcode 中查看隱私報告,再和 Archive 的實際檔案互相比對。報告可以提示宣告內容,但不能取代對最終 Bundle 路徑和簽名狀態的檢查。

06 第五步:重新歸檔、重簽名,再做一次真實上傳

修復後不要直接覆寫原 Archive,也不要把同一份已修改的產物反覆上傳。建立全新的 Archive,並將以下狀態分開記錄:

  1. 本地語法通過plutil 可解析清單。
  2. Archive 完整:責任 Bundle 內有正確版本的清單。
  3. 簽名有效:修改過 Framework 或 App 後,已按發布流程重新簽名。
  4. 上傳被接收:Xcode 或 Transporter 沒有立即拒絕。
  5. 後台處理完成:App Store Connect 完成處理,沒有再次產生隱私清單錯誤。

Apple 的 Xcode App 發布文件可作為重新 Archive 與分發流程的官方參考。這裡的關鍵不是「按一次上傳」,而是確認你上傳的確實是修復後的新產物。

發布前決策條件

  • 若錯誤路徑指向主 App,且 Archive 內清單缺失:修正 Target 資源設定,重新 Archive。
  • 若錯誤路徑指向第三方 Framework,且依賴可升級:優先升級並鎖定版本,不要手改產物。
  • 若只能暫時修改二進位依賴:只在 Archive 副本中操作,完成重新簽名,並排定替換或升級。
  • plutil 通過但 Apple 仍拒絕:回到鍵值、approved reason、Bundle 路徑和 SDK 簽名,不要重複做語法檢查。
  • 若本機可以上傳、持續整合卻失敗:保存依賴鎖檔、Xcode 版本、建置入口、Archive 路徑和隱私報告,先比較兩個環境的最終產物。
  • 若需要恢復 TestFlight 且沒有可重現的 macOS 環境:先建立一個能保留 Archive 和完整日誌的遠端 Mac,再進行乾淨建置。

對獨立開發者而言,真正容易遺漏的是證據保存。至少保留清單差異、依賴鎖檔、建置日誌、上傳回應和錯誤郵件。下次升級 Xcode 或 SDK 時,你才能判斷是規則變動,還是依賴版本重新帶入了問題。

07 目前環境與遠端 Mac 的取捨

如果你目前依賴個人 Mac,常見缺點是 Archive 可能只留在本機、依賴快取與 Xcode 狀態難以重現,而且本機被其他工作占用時,無法穩定保留完整上傳證據。Windows 或 Linux 環境則無法直接提供完整的 macOS、Xcode 和簽名工具鏈。

若你只是偶爾提交一次,而且已有穩定的本地 Mac,不必為了單一錯誤立刻改變整套流程。相反地,如果你需要反覆重建、保存多個 Archive,或要讓持續整合環境和人工排查使用同一套工具鏈,租用 CALMVPS 的遠端 Mac 會更容易固定 Xcode、依賴鎖檔和發布日誌。你可以先查看遠端 Mac 方案與計費資訊,再按你的建置頻率判斷是否值得遷移。

這種方案不適合需要長期滿載編譯、實體 USB 裝置或本地螢幕除錯的工作。若只是短期恢復提交、重現一個 SDK 問題,或需要一台能保留完整 Archive 的發布環境,使用 CALMVPS 的遠端 Mac 訂購入口會比臨時購買一台專用 Mac 更容易控制週期與環境。

你現在要做的不是再新增一份看似正確的清單,而是沿著 Apple 指出的 Bundle 路徑走完六項驗收:錯誤來源、plist 格式、API 理由、依賴來源、Bundle 位置和最終上傳結果。只要任何一項仍以猜測代替證據,App Store Connect 隱私清單無效就可能在下一次 Archive 再度出現。