Mac なしで iPhone アプリを build する方法
00要約Overview
Tools
- Apple Developer ProgramApp Store 申請99 ドル/年
- EAS buildクラウド macOS で buildFree
- GitHub Codespaces初回の証明書操作Free
- PC (何でも可)初回のみブラウザ手持ち
01物語Story
Situation
1.1.1 の開発ループで、Expo Go による動作確認と修正のサイクルは回るようになった。開発中は build なしで進む。
Complication
ただし、App Store に出すとなると話が変わる。Apple に申請するには、アプリを build して submit しなければならない。そして iPhone アプリの build には Mac が必要 — これは Apple のエコシステム上、避けて通れない事実である。
Question
build には Mac が必要。Mac を持たずに、どうやって App Store まで届けるか?
02解決Solution
Criteria
- Mac を所有していなくても成立すること (買わない・借りない・置かない)
Answer
# 初回のみ (証明書作成) PC ブラウザ → GitHub → GitHub Codespaces → EAS (build) # 2 回目以降 (すべて iPhone から) iPhone → GitHub → GitHub Actions → EAS (build) iPhone → GitHub → GitHub Actions → EAS (submit) → App Store Connect → TestFlight → iPhone
Mac は必要である。ただし、クラウドの中にあればいい。
EAS (Expo Application Services) の build サービスは、クラウド側の macOS 環境で iPhone アプリを build してくれる。手元に必要なのは、コマンドを投げる端末だけ。それは iPhone で足りる。
Reason
初回だけ PC ブラウザを使ったのは、対話操作のためである。 初回 build では署名用の証明書 (App Store 用の credentials) を作る必要があり、その過程で Y/N を聞かれる対話式の入力が発生する。基本は Y を答えるだけなのだが、これを GitHub Actions で自動化しようとすると、応答のタイミング合わせのような面倒なコーディングが必要になる。年に数回あるかどうかの操作を自動化する価値はないと判断して、初回だけ手動にした。
その手動操作も、Mac は要らない。GitHub Codespaces — GitHub がブラウザ上に用意してくれる VS Code 環境 — を開けば、そこからコマンドを叩ける。個人アカウントの無料枠 (120 コア時間/月) で十分足りる。理屈の上ではスマホのブラウザでも可能だが、ターミナル操作にはさすがに画面が小さすぎたので、ここだけ PC のブラウザを使った。繰り返すが、PC であって Mac である必要はない。
2 回目以降は対話が不要になるので、iPhone から GitHub Actions の workflow を実行するだけで build が走る。build が成功したら、同じく Actions から submit すると、App Store Connect に上がり、TestFlight で実機に降ってくる。申請の直前まで、すべて iPhone で完結する。
Options
- Xcode Cloud — Apple 純正の CI/CD。ただし初回のセットアップに Xcode つまり Mac が必要で、「Mac を所有しない」という Criteria と矛盾するため見送り。アプリを EXPO で実装している以上、EAS との親和性が高いことも決め手になった。
- GitHub Actions の macOS ランナー — Actions 上で直接 macOS を立ち上げて build する手もある。ただし macOS ランナーの利用は Linux の 10 倍の分数消費で課金される。無料枠 2,000 分/月がみるみる溶けるので、チャレンジしていない。
03結果Result
Good
build も submit も EAS に集約されたので、管理が一元化された。EXPO での開発 (1.1.1) と地続きのまま、申請直前の工程までスマホで届く。「Mac が要る」という iPhone アプリ開発最大の参入障壁が、実は「Mac を持つ必要」ではなかったと分かったのは収穫だった。
Bad
無料枠の組み合わせには制約がある。執筆時点の値で:
| サービス | 無料枠 | 備考 |
|---|---|---|
| EAS build | iOS 15 回/月 (+ Android 15 回/月) | build エラーでも回数を消費する |
| GitHub Actions | 2,000 分/月 | Free プランの private リポジトリの場合 |
通常の開発でこの上限を超えることはまずない。ただし、ネイティブまわりの動作確認でバグが出て build を繰り返すと、一気に持っていかれる。特に痛いのが、build エラーでも回数がカウントされること。設定ミスで 3 連続エラー、などとやると月の 2 割が消える。
Follow-up
EAS の無料枠 build は優先度が低く、キュー待ちが異様に長いことがある。気長に待てばいいのだが、ここに落とし穴がある。Actions 上で build の完了を待っていると、その待ち時間が Actions の 2,000 分/月からも消費されていく。待っても待たなくても build の結果は同じなので、eas build には --no-wait を付けて、trigger したら workflow を即終了させるのが正解である。
その代わり build と submit が 1 本の workflow で繋がらなくなるので、EAS のダッシュボードで build の成功を確認してから、Actions で submit を実行する、という 2 段運用にしている。submit 側も同様に --no-wait が使える。
ここまでで「作って届ける」の縦の線が通った。まずはこれでリリースできる。リリースが済んだら、OTA を安全に運用する設定 — 開発用と公開用の配信経路を分ける 1.1.3 EAS Update の安全な運用 — を入れて、改修時の誤配信を防いでおきたい。次は横に広げる話、1.2 多言語対応 に続く。
手動で GitHub Actions を起動しているのだが (Claude のクレジットがもったいないので)、Claude に Actions の起動もお願いしてしまった方が良い気がしている。気がしていると言うか最近そうしている。手動で Actions を起動すると、Claude 上にやったことが残らないため、次に Claude と会話していると「まだリリースしてないから大丈夫ですよね」的な判断をされることがある。Claude の進捗ファイルにアプリの status を記録させるためには、クレジットが多少もったいなくても Claude 経由で指示を出した方がズレることがない。(build した後に「build したよ」と教えてあげれば良いんだけど。)
■ 再現できる方法
この構成を自分で再現したい人向けのメモ。読み物ではないので、指示文は数行だけ表示してスクロールにしてある。
- Apple Developer Program に登録 (年 $99、developer.apple.com/programs)
- App Store Connect で App ID を発行 (アプリ枠を作る → ascAppId (数値) を控える)
- App Store Connect API Key を発行 (Users and Access → Integrations → App Store Connect API)。
.p8ファイル・Key ID・Issuer ID の 3 点を控える (.p8 は再ダウンロード不可なので必ず保存) - GitHub Secrets に 3 つ追加:
APPLE_API_KEY(.p8 の全文) /APPLE_API_KEY_ID/APPLE_API_KEY_ISSUER_ID
してから、下記を貼り付けで実行。
このアプリを iOS でビルドして App Store Connect に提出できるようにしたいです。
ビルドは EAS Build、提出は EAS Submit を使い、2 回目以降は GitHub Actions の
手動トリガ(workflow_dispatch)で回せる構成にしてください。
【作ってほしいもの】
1. app.json にビルドに必要な項目を追加
- ios.bundleIdentifier(私が Apple Developer で登録した値。後で私が差し替える
ので、いったんプレースホルダで置いてコメントで指示してください)
- ios.supportsTablet / requireFullScreen など基本項目
- version(1.0.0 スタート。ビルドのたびに上げる方針をコメントで明記)
2. eas.json
- production プロファイル
- submit.production.ios.ascAppId は私が後で入れるのでプレースホルダに
3. .github/workflows/eas-build-ios.yml(workflow_dispatch)
- buildNumber を YYYYMMDDHHMM で自動採番して app.json に書き込む
- eas build --profile production --platform ios --non-interactive --no-wait
- Apple 系の認証は Secrets(APPLE_API_KEY / APPLE_API_KEY_ID /
APPLE_API_KEY_ISSUER_ID)を env で渡す
4. .github/workflows/eas-submit-ios.yml(workflow_dispatch)
- Secrets の APPLE_API_KEY を一時ファイル(.p8)に書き出して eas.json に注入
- eas submit --platform ios --profile production --latest --non-interactive
【重要な注意をコメントで残してほしい】
- 初回ビルドだけは対話シェルで手動実行が必要(証明書発行の対話プロンプトが
出るため、CI では応答できない)。2 回目以降は CI の非対話モードで通せる、と。
- ビルドのたびに app.json の version を上げる(Apple の重複バージョンエラー回避)
作り終えたら、私が Codespaces で初回ビルドを通すための具体コマンドを
手順で教えてください。
下記を参考にして教えて。
(これは Claude Code への指示ではなく、自分が Codespaces のターミナルで叩くコマンド)
# 1. リポジトリの Codespace を開く(GitHub の Code → Codespaces → Create)
# 2. EAS CLI を入れて Expo にログイン
npm install -g eas-cli
eas login # Expo のユーザー名 / パスワードを対話入力
# 3. 依存を入れる
npm install --legacy-peer-deps
# 4. 初回ビルド(ここで証明書の対話プロンプトに「Yes」で答えていく)
eas build --platform ios --profile production
# → 証明書は EAS 側に保存されるので、次回以降は CI から非対話で通せる
自分でコマンドを叩くときも、PC のブラウザをスマホで写真を撮っておいて「何すれば良いの?」と Claude に聞くと全部教えてくれる。基本 Y を押すだけで良い。App Store Connect (ブラウザ) でアプリの ID を作らないといけないのだが、それも「Claude で必要な項目足りてる?」と聞くと教えてくれる。