
はじめに
こんにちは。開発部でiOSエンジニアをしている野口です。
ヘルシカのiOSアプリではXcode Cloudを使用して開発環境・本番環境への配布を行っています。本記事では、配布にかかっていた実行時間を約50%削減した方法を紹介します。
背景と課題
削減前のXcode Cloudの実行時間は約30分かかっていました。これを削減できれば、開発スピードの向上やQAから修正へのサイクルが回しやすくなり、品質の向上が期待できると考えました。
各ステップの実行時間はApp Store Connectのダッシュボードから確認できます。調査したところ、ci_post_clone.shの実行が全体の約62%を占めており、ここがボトルネックであることがわかりました。
| ステップ | 時間 | 割合 |
|---|---|---|
| Run ci_post_clone.sh script | 16分46.9秒 | 62.34% |
| Run xcodebuild archive | 6分17.9秒 | 23.39% |
| Resolve package dependencies | 2分2秒 | 7.55% |
| その他(環境設定・取得・Export・後処理など) | 1分48.5秒 | 6.72% |
| Build Archive 合計 | 26分55.3秒 | 100.00% |
| Prepare Build for App Store Connect | 44.8秒 | — |
| 総合計 | 約27分40秒 | — |
※主要なステップ以外は「その他」にまとめています。
原因の分析
ci_post_clone.shとは
ci_post_clone.shは、Xcode Cloudがリポジトリをクローンした直後に自動で実行されるシェルスクリプトです。ビルドに必要な追加ツールのインストールや設定ファイルの書き換えなど、ビルド前の準備処理を記述します。以下の画像のPost-cloneと記述されている箇所でci_post_clone.shが動きます。

引用元:
実行していた処理
ci_post_clone.shでは、以下の処理を行っていました。
# ci_post_clone.sh #!/bin/sh brew install mint mint bootstrap -m ../Mintfile --overwrite y
# Mintfile realm/SwiftLint@0.52.4 mono0926/LicensePlist@3.24.11 kiliankoe/swift-outdated@0.8.0 nicklockwood/SwiftFormat@0.53.3
Mintを使用して、以下のツールをインストールしていました。
- SwiftLint: コードの静的解析
- SwiftFormat: コードのフォーマット
- LicensePlist: ライセンスの管理
- swift-outdated: 依存関係の更新確認
改善のアプローチ
これらのツールはいずれも開発時に使用するものであり、Xcode Cloudでの配布時には実行する必要がありません。Xcode Cloudの役割はCDであり、配布作業のみ行えれば十分だからです。
そこで、「不要なツールのインストールをやめる」ことで実行時間を削減する方針としました。
キャッシュによる高速化を採用しなかった理由
「Mintのインストールをキャッシュすれば速くなるのでは?」と考えるかもしれませんが、Xcode Cloudのキャッシュ機能には制約があります。
GitHub ActionsやCircleCIなどのCI/CDツールでは、任意のパスやフォルダを指定してキャッシュできます。例えば~/.mintをキャッシュしておけば、2回目以降のインストールを高速化できます。
一方、Xcode Cloudのキャッシュ対象はDerivedData配下のみに限定されています。具体的にキャッシュされるのは以下の2つです。
- Xcodeのビルドキャッシュ(インクリメンタルビルド用の中間成果物)
- Swift Package Managerで取得・ビルドされたライブラリ
任意のパスを指定する機能は提供されていないため、Homebrew経由でインストールしたMintなどはキャッシュの対象外です。つまり、ci_post_clone.shでのインストール処理は毎回フルで実行されることになります。
To reduce the amount of time it takes to perform a build, Xcode Cloud stores each build's derived data and other cached information for reuse in a secure and private way.
— Xcode Cloud workflow reference | Apple Developer Documentation
このキャッシュの制約があるからこそ、キャッシュで高速化するのではなく、そもそも不要な処理をXcode Cloudから取り除くアプローチが有効になります。
具体的には、ci_post_clone.shを削除してXcode CloudでのMintインストール自体をやめ、各ツールの実行はGitHub Actionsやローカル環境に移行しました。
各ツールの対応内容
SwiftLint・SwiftFormat
ローカル環境でのビルド(Build Phase)とGitHub Actionsでのみ実行するようにしました。
GitHub Actionsの設定
# .github/workflows/ci.yml name: CI on: pull_request: branches: - develop jobs: swift_format: name: SwiftFormat runs-on: macos-latest steps: - uses: actions/checkout@v4 - name: Cache Mint packages uses: actions/cache@v4 with: path: ~/.mint key: ${{ runner.os }}-mint-${{ hashFiles('Mintfile') }} restore-keys: | ${{ runner.os }}-mint- - name: Install Mint run: brew install mint - name: Run SwiftFormat lint run: mint run swiftformat healthcare Packages --lint swift_lint: name: SwiftLint runs-on: macos-latest steps: - uses: actions/checkout@v4 - name: Cache Mint packages uses: actions/cache@v4 with: path: ~/.mint key: ${{ runner.os }}-mint-${{ hashFiles('Mintfile') }} restore-keys: | ${{ runner.os }}-mint- - name: Install Mint run: brew install mint - name: Run SwiftLint run: mint run swiftlint lint
ローカル環境(Xcode Build Phase)の設定
XcodeのRun Script(Build Phase)でSwiftFormatとSwiftLintを実行します。Run ScriptはXcode Cloud上のビルドでも実行されるため、環境変数CIがTRUEのときはスキップするようにしています(Xcode CloudではCI=TRUEが設定されます)。
if [ "$CI" = "TRUE" ]; then exit 0 fi if [ -d "/opt/homebrew/bin" ]; then export PATH="$PATH:/opt/homebrew/bin" fi mint run swiftformat healthcare Packages mint run swiftlint lint
LicensePlist
もともとBuild PhasesのRun Scriptでビルドのたびにライセンス情報を自動生成していたため、生成物をGit管理していませんでした。今回の対応で生成物をGit管理に含め、Build PhasesからRun Scriptを削除しました。パッケージを変更した際にはローカルでライセンス情報を再生成する運用としています。
swift-outdated
もともとXcode Cloudでは実行していなかったため、対応は不要でした。
結果
これらの対応により、Xcode Cloudの実行時間を約30分から約15分へ、約50%削減することができました。ビルドやパッケージ解決の設定を変えたわけではなく、ci_post_clone.shの処理を見直しただけでこれだけの効果が得られました。
まとめ
Xcode Cloudの実行時間を削減するために、CDとして不要なツールのインストール処理を見直しました。Xcode CloudのキャッシュはDerivedData配下のみという制約があるため、キャッシュで高速化するのではなく、そもそも不要な処理をXcode Cloudから取り除くアプローチを採用しています。各ツールの実行はGitHub Actionsやローカル環境に移行し、CI/CDの役割を明確に分離しました。
Xcode Cloudの実行時間に課題を感じている方の参考になれば幸いです。
