リンクをコピーしました

前の記事で EAS Cloud から TestFlight に上げる流れは整いましたが、EAS のビルド枠を気にする場面もあります。
そこで Xcode Cloud を試しました。Apple Developer Program に含まれる月 25 時間の無料枠があり、個人開発ではコスト面でありがたいですし、個人的にもこのフローを採用しております。

Expo アプリを Xcode Cloud で動かすには、ビルド環境に必要なものがないので、ci_post_clone.sh で準備する 必要があります。

手順は template-expo-build-cicd をベースに試した内容です。

サンプルリポジトリ

前の記事と同じテンプレートです。
eas.jsonapp.json の設定が入っているので、Bundle ID などを自分のものに差し替えて使います。

git clone https://github.com/testkun08080/template-expo-build-cicd.git
cd template-expo-build-cicd
npm ci

テンプレートは Node.js 20.x 前提です(eas.jsonbase.node20.18.0)。
Xcode Cloud の ci_post_clone.sh でも、入っていなければ Homebrew で Node 20 を入れます。

clone 後に差し替える項目

ファイル差し替える項目
app.jsonios.bundleIdentifier / extra.eas.projectId
ios/templateexpobuild.xcodeproj/project.pbxprojDEVELOPMENT_TEAM / PRODUCT_BUNDLE_IDENTIFIER

extra.eas.projectId は Xcode Cloud では直接は使いませんが、ローカルで expo prebuild するときに eas init 済みのプロジェクトのほうが楽です。
前の記事の「clone 後に差し替える項目」もあわせて確認してください。

EAS Cloud と Xcode Cloud の使い分け

前の記事で触れた EAS Cloud に対して、Xcode Cloud 視点で整理します。

EAS CloudXcode Cloud
Mac 必須不要不要(Apple 管理)
署名EAS リモート credentialsApple 自動(match 不要)
Expo prebuildEAS 内蔵ci_post_clone.sh で実行
主なコストEAS ビルド枠月 25h 無料枠
セットアップeas.json + eas loginios/ 部分コミット + CI スクリプト

普段は EAS Cloud無料分をテスト的にビルドするのに使用し、ios向けだけならXcode Cloudを使っています。

前の記事で済ませていること

Xcode Cloud に進む前に、次が終わっているとスムーズです。
EAS LocalEAS Cloudで一通りやっているので、ここでは要点だけ。

  • App Store Connect にアプリ登録済み(Bundle ID 確定)
  • app.jsonios.bundleIdentifiercom.testkun08080.template-expo-build のように確定している
  • TestFlight まで一度上げた経験がある(提出フローのイメージがついている)

Xcode Cloud では EAS や EXPO_TOKEN は不要 です。
代わりに ios/ の最小コミットと ci_post_clone.sh の用意が必要になります。

Xcode Cloud とは

Xcode Cloud は Apple が提供する CI/CD サービスで、App Store Connect や Xcode から設定できます。
Archive から TestFlight 配布までを Apple 側で完結できるので、EAS を使わない配布経路として選択肢になります。

ワークフローを作る前に、手元の Mac で一度 Archive を通しておく必要があります。
次のセクションで、その手順を写真つきでまとめます。

ios/ の部分コミット戦略

Managed Workflow の Expo アプリでは、通常 ios/ は gitignore します。
Xcode Cloud では Xcode プロジェクトと CI スクリプトだけ をコミットする「部分コミット」が現実的です。

テンプレートでは expo prebuild 後の構成を、だいたい次のように整理しています。

ios/
├── ci_scripts/
│   └── ci_post_clone.sh           # コミット
├── templateexpobuild.xcodeproj/   # pbxproj + scheme のみコミット
├── templateexpobuild.xcworkspace/ # コミット
└── (Podfile, Pods 等は gitignore → prebuild で生成)

ios/.gitignore の例:

# 生成物は無視。Xcode プロジェクトと ci_scripts だけ残す
*
!.gitignore
!templateexpobuild.xcodeproj/
!templateexpobuild.xcworkspace/
!ci_scripts/

templateexpobuild.xcodeproj/*
!templateexpobuild.xcodeproj/project.pbxproj
!templateexpobuild.xcodeproj/project.xcworkspace/
!templateexpobuild.xcodeproj/xcshareddata/

expo prebuild --clean のたびに Podfile や entitlements が再生成されるので、全部コミットするよりこの最小構成のほうが扱いやすいです。
scheme ファイルも xcshareddata/xcschemes/ に入っていることを確認して、明示的に git に含めてください。

ci_post_clone.sh

クローン直後に Node と native プロジェクトを用意します。
Xcode Cloud が認識するのは ios/ci_scripts/ci_post_clone.sh です。

テンプレートで実際に使っているのがこちらです(ci_post_clone.sh)。

#!/bin/sh
set -euo pipefail

cd "$CI_PRIMARY_REPOSITORY_PATH"

# Xcode Cloud images do not include Node.js by default.
if ! command -v node >/dev/null 2>&1; then
  brew install node@20
  brew link node@20 --force --overwrite
fi

npm ci
npx expo prebuild --platform ios

cd ios
pod install

やっていることはだいたい次のとおりです。

  1. CI_PRIMARY_REPOSITORY_PATH(Xcode Cloud が渡すリポジトリルート)へ移動
  2. Node がなければ Homebrew で node@20 を入れる
  3. npm ciexpo prebuildpod install

Flutter × Xcode Cloud の記事でも同じく CI_PRIMARY_REPOSITORY_PATH を使っています。

CocoaPods がイメージに無い場合は、Node の直後に brew install cocoapods を足すと安定しやすいです。
prebuild のキャッシュ不整合で何度も落ちるときは、npx expo prebuild --platform ios --non-interactive --cleanpod install --repo-update に切り替えてもよいです。

NODE_BINARY が必要なとき

React Native のビルドフェーズで Node が見つからないエラーが出る場合は、ci_post_clone.sh の末尾に次を追加します。

NODE_BINARY="$(command -v node)"
echo "export NODE_BINARY=${NODE_BINARY}" > ios/.xcode.env.local

ios/.xcode.env.local は通常 gitignore 対象なので、毎ビルドで生成する形にします。

ローカルで Archive を作る

Xcode Cloud のワークフローを設定する前に、手元の Mac で一度 Archive を通しておきます。
expo prebuildpod install が済んだあと、ios/templateexpobuild.xcworkspace を Xcode で開いてください。

ワークスペースを開いて署名を確認する

左のナビゲータでプロジェクトを選び、Signing & Capabilities タブを開きます。
Archive 用の Release 構成も見ておくと安心です。

次の項目が意図どおりか確認します。

  • Automatically manage signing がオンになっている
  • Team が自分の Apple Developer アカウントになっている
  • Bundle Identifiercom.testkun08080.template-expo-build で、app.json と一致している
Signing & Capabilities(Release)の確認

project.pbxproj を直接編集している場合も、Xcode 上の表示と食い違っていないか見ておくと安心です。
部分コミット戦略では、だいたい次の値を入れています。

CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = XXXXXXXXXX;
PRODUCT_BUNDLE_IDENTIFIER = com.testkun08080.template-expo-build;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;

Xcode Cloud は App Store Connect と連携しているため、証明書の手動管理や fastlane match は不要です。
EAS Cloud の記事で整えた Bundle ID をそのまま使えます。

Archive を作成する

デスティネーションは実機または Any iOS Device を選び、メニューから Product → Archive を実行します。

Product → Archive

Archive 完了後の Organizer

ビルドが通ると Organizer が開き、アーカイブが一覧に載ります。
テンプレートでは templateexpobuild という名前で、Version 1.0.0 (1) のように表示されます。

Archive 完了後の Organizer

Identifier に com.testkun08080.template-expo-build、Team に自分のアカウント名が出ていれば、署名まわりはだいたい問題なさそうです。
TestFlight に手動で上げる場合は Distribute App から提出できますが、Xcode Cloud を使う場合は次のワークフロー設定に進みます。

ここまで通れば、Integrate → Create Workflow… のメニューも有効になることが多いです。

Xcode Cloud ワークフローを作る

Archive が通ったら、Xcode Cloud のワークフローを作成します。
メニューは Integrate → Create Workflow… から開けます(Product → Xcode Cloud からでも同じです)。

Integrate → Create Workflow...

プロダクトを選ぶ

ワークスペース内のアプリ一覧が出るので、templateexpobuild を選びます。
Team 列に自分のアカウントが入っているかもここで確認できます。

Select a Product

ソースコードへのアクセスを許可する

GitHub リポジトリへのアクセスを求められたら、対象リポジトリ(testkun08080/template-expo-build-cicd など)にチェックを入れて進めます。

Grant Access to Your Source Code

初回は GitHub アカウントと Apple ID の連携が必要なことがあります。
Xcode の Settings → Accounts で GitHub が登録されているかも見ておくとよいです。

App Store Connect のアプリと紐づける

App Store Connect 上のアプリ名と Bundle ID が表示されます。
com.testkun08080.template-expo-build になっているか確認して Next を押します。

Confirm App on App Store Connect

ワークフロー内容を確認する

初期設定では、だいたい次のような内容になります。

項目設定値
開始条件Branch Changes(main
環境Latest Release(Xcode の最新版)
アクションArchive(iOS)
ポストアクションTestFlight なし(初回はこうなることも)
Review Workflow

スキームは templateexpobuild、ワークスペースは templateexpobuild.xcworkspace を指定します。
初回は Post-Actions に TestFlight が載っていないこともあります(スクショ参照)。
実際にこの時点でワークフローを編集してもTestFlightの項目が出ない時がありました

初回ビルドを開始する

設定が終わると Start Build の画面が出ます。
main ブランチを選んで Start Build を押すと、Xcode Cloud 上で最初のビルドが走ります。

Start Build

この時スタートしても閉じないので(僕の環境では)、、、
ボタンを押したら、以下の項目でビルドの動作を確認してから、閉じてしまってください。

ビルドの動作を確認する

ビルドが始まると、App Store Connect の Xcode Cloud タブでも進捗を追えます。
最初はステータスがくるくる回っている状態になります。

App Store Connect — ビルド進行中

完了すると緑のチェックマークが付き、ビルド番号(12…)が並びます。
警告数は出ますが、Archive まで通っていれば TestFlight 配布の対象になります。

App Store Connect — ビルド完了

TestFlight タブ側でもビルドが載るまで少しラグがあることがあります。
Xcode Cloud のログで ci_post_clone.sh が通っているかもあわせて確認すると安心です。

TestFlight 配布を有効にする

1回ビルドが通ったあと、ワークフローの Archive - iOS アクションを開き、Distribution PreparationTestFlight(Internal Testing Only) を選びます。

Archive アクションで TestFlight を選ぶ

初回のワークフロー作成ウィザードでは TestFlight の項目が出てこないことがあります。
そのときは次の「TestFlight がワークフロー作成時に見えない」を試してから、Edit Workflow… で設定し直してください。

よくあるトラブル

TestFlight がワークフロー作成時に見えない

初回セットアップのウィザードで TestFlight の選択肢が出てこないことがあります。
僕の環境では、次の順で解消しました。

  1. 一度 Start Build で Xcode Cloud 上のビルドが走るのを確認する(App Store Connect の Xcode Cloud タブで OK)
  2. Xcode を再起動 する
  3. Integrate → Manage Workflows…(またはワークフロー編集)を開き直す
  4. Archive - iOSDistribution PreparationTestFlight が出るか確認する

うまくいかないときは、先に Archive だけのワークフローで1回通してから TestFlight を足す形でも進められます。

prebuild 後の Pod エラー

pod install --repo-updateci_post_clone.sh に入れておくと、キャッシュ不整合を減らせます。
entitlements(App Groups など)は app.json に書き、prebuild で反映させます。

スキームが見つからない

expo prebuild 後のプロジェクト名と、Xcode Cloud で選ぶスキーム名が一致しているか確認してください。
部分コミット戦略では、templateexpobuild.xcodeproj/xcshareddata/xcschemes/ 以下の scheme ファイルを明示的に git に含める必要があります。

ビルド時間

毎回 npm ciexpo prebuild --clean が走るため、初回に近い時間がかかることがあります。
キャッシュは Xcode Cloud 側に任せつつ、スクリプトは必要最小限に保つのがよいです。

EAS は通るが Xcode Cloud だけ落ちる

EAS Cloudが通るなら Bundle ID や entitlements の設定自体は正しいことが多いです。
Xcode Cloud 側は ci_post_clone.sh のログ、NODE_BINARYpod install の成否を順に見るのが良いと思います。

まとめ

  • Xcode Cloud では ci_post_clone.sh で Node + expo prebuild + pod install が必須
  • ios/ は Xcode プロジェクトと ci_scripts だけコミットする部分戦略が扱いやすい
  • ローカルで Archive を一度通してからワークフローを作ると設定がスムーズ
  • TestFlight が見えないときは、Xcode Cloud で1回ビルド → Xcode 再起動を試す
  • コスト面では月 25 時間無料枠が個人開発向き

次の記事では、前回の記事でやっていたビルドとテストフライトまでの提出フローを GitHub Actions 上で行いたいと思います。
fastlane match 版と、EAS Cloudを使用したものです。

Expo × GitHub Actions で TestFlight 自動デプロイ(fastlane / EAS)

関連リンク