VMArm 技術紀錄

雲端 Mac 上的 iOS 隨選資源包建置與驗收

雲端 Mac 上的 iOS 隨選資源包建置與驗收

iOS 專案將關卡、高解析度紋理或離線模型移入 On-Demand Resources 後,安裝包通常會明顯縮小,但建置成功不代表資源一定可用。最常見的問題不是編譯錯誤,而是標籤遺漏、測試包意外嵌入全部資源,或發佈產物中的資源包與程式碼請求不一致。雲端 Mac 很適合將這類檢查固定為獨立作業:每次都從乾淨的工作區封存,保留清單與校驗和,再執行一次真實請求。

先定義可驗收的邊界

ODR 驗收至少要涵蓋三個層面:專案設定、匯出產物與執行階段請求。只檢查其中一層,必然會留下盲點。

專案層需要確認 Target 已啟用 On-Demand Resources,且資源目錄中的每個標籤都有明確用途。產物層檢查 .assetpack、清單檔案與檔案雜湊。執行階段則要驗證首次請求、釋放資源與再次請求。

將「應用程式能啟動」當成 ODR 驗收標準並不足夠。未被存取的關卡資源即使完全缺失,也不會妨礙首頁啟動。

建議為每個標籤保存四項資訊:負責人、觸發頁面、預期資源類型,以及是否屬於首次安裝的必要內容。這些資訊應寫入儲存庫中的一般文字清單,而不是只留在 Xcode 介面中。

使用穩定標籤控制分包粒度

標籤不要直接與資料夾數量一一對應。切分過細會產生大量小型資源包與頻繁請求;切分過粗,則會讓使用者為了單一頁面下載整組無關資源。較穩妥的做法是依使用者操作路徑劃分,例如 level-01tutorial-audiomodel-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、檔案雜湊,並在清除快取後重新請求資源,避免舊資料掩蓋問題。

獨享實體 Mac 節點

為下一次 Xcode 建置選擇持續在線的雲端 Mac

確認晶片、記憶體、儲存空間、計費週期及節點區域後,即可進入設定流程。實際可用狀態以控制台即時回傳為準。

選擇雲端 Mac 設定