GitHub Actions の iOS CI が遅いとき、犯人は CPU ではなくcache がない・key がズレていることが多い。当たり所は三つ:
- CocoaPods:
Pods/未キャッシュ → 毎回pod installから - Swift Package Manager(SPM):依存グラフを毎回 resolve、
.buildが job ごとに消える - Xcode DerivedData:コールドビルドで Swift 全量コンパイル・モジュール再構築
CocoaPods / SPM / DerivedData の cache を正しく配線すると、warm ビルドの壁時計はよく30%–60% 短くなる(Shadow 対照は ウォーターフォール拆解)。
1. GitHub Actions iOS CI が遅く感じる理由
まず出る声はだいたいこうです:「macos-latest が貧弱?」「M4 か上位機に乗り換える?」
現場の Xcode CI 遅延では、ボトルネックは CPU より見えにくい三つのコストに寄ることが多いです:
- 依存の再 resolve(CocoaPods / SPM)
- DerivedData のコールドスタート(全量コンパイル)
- actions/cache のミス——毎回が初回ビルドみたいになる
GitHub Actions はこれを増幅します。runner は使い捨て(job ごとにクリーンディスク)、cache は勝手に残らない、多ブランチ・多 job で key が汚れやすい。同じ commit なのに速い run と遅い runを追っているなら、まず cold / warm を分けてから、この記事の cache 設計に当ててください。
2. 壁時計が消える場所
2.1 CocoaPods(Pods cache)
典型的なコールドコスト:pod install およそ 3–8 分。
本質:毎 CI で pod install を最初から——resolve、ダウンロード、統合を全部やり直し。
対処:actions/cache で Pods/ を保存。key は Podfile.lock ハッシュ + github.ref + arm64。CI では pod install --deployment。
2.2 Swift Package Manager(SPM)
典型的なコスト:パッケージグラフの resolve で 1–5 分。
本質:毎 run で再 resolve、.build 消去、Package.resolved が key に入っていない。
対処:~/Library/Caches/org.swift.swiftpm と .build を cache。key に Package.resolved ハッシュ(§3.2)。
2.3 Xcode DerivedData
典型的なコールド増分:全量〜準全量コンパイルで 5–15 分。
本質:DerivedData 未再利用 → Swift 全量コンパイル、アセット再処理、モジュール再構築。
対処:xcodebuild -derivedDataPath DerivedData でパスを固定し、そのパスを cache(§3.3)。
3. 動く GitHub Actions iOS CI cache 設定
runs-on: macos-latest 向け。下の三ブロックを同一 job に、pod install / xcodebuild の前へ(保存は actions/cache@v4 が自動)。
3.1 CocoaPods cache(必須)
- 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 cache(必須)
- 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 cache(インパクト大)
- 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 を確認。ミスなら先に key と path——Xcode バージョンのせいにしない。
4. cache key 設計(ここを飛ばさない)
iOS CI 最適化が runner ではなく key で落ちるケース、現場では珍しくありません。
| ✔ 入れるべきもの | ❌ よくあるミス |
|---|---|
アーキテクチャ(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 cache(job あたり 30–90 秒のアップロード/ダウンロードを省略)
export DERIVED_DATA=/Volumes/Data/DerivedData/App-${{ github.run_id }}
export PODS_ROOT=/Volumes/Data/Pods/${{ github.ref_name }}
job 全文と 1 TB データディスクのメモ:下の workflow と CI/CD 導入 FAQ。
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. cache ミス ≠ 遅い runner(よくある誤読)
GitHub Actions iOS CI が遅いと、runner や Xcode 版のせいにしがちです。実際はこちらが多い:
| 見える現象 | よりありそうな原因 |
|---|---|
| 突然ビルドが遅くなる | cache ミス |
| P95 が揺れる | cold ビルドが warm 統計に混ざる |
| ランダムに遅くなる | 並列 job の DerivedData 争奪 |
| 依存がゼロから再ビルド | cache key の誤設定 |
iOS CIが遅い?GitHub Actionsでxcodebuildがブレる理由と併読:cold / warm 分離 → cache 調整 → そのあとハード評価(2026 年、なぜ iOS CI/CD は Mac mini M4 で回すのか)。
7. 実測での改善幅
cache だけ直したときの典型的な節約:
| レイヤー | コールドミス時の上乗せ | cache 配線後 |
|---|---|---|
| CocoaPods | 3–8 分 | warm ではよく <30 秒–3 分 |
| SPM | 1–5 分 | ヒット時は resolve が大幅短縮 |
| DerivedData | 5–15 分 | warm はほぼ増分コンパイル |
14 日間の Shadow では、cache 最適化だけで warm 壁時計がおよそ −1:40。キュー排除と self-hosted 固定ディスクを重ねると warm P95 は 14:12 → 6:05(−57%)——GitHub Actions macOS ランナー最適化を参照。
8. FAQ
GitHub Actions の iOS CI はなぜ遅い?
CPU より、CocoaPods / SPM / DerivedData が未キャッシュか key がズレていることが多い。使い捨て runner がミスを増幅します。
GitHub Actions で CocoaPods を cache するには?
Pods/ を cache。key:pods-arm64-${{ github.ref }}-${{ hashFiles('**/Podfile.lock') }}。§3.1 参照。
iOS CI で SPM を cache するには?
swiftpm グローバルディレクトリと .build を cache。key は Package.resolved にバインド。§3.2 参照。
DerivedData cache の path はどうする?
actions/cache の path は xcodebuild -derivedDataPath と一致させる。システムデフォルトは不要。
cache が大きすぎる?
GitHub はエントリあたり約 10 GB 上限。DerivedData は scheme ごとに key を分割するか、self-hosted で固定ディスクに置いてアップロードを省略。
macos-latest と self-hosted の違いは?
託管は actions/cache のみ、job 間で消えることも。self-hosted は /Volumes/Data 固定で warm が安定。GitHub Actions macOS ランナー最適化で対照。
9. まとめと関連ガイド
iOS CI 最適化で ROI が高いのは、チップ交換の前に CocoaPods・SPM・DerivedData の cache を配線すること——workflow は今日から試せます。
同シリーズ:
- iOS CIが遅い?GitHub Actionsでxcodebuildがブレる理由 — cold / warm、P95 統計
- GitHub Actions macOS ランナー最適化:P95 57% 短縮 + iOS CI 実践ガイド — P95 −57%、Shadow ウォーターフォール
- 買う vs 借りる · 月500回iOSビルド:Mac mini購入 vs Mac Cloudサーバーレンタル コスト比較ガイド
- 2026 年、なぜ iOS CI/CD は Mac mini M4 で回すのか · CI/CD 導入 FAQ
DerivedData と Pods のパスを固定したい?
Vuncloud Cloud Mac M4 Pro は 1 TB データディスク付き——self-hosted iOS CI 向け。actions/cache の往復を減らし、warm を安定させやすい構成です。