2026年 専有リモート Mac CI における CocoaPods と Swift Package Manager の混合依存:キャッシュディレクトリ、並行性の分離、SSH で再現可能な xcodebuild パラメータ意思決定マトリクス+FAQ
CocoaPods と SPM が同一 Xcode プロジェクトに共存するとき、CI のつらみは「コンパイルできるか」より前に、キャッシュの着地先・並列ジョブの踏み合い・SSH 上で同じ xcodebuild 引数を再現できるか、に現れます。本稿では専有リモート Mac を前提に、パス・分離ルール・フラグの意思決定表と FAQ を runbook 用に整理します。
2026 年も CocoaPods と SPM の併用が普通である理由
多くの本番 iOS コードベースでは、CocoaPods を配布の正(Podfile.lock)としつつ、第一者パッケージや公式ライブラリを SPM で取り込む構成が残っています。解決グラフ・ディスク上の配置・Xcode の配線はスタックごとに異なります。専有リモート Mac CI ではマシンは自前でも、複数ジョブのタイムシェア や ジョブ内並列 によりディスク競合は起こり得ます。まず各ツールの書き込み先を地図にし、並行性とフラグを決めます。
キャッシュパスと正の所在
以下は一般的なユーザホームを想定した例です。CI ではグローバルキャッシュとビルドごとの出力を環境変数で明示し、無人アカウント間でデフォルトが漂わないようにします。
| 領域 | 典型パス/正 | CI での推奨 |
|---|---|---|
| CocoaPods specs/キャッシュ | ~/.cocoapods(repos・ダウンロード等) |
読み取りミラーをマウントして高速化。書き込みジョブはジョブ別サブツリーやホームルート分離 |
| Pods ワークスペース出力 | リポジトリ内 Pods/、Podfile.lock |
lockfile をコミット。重い xcodebuild の前に pod install をステージしロック争いを避ける |
| SPM グローバルキャッシュ | ~/Library/Caches/org.swift.swiftpm(典型) |
大規模リポでは -clonedSourcePackagesDirPath と組み合わせ、clone を制御パスへ |
| SPM と Xcode の統合 | Derived Data 配下の SourcePackages、解決成果物 |
-derivedDataPath で分離。両方がプロジェクトに触れる場合は CocoaPods が workspace を生成した後にシーケンスする |
「専有」ホスト上の並行性分離
専有 は シングルスレッド を意味しません。1 台のリモート Mac に複数 Runner・SSH セッション・キュー済みジョブが載ることがあります。実務ルール:
- ジョブ別 Derived Data:区別した
-derivedDataPath(または同等)を渡し、SPM の解決成果物とモジュール索引の交差汚染を防ぐ。 - 解決が重い工程の直列化/間引き:
pod install、初回のresolvePackageDependencies、大きな git clone は IO を圧迫する。xcodebuild archiveなど CPU 重い工程と時間をずらすか並列度を下げる。 - アカウントとキーチェーン境界:同一 macOS ユーザーで動く複数ジョブは署名アイデンティティと SPM 資格情報を共有する。より強い分離は複数ユーザーまたは VM——運用コストは上がる。
Runner 方式と SSH の長寿命セッションの整理は 2026年 Jenkins SSH ビルドエージェント vs GitHub Actions セルフホスト macOS Runner:専有リモート Mac 上の並行性、SSH セッション安定性、TCO 意思決定マトリクス+FAQ を参照。導入全体の流れは 2026年リモート Mac mini で iOS CI 環境を構築:Xcode から TestFlight までの全工程 と併せると、依存取得フェーズとコンパイルフェーズの切り分けがしやすくなります。
SSH で再現可能な xcodebuild パラメータ・マトリクス
以下はよくある前提:CocoaPods が workspace を既に生成済み で SPM パッケージが統合済み。各 SSH セッションでカレントディレクトリと Xcode の選択(xcode-select)を固定してからフラグを選びます。正確なスイッチは Xcode バージョン依存のため、利用中の環境で xcodebuild -help を確認してください。
| シナリオ | 推奨フラグ/環境 | メモ |
|---|---|---|
| シミュレータのみビルド/テスト | -destination 'platform=iOS Simulator,name=…'、任意で CODE_SIGNING_ALLOWED=NO |
署名差分を抑える。Pods 利用時は必ず *.xcworkspace をビルド |
| SPM 解決の固定 | -disableAutomaticPackageResolution、-skipPackageUpdates |
依存解決後は毎回ネットワークに当てずにビルドを再現 |
| clone ディレクトリの明示 | -clonedSourcePackagesDirPath <path> |
グローバル SPM キャッシュから切り離し、マウントキャッシュと pruning がしやすい |
| 出力の分離 | -derivedDataPath <path> |
Pods と SPM の中間生成物をジョブ単位で保持し、並列ビルドと事後調査に有利 |
| パッケージの再解決が必要 | xcodebuild -resolvePackageDependencies を別ステップで実行してからビルド |
pod install と同一ステップに混ぜない方がログが読みやすい |
スクリプト化する場合は xcodebuild -version、swift --version、pod --version もログに残し、「コード変更なし・ビルドだけ変わった」回帰の二分探索をしやすくします。
FAQ
Pods と SPM の両方が変わった——どちらを先に?
通常は pod install で Xcode プロジェクト/workspace を再生成してから、SPM の解決またはビルド。両方がプロジェクトファイルを変える場合は Podfile.lock と Xcode のパッケージ解決状態を同一の変更管理プロセスに載せます。
Derived Data をキャッシュしているのに SPM が毎回 clone し直す
clone 先がキャッシュ外にある、または -derivedDataPath が毎ジョブで変わり環境が新規扱いになっている可能性があります。-clonedSourcePackagesDirPath を安定パスにし、ジョブごとに誤って消していないか確認してください。
専有 Mac のディスクが逼迫——先に何を消す?
古いジョブ別 Derived Data と SPM clone ツリーを優先的に整理し、その後にグローバル CocoaPods キャッシュの削減を検討。削除前に、並列ジョブがまだそのパスを参照していないことを確認します。
混合依存 CI を静かに回すなら Mac mini が向く理由
CocoaPods と SPM の併用は、Unix 的なファイル意味論とディスクスループットの安定に依存します。Apple Silicon 上の macOS は IO とメモリ帯域が素直で、「ローカルでは通るが CI だけ不安定」という環境由来のノイズを減らしやすいです。Mac mini クラスは待機時の消費電力がごく低く(目安で数ワット級)、常時稼働の 専有リモートビルドノード に向きます。Gatekeeper・SIP・FileVault により、汎用 Windows ワークステーションをビルドファームに流用する場合と比べて攻撃面も小さくなりやすいです。
本稿のキャッシュ設計と xcodebuild のマトリクスを、静音で 24 時間置けるハードウェアで回したい場合、現時点でコスト対安定性のバランスが良い入口のひとつが Mac mini M4 です。SSHMac で専有マシンを揃え、依存インストールとコンパイルを「退屈なくらい予測可能」に近づけてください。