GitHub Actions iOS CI 很慢時,核心原因通常不是機器性能,而是快取缺失或 cache key 配錯,集中在三塊:
- CocoaPods:未快取
Pods/,每次 CI 重複pod install - Swift Package Manager(SPM):每次重新解析依賴圖,
.build被清空 - Xcode DerivedData:冷啟動構建觸發全量 Swift 編譯與模組重建
正確配置 GitHub Actions 上的 CocoaPods 快取、SPM 快取 與 Xcode DerivedData 快取 後,warm 構建時間常見可減 30%–60%(Shadow 對照見 瀑布圖)。
1. 為什麼 GitHub Actions iOS CI 會這麼慢?
很多團隊的第一反應是:「是不是 macos-latest runner 太慢?」「要不要換 M4 或更高配機器?」
在真實的 Xcode CI 構建變慢 場景裡,瓶頸往往不在 CPU,而在下面三類隱藏成本:
- 依賴重新解析(CocoaPods / SPM)
- DerivedData 冷啟動重建(全量編譯)
- actions/cache 未命中,導致每次像首次構建
GitHub Actions 會放大這些問題,因為 runner 是臨時環境(每次乾淨磁碟)、快取不會自動命中、多分支 / 多 job 容易造成 cache key 污染。若你正在排查 Xcode 構建為什麼忽快忽慢,建議先分清 cold/warm,再對照本文配快取。
2. iOS CI 構建慢的真實瓶頸拆解
2.1 CocoaPods(Pods 快取)
典型冷啟動成本:pod install 約 3–8 分鐘。
問題本質:每次 CI 都重新執行 pod install——依賴解析、下載、整合全部重來。
正確做法:用 actions/cache 快取 Pods/,cache key 綁定 Podfile.lock 雜湊 + github.ref + arm64。CI 裡建議 pod install --deployment。
2.2 Swift Package Manager(SPM)
典型成本:解析 Package 依賴圖約 1–5 分鐘。
問題本質:每次重新解析;.build 被清空;Package.resolved 未納入 cache key。
正確做法:快取 ~/Library/Caches/org.swift.swiftpm 與 .build,key 含 Package.resolved 雜湊(見 §3.2)。
2.3 Xcode DerivedData
典型冷啟動增量:全量/近全量編譯約 5–15 分鐘。
問題本質:DerivedData 未複用 → 全量 Swift 編譯、資源重新處理、模組重建。
正確做法:xcodebuild -derivedDataPath DerivedData,並對該路徑配置 Xcode DerivedData 快取(見 §3.3)。
3. 正確的 GitHub Actions iOS CI 快取方案
適用於 runs-on: macos-latest。下面三段可併進同一 job,放在 pod install / xcodebuild 之前(儲存由 actions/cache@v4 自動完成)。
3.1 CocoaPods 快取(必須)
- name: Restore Pods cache
uses: actions/cache@v4
with:
path: Pods
key: pods-arm64-${{ github.ref }}-${{ hashFiles('**/Podfile.lock') }}
restore-keys: |
pods-arm64-${{ github.ref }}-
- name: Pod install
run: pod install --deployment
3.2 SPM 快取(必須)
- name: Restore SPM cache
uses: actions/cache@v4
with:
path: |
~/Library/Caches/org.swift.swiftpm
.build
key: spm-arm64-${{ github.ref }}-${{ hashFiles('**/Package.resolved') }}
restore-keys: |
spm-arm64-${{ github.ref }}-
3.3 DerivedData 快取(關鍵優化)
- name: Restore DerivedData cache
uses: actions/cache@v4
with:
path: DerivedData
key: dd-arm64-${{ github.ref }}-${{ hashFiles('**/Podfile.lock') }}
restore-keys: |
dd-arm64-${{ github.ref }}-
- name: Build iOS
run: xcodebuild -scheme MyApp -derivedDataPath DerivedData ...
日誌出現 Cache hit occurred(快取命中)後再對比步驟耗時;未命中時先查 cache key 與 path,別急著怪 Xcode 版本。
4. cache key 設計原則(非常重要)
很多 iOS CI 優化失敗,根子在 cache key 而不是 runner。
| ✔ 必須包含 | ❌ 常見錯誤 |
|---|---|
架構(arm64) |
不區分 arm64 / x86,換 runner 後全部未命中 |
分支(github.ref) |
只用鎖檔案,跨分支污染 DerivedData |
| 鎖檔案雜湊 | restore-keys 過寬(如單前綴 dd-),錯誤命中 |
鎖檔案變更時可設前綴如 dd-arm64-${{ github.ref }}- 部分複用;多 scheme 時在 key 加 ${{ matrix.scheme }}。同一 runner 上並發 job 勿共寫同一 DerivedData 根目錄——用 ${{ github.run_id }} 子目錄隔離。
5. self-hosted / Cloud Mac 優化方案(進階)
若使用 Mac mini 或 Cloud Mac 做 self-hosted runner,不必只依賴 actions/cache——磁碟一直在,warm 構建更穩:
- 固定 DerivedData 路徑
- 固定 Pods 目錄
- 本地磁碟 warm 快取(省 30–90 秒上傳/下載時間)
export DERIVED_DATA=/Volumes/Data/DerivedData/App-${{ github.run_id }}
export PODS_ROOT=/Volumes/Data/Pods/${{ github.ref_name }}
完整 job 範例、1TB 資料盤說明見 下方 workflow 與 CI/CD 接入說明。
env:
DERIVED_DATA: /Volumes/Data/DerivedData/App-${{ github.run_id }}
PODS_ROOT: /Volumes/Data/Pods/${{ github.ref_name }}
jobs:
build:
runs-on: [self-hosted, macos-m4-ios]
steps:
- uses: actions/checkout@v4
- name: Prepare dirs
run: mkdir -p "$DERIVED_DATA" "$PODS_ROOT"
- name: Pod install
run: pod install --deployment
- name: xcodebuild
run: xcodebuild -scheme MyApp -derivedDataPath "$DERIVED_DATA" build
6. 快取未命中 ≠ CI 變慢(常見誤判)
團隊常把 GitHub Actions iOS CI 很慢 歸因於 runner 或 Xcode 版本。實際情況往往是:
| 現象 | 更可能的原因 |
|---|---|
| 構建突然變慢 | 快取未命中 |
| P95 抖動 | 冷啟動構建混進 warm 統計 |
| 隨機變慢 | DerivedData 並發衝突 |
| 依賴全量重新構建 | cache key 配置錯誤 |
配合 構建波動專題:先分 cold/warm,再調快取,最後才評估硬體(為什麼 iOS CI/CD 跑在 Mac mini M4 上)。
7. 優化效果(真實收益)
僅優化快取,單項常見節省:
| 對象 | 冷啟動未命中時多耗 | 快取配對後 |
|---|---|---|
| CocoaPods | 3–8 分鐘 | warm 常 <30 秒–3 分鐘 |
| SPM | 1–5 分鐘 | 命中後解析大幅縮短 |
| DerivedData | 5–15 分鐘 | warm 以增量編譯為主 |
我們 14 天 Shadow 對照裡,僅優化快取一項 warm 牆鐘約 −1:40;疊加消除排隊、self-hosted 固定盤,warm P95 可從 14:12 壓到 6:05(−57%)——見 優化總覽。
8. 常見問題
GitHub Actions iOS CI 為什麼這麼慢?
多數不是 CPU,而是 CocoaPods / SPM / DerivedData 未快取或 cache key 錯誤。runner 每次乾淨環境,未命中成本被放大。
GitHub Actions 上 CocoaPods 怎麼快取?
快取 Pods/,cache key:pods-arm64-${{ github.ref }}-${{ hashFiles('**/Podfile.lock') }}。見 §3.1。
SPM 在 iOS CI 裡怎麼配快取?
快取 swiftpm 全域目錄與 .build,key 綁定 Package.resolved。見 §3.2。
Xcode DerivedData 快取路徑要注意什麼?
actions/cache 的 path 必須與 xcodebuild -derivedDataPath 一致;不必用系統預設路徑。
快取體積超限怎麼辦?
GitHub 單條快取約 10GB 上限。DerivedData 過大時按 scheme 拆 key,或 self-hosted 固定大盤不走上傳。
macos-latest 和 self-hosted 怎麼選?
託管只能用 actions/cache,跨 job 可能被清除;self-hosted 固定 /Volumes/Data,warm 更穩、CI 波動更低。對照見 優化總覽。
9. 結論與專題導航
iOS CI 優化投入產出比最高的一步,往往是先把 CocoaPods、SPM、DerivedData 三塊快取配對——不必先換晶片。今天就能改 workflow 試一輪。
同系列專題:
- iOS CI 構建很慢怎麼辦?GitHub Actions 上 Xcode build 變慢原因解析 — cold/warm、P95 統計
- GitHub Actions macOS 優化總覽 — P95 −57%、Shadow 瀑布圖
- 買還是租 · 月構建 500 次怎麼選
- 為什麼 iOS CI/CD 跑在 Mac mini M4 上 · CI/CD 接入說明
固定 DerivedData 與 Pods 路徑?
Vuncloud Cloud Mac M4 Pro 自帶 1TB 資料盤,適合 self-hosted iOS CI:少靠 actions/cache 來回搬運,warm 構建更穩。