iOS 多語截圖自動化:在雲端 Mac mini 上批量產出 App Store 素材

CI/CD 實踐 ·約 6 分鐘閱讀

iOS 多語截圖自動化:在雲端 Mac mini 上批量產出 App Store 素材

出海團隊第一次上傳素材到 App Store Connect 時,幾乎都會撞上同一個數字:十四種語言 × 六張截圖 × 三種裝置尺寸,將近三百張圖。營運同事手動截一輪,一天時間就搭進去,還很容易漏改狀態列時間。這類重複性工作最適合丟給一台跑 macOS 圖形介面的機器批量處理,而不是佔用團隊裡唯一那台 MacBook。

为什么放到云端 Mac mini 上跑

本機筆電跑截圖有兩個硬傷:一是模擬器一開就是四五台,風扇狂轉還佔著螢幕沒法做別的事;二是本機環境和發布分支不同步,截圖用的程式碼常常跟實際送審的版本對不上。把這套流程搬到一台獨享的雲端 Mac mini 上,好處很直接:

截圖自動化本質上是「跑一遍 UI 測試順便截圖」,所以前提是應用程式要能被 UI 測試正常導引到目標頁面,埋點式的強制登入、驗證碼攔截都要提前做好測試環境開關。

用 fastlane snapshot 搭起流水线

先在專案裡初始化 snapshot,產生 SnapshotHelper.swiftSnapfile:

cd MyApp
fastlane snapshot init

Snapfile 是整套流程的設定核心,把語言清單、裝置清單、輸出目錄都寫死在這裡,避免每次手動帶參數:

devices([
  "iPhone 16",
  "iPhone 16 Pro Max",
  "iPad Pro (12.9-inch)"
])

languages([
  "zh-Hans", "zh-Hant", "en-US", "ja",
  "ko", "de-DE", "fr-FR", "es-ES",
  "pt-BR", "ru", "it", "id",
  "th", "vi"
])

scheme("MyAppUITests")
output_directory("./screenshots")
clear_previous_screenshots(true)
override_status_bar(true)
concurrent_simulators(true)

UI 測試裡只需要在目標頁面呼叫一句截圖函式,不用管多語言邏輯,fastlane 會自動照 languages 陣列一個個跑:

func testHomeScreenshot() {
    let app = XCUIApplication()
    app.launch()
    snapshot("01Home")
}

多模拟器并发与资源边界

打開 concurrent_simulators(true) 後,fastlane 會依裝置數量自動建立對應的模擬器實例。但「能開」不代表「該同時開這麼多」,記憶體才是真正的瓶頸。以雲端 Mac mini 的兩個規格為例:

機型 記憶體 建議並發模擬器數 完整跑一輪(14 語言 × 3 裝置)耗時參考
Mini Basic(M4/16GB) 16GB 3~4 台 約 55~70 分鐘
Mini Standard(M4/24GB) 24GB 5~6 台 約 35~45 分鐘

超過建議並發數後,系統會因記憶體壓力把模擬器行程換出到磁碟,截圖任務反而會排隊變慢,還容易觸發個別語言截圖失敗需要重跑。穩妥的做法是先用 xcrun simctl list devices 確認目前已啟動的模擬器數量,超過門檻就分批執行:

xcrun simctl list devices booted
fastlane snapshot --devices "iPhone 16" --languages "zh-Hans,ja,ko"
fastlane snapshot --devices "iPhone 16" --languages "en-US,de-DE,fr-FR"

命名规范与 frameit 加框

跑完一輪會得到幾百張圖,命名混亂是後續上傳 App Store Connect 時最容易出錯的環節。建議輸出目錄按 語言代碼/裝置名稱/序號-頁面名稱.png 三層結構存放,並與 Snapfile 的預設行為保持一致,不要自己再多包一層命名。

如果截圖需要帶裝置外框展示(常見於宣傳頁,而不是 App Store 商店頁本身),用 frameit:

fastlane frameit

它會讀取截圖的中繼資料自動比對對應機型的外框,批量輸出到 screenshots/framed 目錄,不需要在 Photoshop 裡一張一張手動對齊。

常见坑与排查

CI 集成:定时增量出图

素材不需要每次發布都全量重截,只有 UI 有變動的頁面才需要更新對應截圖。做法是在雲端 Mac mini 上跑一個排程任務,比對 Git diff 涉及的頁面,只重跑相關的 UI 測試案例:

git diff --name-only HEAD~1 HEAD | grep "Screens/" > changed_screens.txt

搭配 self-hosted runner 把這段邏輯接進 GitHub Actions,PR 合併到發布分支時自動觸發增量截圖,再由人工確認無誤後統一上傳。整套流程搬到獨享的實體節點上跑,不會和團隊其他人的建構任務搶資源,也不用為了截圖特地佔用一台開發機。

常見問題

一次跑幾台模擬器比較合適?

M4 芯片、16GB 記憶體的機型建議同時開 3~4 台模擬器截圖,超過這個數字任務會因記憶體壓力而排隊變慢,分兩批跑反而更快。

截圖裡的狀態列時間和電量怎麼統一?

在 Snapfile 裡設定 override_status_bar true,fastlane 會自動把時間改成 9:41、電量改成滿格,不需要手動修圖。

需要一台獨享 Mac mini 跑建置作業?

新加坡/東京/韓國/香港/美西三地實體節點,按天起租,10 分鐘內收到 VNC/SSH 憑證。

立即租用 Mac mini