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,再调缓存,最后才评估硬件(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/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 构建很慢怎么办?Xcode 构建变慢原因 — cold/warm、P95 统计
- GitHub Actions macOS 优化总览 — P95 −57%、Shadow 瀑布图
- 买还是租 · 月构建 500 次怎么选
- Mac mini M4 CI 入门 · CI/CD 接入说明
固定 DerivedData 与 Pods 路径?
Vuncloud Cloud Mac M4 Pro 自带 1TB 数据盘,适合 self-hosted iOS CI:少靠 actions/cache 来回搬运,warm 构建更稳。