iOS 專案將關卡、高解析度紋理或離線模型移入 On-Demand Resources 後,安裝包通常會明顯縮小,但建置成功不代表資源一定可用。最常見的問題不是編譯錯誤,而是標籤遺漏、測試包意外嵌入全部資源,或發佈產物中的資源包與程式碼請求不一致。雲端 Mac 很適合將這類檢查固定為獨立作業:每次都從乾淨的工作區封存,保留清單與校驗和,再執行一次真實請求。
先定義可驗收的邊界
ODR 驗收至少要涵蓋三個層面:專案設定、匯出產物與執行階段請求。只檢查其中一層,必然會留下盲點。
專案層需要確認 Target 已啟用 On-Demand Resources,且資源目錄中的每個標籤都有明確用途。產物層檢查 .assetpack、清單檔案與檔案雜湊。執行階段則要驗證首次請求、釋放資源與再次請求。
將「應用程式能啟動」當成 ODR 驗收標準並不足夠。未被存取的關卡資源即使完全缺失,也不會妨礙首頁啟動。
建議為每個標籤保存四項資訊:負責人、觸發頁面、預期資源類型,以及是否屬於首次安裝的必要內容。這些資訊應寫入儲存庫中的一般文字清單,而不是只留在 Xcode 介面中。
使用穩定標籤控制分包粒度
標籤不要直接與資料夾數量一一對應。切分過細會產生大量小型資源包與頻繁請求;切分過粗,則會讓使用者為了單一頁面下載整組無關資源。較穩妥的做法是依使用者操作路徑劃分,例如 level-01、tutorial-audio、model-basic,並確保程式碼中的請求名稱與資源目錄標籤完全一致。
為標籤清單設定門禁
可以在儲存庫中保存允許使用的標籤:
level-01
level-02
tutorial-audio
model-basic
建置指令碼從專案檔案或產生後的資源資訊中擷取標籤,排序後與這份清單比較。新增標籤時必須同步修改清單;刪除標籤時,也要一併檢查程式碼中的 NSBundleResourceRequest 呼叫。這樣便能將拼字錯誤從執行階段提前到合併階段處理。
標籤命名應只使用穩定的 ASCII 字元,避免空格、大小寫混用與臨時版本號。資源升級應透過內容版本或校驗和表示,不要持續建立 level-final-v2-new 這類難以維護的名稱。
固定封存與匯出條件
同一個提交必須使用固定的 workspace、scheme、Release 設定與匯出選項。VMArm 節點上的作業可以先清空本次輸出目錄,但不要不加區分地刪除全域快取,否則很難判斷變化究竟來自資源還是環境。
set -euo pipefail
: "${WORKSPACE:?WORKSPACE is required}"
: "${SCHEME:?SCHEME is required}"
ROOT="$PWD"
ARCHIVE_PATH="$ROOT/out/Game.xcarchive"
EXPORT_DIR="$ROOT/out/export"
rm -rf "$ARCHIVE_PATH" "$EXPORT_DIR"
mkdir -p "$EXPORT_DIR"
xcodebuild \
-workspace "$WORKSPACE" \
-scheme "$SCHEME" \
-configuration Release \
-destination 'generic/platform=iOS' \
-archivePath "$ARCHIVE_PATH" \
clean archive
xcodebuild \
-exportArchive \
-archivePath "$ARCHIVE_PATH" \
-exportPath "$EXPORT_DIR" \
-exportOptionsPlist "$ROOT/ci/ExportOptions.plist"
本機驗收流程可將 embedOnDemandResourcesAssetPacksInBundle 設為 true,先確認分包內容與請求邏輯正確。正式流程則必須使用符合實際發佈方式的匯出設定,不能沿用本機嵌入設定。兩種流程的產物必須分別存放在不同目錄,避免測試包覆蓋待發佈的套件。
對產物進行三層檢查
第一層檢查檔案是否存在。第二層檢查 plist 是否能正常解析。第三層為所有資源檔案產生穩定的校驗和,以便比較同一提交的兩次建置結果。
set -euo pipefail
ARCHIVE_PATH="$PWD/out/Game.xcarchive"
EXPORT_DIR="$PWD/out/export"
REPORT_DIR="$PWD/out/report"
mkdir -p "$REPORT_DIR"
find "$ARCHIVE_PATH" "$EXPORT_DIR" \
-name '*.assetpack' \
-print | LC_ALL=C sort > "$REPORT_DIR/assetpacks.txt"
find "$ARCHIVE_PATH" "$EXPORT_DIR" \
-name 'AssetPackManifest.plist' \
-exec plutil -lint {} \; > "$REPORT_DIR/plist-lint.txt"
find "$ARCHIVE_PATH" "$EXPORT_DIR" \
-type f \
\( -name '*.car' -o -name '*.plist' -o -name '*.assetpack' \) \
-exec shasum -a 256 {} \; |
LC_ALL=C sort > "$REPORT_DIR/checksums.txt"
test -s "$REPORT_DIR/assetpacks.txt"
如果 .assetpack 實際上是目錄,最後一條雜湊命令不會直接計算目錄內容,因此還應將該目錄下的一般檔案納入檢查。報告中應記錄提交編號、Xcode 版本、scheme 與匯出設定檔的雜湊,但不得寫入簽章私鑰、權杖或完整的憑證路徑。
比較兩次報告時,要先區分「檔案順序變化」與「內容變化」。統一排序可以排除前者。若同一提交的雜湊仍持續變化,再檢查產生指令碼是否寫入時間戳記、工作區絕對路徑或隨機識別碼。
執行階段驗證與故障定位
執行階段測試必須從乾淨狀態開始。安裝驗收套件後,請求一個非首次安裝標籤,記錄請求開始、允許存取與釋放資源這三個事件。接著結束應用程式、清理測試環境,再執行一次,以確認成功並非來自舊快取。
常見症狀的疑難排解順序
請求立即失敗時,先核對程式碼標籤與資源目錄標籤;請求長時間等待時,檢查資源包是否正確匯出,以及測試網路是否正常;本機嵌入成功但正式流程失敗時,應重點比較兩份 ExportOptions 與資源託管設定;只有部分資源缺失時,則要檢查同名檔案是否被分配到多個標籤,或資源是否仍屬於主套件。
不要用無限重試掩蓋錯誤。請為請求設定明確的逾時限制;失敗日誌只記錄標籤、錯誤網域、錯誤碼與階段,不得輸出存取權杖或敏感路徑。驗收完成後,保留報告、匯出設定雜湊與失敗日誌,並清理暫存套件與測試快取。如此一來,下次 ODR 變更出現偏差時,就能直接比較產物,而不必重新猜測 Xcode 執行了哪些操作。
常見問題
開發測試時應該把隨選資源包嵌入 App 嗎?
可以。第一階段可將 embedOnDemandResourcesAssetPacksInBundle 設為 true,先確認資源分包是否正確,再用實際發佈設定執行另一條生產型匯出流程。
只比對 IPA 大小就能確認 ODR 正確嗎?
不能。還要核對 assetpack 數量、標籤映射、AssetPackManifest.plist、檔案雜湊,並在清除快取後重新請求資源,避免舊資料掩蓋問題。
為下一次 Xcode 建置選擇持續在線的雲端 Mac
確認晶片、記憶體、儲存空間、計費週期及節點區域後,即可進入設定流程。實際可用狀態以控制台即時回傳為準。