Vuncloud 部落格
← 返回機房手記專欄

GitHub Actions iOS CI 為什麼這麼慢?CocoaPods / SPM / DerivedData 快取優化實戰(2026)

機房手記 · CocoaPods / SPM / DerivedData 在 GitHub Actions 怎麼快取 · 含完整 workflow 範例 ·約 12 分鐘閱讀

GitHub Actions iOS CI 快取優化:CocoaPods SPM DerivedData Xcode 構建變慢
先說結論

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 對照見 瀑布圖)。

30–60%
warm 構建時間常見降幅
3
CocoaPods · SPM · DerivedData
1–5 分鐘
僅快取優化常見節省(單項)

1. 為什麼 GitHub Actions iOS CI 會這麼慢?

很多團隊的第一反應是:「是不是 macos-latest runner 太慢?」「要不要換 M4 或更高配機器?」

在真實的 Xcode CI 構建變慢 場景裡,瓶頸往往不在 CPU,而在下面三類隱藏成本

  1. 依賴重新解析(CocoaPods / SPM)
  2. DerivedData 冷啟動重建(全量編譯)
  3. actions/cache 未命中,導致每次像首次構建

GitHub Actions 會放大這些問題,因為 runner 是臨時環境(每次乾淨磁碟)、快取不會自動命中、多分支 / 多 job 容易造成 cache key 污染。若你正在排查 Xcode 構建為什麼忽快忽慢,建議先分清 cold/warm,再對照本文配快取。

2. iOS CI 構建慢的真實瓶頸拆解

2.1 CocoaPods(Pods 快取)

典型冷啟動成本:pod install3–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)。

團隊審查 GitHub Actions workflow:CocoaPods SPM DerivedData 快取與 iOS CI 優化

3. 正確的 GitHub Actions iOS CI 快取方案

適用於 runs-on: macos-latest。下面三段可併進同一 job,放在 pod install / xcodebuild 之前(儲存由 actions/cache@v4 自動完成)。

3.1 CocoaPods 快取(必須)

CocoaPods 快取 · GitHub Actions
- 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 快取(必須)

SPM 快取 · iOS CI
- 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 快取(關鍵優化)

DerivedData 快取 · xcodebuild
- 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-),錯誤命中
restore-keys 降級

鎖檔案變更時可設前綴如 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 秒上傳/下載時間)
self-hosted 固定路徑
export DERIVED_DATA=/Volumes/Data/DerivedData/App-${{ github.run_id }}
export PODS_ROOT=/Volumes/Data/Pods/${{ github.ref_name }}

完整 job 範例、1TB 資料盤說明見 下方 workflowCI/CD 接入說明

self-hosted · 固定 DerivedData + Pods
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/cachepath 必須與 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 試一輪。

同系列專題:

固定 DerivedData 與 Pods 路徑?

Vuncloud Cloud Mac M4 Pro 自帶 1TB 資料盤,適合 self-hosted iOS CI:少靠 actions/cache 來回搬運,warm 構建更穩。

查看 Cloud Mac 套餐 · CI/CD 接入說明

機房手記 · iOS CI

GitHub Actions iOS CI 慢?先查這三塊快取

CocoaPods · SPM · DerivedData · workflow 範例

看瀑布圖 −1:40
限時優惠 點擊查看套餐