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,再调缓存,最后才评估硬件(Mac mini M4 CI 入门)。

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
限时优惠 点击查看套餐