Xcodeを開かずにMac・iOSアプリをビルドして配布する
(scottwillsey.com)- 初期のAppleアカウント・証明書設定を終えれば、Xcode.app内のCLIツールだけでMacとiOSアプリのビルド、署名、配布を自動化できる
project.ymlからXcodeGenで.xcodeprojを再生成し、xcodebuild、notarytool、stapler、devicectlでプロジェクト生成からMacの公証、iPhoneへのインストールまで処理する- Mac配布は
scripts/release.shひとつでアーカイブ、Developer ID署名、公証、チケットのステープル、Gatekeeper検証、/Applicationsへのインストールを順に実行し、失敗時は即座に中断する - 高速なコンパイルとテストには
CODE_SIGNING_ALLOWED=NOを使えるが、この一時署名ビルドはGatekeeperを通過できず、iCloud KVSやApp Group権限も実際のチームIDに結び付かない - 署名用秘密鍵と公証用パスワードはログインキーチェーンに保管し、
CLAUDE.mdまたはAGENTS.mdにコマンドと配布ルールを記録しておくと、Claude Codeのようなエージェントがワークフロー全体を繰り返し実行できる
Xcode GUIなしで処理できる範囲
- Xcode自体はインストールする必要があるが、起動する必要はなく、
xcodebuild、notarytool、stapler、devicectlはいずれもXcode.app内のシェルツールとして単独で実行される - Apple IDログイン、Developer ID証明書の作成、公証用パスワードの保存といった初期設定にはGUIまたは対話型ターミナルが必要
- 初期設定を終えれば、プロジェクト生成、ビルド、アーカイブ、署名済みアプリのエクスポート、公証、検証、インストール、実機iPhoneへの配布までマウスなしで処理できる
- 設定やスクリプト作成で行き詰まった場合は、Claude Codeまたは他のLLMコーディングツールに作業要件とプロジェクト別の値を渡してワークフローを作成できる
Xcodeツールチェーン全体を選択する
- まず
xcode-select -pの結果が/Applications/Xcode.app/Contents/Developerか確認する必要がある - スタンドアロンのCommand Line Toolsが選択されている場合は、次のコマンドでXcodeツールチェーン全体を指定する
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
xcode-select --installでインストールされる/Library/Developer/CommandLineToolsにはclangとgitは含まれるが、iOS SDK、notarytool、devicectlなどアプリ開発ツール一式は含まれない- Xcodeがすでにインストールされているなら、別途Command Line Toolsパッケージは不要
- ライセンス同意と追加コンポーネントのインストールもCLIで処理できる
sudo xcodebuild -license accept
sudo xcodebuild -runFirstLaunch
XcodeGenでプロジェクトを再現する
- XcodeGenはXcodeプロジェクト設定をYAMLで管理し、ビルドのたびに
.xcodeprojを再生成する - GitHubからダウンロードするか、Homebrewでインストールできる
brew install xcodegen
.xcodeprojはmacOSではファイルのように見えるフォルダであり、Xcodeが内部ファイルや参照を継続的に変更するため、Gitリポジトリで競合や不要な変更が発生することがあるproject.ymlだけをGitにコミットし、生成された.xcodeprojは無視すれば、プロジェクト設定を再現可能な状態に保てる
xcodegen generate
Apple開発者アカウントと証明書の設定
- Xcodeの
Settings → AccountsでApple IDを追加する必要がある - アプリの配布と公証には有料のApple Developerアカウントが必要
Settings → Accounts → Apple ID → Manage Certificates… → + → Developer ID Applicationで配布用証明書を作成する- Apple DevelopmentとDeveloper ID Applicationは用途が異なる
- Apple Developmentは自分のデバイスで実行したり、iPhoneに転送してローカルデバッグしたりする際に使う
- Developer ID Applicationは、他人のMacでもGatekeeperを通過する公証済み
.appを配布する際に必要
- Developer ID Application証明書を作成すると、証明書と対応する秘密鍵がログインキーチェーンにインストールされる
- 実際の署名には秘密鍵が使われ、再ダウンロードはできないため、削除せずキーチェーンをバックアップする必要がある
公証資格情報をキーチェーンに保存する
- Macアプリの公証は、署名済みアプリをAppleにアップロードしてマルウェア検査を受けるプロセス
notarytoolはキーチェーンに保存されたプロファイルで認証し、プロファイルを初めて作成するときはアプリ用パスワードを対話的に入力する必要がある
xcrun notarytool store-credentials App-Name \
--apple-id "you@example.com" \
--team-id YOUR-TEAM-ID
- 公証プロファイルは他のアプリのものを流用せず、アプリ名に合わせて作成することで、別環境でひそかに失敗する状況を避けられる
- アプリ用パスワードはApple IDのパスワードとは別物で、Apple IDサイトの
Sign-In & Security → App-Specific Passwordsで作成する - Apple IDのパスワードを変更すると、アプリ用パスワードが別途警告なしに期限切れになることがある
- 公証中に
401 invalid credentialsが発生したら、設定全体を疑う前にアプリ用パスワードの期限切れをまず確認する必要がある
- 公証中に
- プロファイルが保存されているかは次のコマンドで検証する
xcrun notarytool history --keychain-profile App-Name
- アプリ用パスワードをClaude Codeがアクセスできる1Passwordの保管庫に保存すれば、新しいアプリの公証プロファイル作成もエージェントに任せられる
--passwordでパスワード入力まで自動化できるが、シェル履歴にパスワードが残る可能性があるため、一度手動で入力してからキーチェーンや1Passwordに保管する方式を使う
ローカル署名設定と秘密情報の分離
- 実際の署名に必要なチームIDとバンドル接頭辞は
Local.xcconfigに保存する
cp Local.xcconfig.example Local.xcconfig
BUNDLE_PREFIX = your.real.prefix
DEVELOPMENT_TEAM = YOUR-TEAM-ID
Local.xcconfigは.gitignoreに追加し、プロジェクト別のローカル設定がリポジトリに入らないようにする- 署名用秘密鍵はログインキーチェーンに、公証用アプリ用パスワードは
notarytoolのキーチェーンプロファイルに残るため、秘密情報がGitに記録されない
release.shでMac配布を自動化する
- リポジトリの
scripts/release.shが、Macアプリの配布パイプライン全体を1つのコマンドで実行する
./scripts/release.sh
-
スクリプトは次の順序で動作する
xcodegenと公証プロファイルが存在するか事前確認xcodegen generateでプロジェクトを再生成xcodebuild archiveでReleaseアーカイブを作成ExportOptions.plistとxcodebuild -exportArchiveでDeveloper ID署名済みアプリをエクスポート- アプリをZIPにまとめ、
notarytool submit --waitでAppleに提出 stapler stapleで公証チケットを添付spctlでGatekeeperによる許可可否を検証- 既存プロセスを終了し、アプリを
/Applicationsにコピー lsregisterでインストール済みアプリを登録- インストール済みバンドルのチケットとGatekeeper状態を再検証
-
モデルが一般的な配布手順を知っていても、スキーム名、チームID、公証プロファイル名、インストール先のようなプロジェクト固有情報はユーザーが提供する必要がある
-
最初のスクリプトが失敗した場合は、実行結果を基に修正する反復プロセスが必要だが、ワークフローが安定した後は追加修正なしで配布コマンドだけを実行できる
-
失敗と不完全な配布を防ぐ
set -euo pipefailはコマンドが1つでも失敗するとスクリプトを即座に中断し、不完全な状態を成功として扱わないようにするcd "$(dirname "$0")/.."はスクリプトをどのディレクトリから呼び出してもリポジトリルートへ移動させる- 事前検査ブロックは時間のかかるアーカイブの前に
xcodegenと公証プロファイルを確認する - エクスポートしたアプリだけでなく
/Applicationsにコピーされたアプリも再検査し、コピー過程でバンドルが破損する場合を検出する - 別の公証プロファイルが必要なら環境変数で上書きできる
TZED_NOTARY_PROFILE=<name> ./scripts/release.sh
エージェントに配布ルールを伝える
release.shが1つのコマンドで配布を実行するなら、CLAUDE.mdやAGENTS.mdは、エージェントがセッションごとに別途指示されなくてもそのコマンドを使うようにする- 文書にはプロジェクト再生成、単体テスト、高速macOSビルド、実際の配布コマンドを記録する
xcodegen generate
swift test
xcodebuild -project YOUR-APP-NAME.xcodeproj \
-scheme YOUR-APP-NAME-macOS \
-destination 'platform=macOS' \
CODE_SIGNING_ALLOWED=NO build
./scripts/release.sh
- 2つのビルド経路の違いも文書に明記する必要がある
CODE_SIGNING_ALLOWED=NOビルドはCIと高速なローカル確認に適している- 実際のメニューバーアプリ配布、隔離属性の通過、iCloud同期には、Developer ID署名と公証を実行する
release.shが必要
- あるアプリで作った方式を別のリポジトリで参照するよう指示すれば、Claude Codeが以後のアプリにも同じ設定を複製できる
高速な無署名ビルドとテスト
- Swift Package Managerベースの単体テストはXcodeビルドなしで実行する
swift test
- macOSアプリの高速なコンパイル確認は、署名をオフにした状態で行う
xcodebuild -project TZed.xcodeproj \
-scheme TZed-macOS \
-destination 'platform=macOS' \
CODE_SIGNING_ALLOWED=NO build
- iOSアプリとウィジェット拡張のシミュレータービルドも同じ方法で実行できる
xcodebuild -project TZed.xcodeproj \
-scheme TZed-iOS \
-destination 'generic/platform=iOS Simulator' \
CODE_SIGNING_ALLOWED=NO build
CODE_SIGNING_ALLOWED=NOで作成した一時署名ビルドは、コンパイルとシミュレーター実行に使える- このビルドはGatekeeperに拒否され、チーム接頭辞がないためiCloud KVSやApp Groupなどの権限が結び付かず、配布用には使えない
実機iPhoneへヘッドレス配布する
- iOSデバイスへの配布にはMacアプリと同じ公証ステップはなく、
xcodebuildとdevicectlを使う - 実機用アプリはApple Development証明書と開発用プロビジョニングプロファイルでビルドし署名する
xcodebuild -project TZed.xcodeproj \
-scheme TZed-iOS \
-destination 'generic/platform=iOS' \
-allowProvisioningUpdates \
-derivedDataPath build/ios \
archive -archivePath build/TZed-iOS.xcarchive
- 生成された
.appは、接続されたデバイスのUDIDを指定してインストールする
xcrun devicectl device install app \
--device <DEVICE-UDID> build/ios/…/TZed.app
devicectl list devicesは、接続されペアリング済みのデバイスとUDIDを表示する-allowProvisioningUpdatesを指定すると、必要な開発用プロビジョニングプロファイルを取得する
GUIなしでコード署名が機能する仕組み
-
Developer ID Application証明書を作成すると、Appleは証明書を発行し、Macは対応する秘密鍵を生成して、どちらもログインキーチェーンに保存する
-
xcodebuildが呼び出すcodesignは秘密鍵でバイナリに署名し、Appleルート証明書につながる証明書をアプリに含めて、署名者を検証できるようにする -
signingStyle: automaticを使うと、xcodebuildがチームIDに合う証明書を選択し、必要なプロビジョニングプロファイルをAppleから取得する -
したがって、プロビジョニングプロファイルをリポジトリにコミットする必要はない
-
権限、公証、ステープル
- 各ターゲットの
.entitlementsファイルには、サンドボックス、ネットワーククライアント、iCloud KVS、App Groupなどの権限が入っている - 権限は実際のチームIDで署名するときに結び付くため、一時署名ビルドではiCloudとApp Groupは正常に動作しない
- 署名と公証は別のステップ
- 署名は誰がアプリを作ったかを証明する
- 公証はAppleが署名済みアプリをマルウェア検査した後、チケットを発行する手続き
- ステープルはそのチケットをアプリに添付し、オフラインでもGatekeeperが信頼できるようにする
- 隠れたUIを使うメニューバーアプリである
LSUIElementアプリでは、公証がXProtectの検出を避けるために必要
- 各ターゲットの
-
手動検証コマンド
codesign -dv --verbose=4 /Applications/TZed.app spctl -a -vvv -t exec /Applications/TZed.app stapler validate /Applications/TZed.appcodesignは署名者と証明書を確認するspctlはGatekeeperが実行を許可するか検査するstapler validateは公証チケットが添付されているか検証する
エージェントが実行する標準ツール
- 別途ビルド用MCPサーバーや専用プラグインは不要で、Claude Codeは非対話型シェルで標準CLIを実行する
- 使用するツールは
xcodebuild、xcrun notarytool、xcrun stapler、spctl、codesign、devicectl、xcodegen、swift CLAUDE.mdは公証プロファイルの命名規則、高速ビルドと実際の配布の違い、配布時にはrelease.shを使うべきというルールをエージェントに伝える- 最初の
notarytool store-credentialsだけを対話型ステップとして残し、その後の過程はすべて自動化できる
Xcode GUI作業をCLIに置き換える
- Xcodeが管理する
.xcodeproj生成はproject.ymlとxcodegen generateで置き換える ⌘BまたはRunボタンはxcodebuild … buildに対応するProduct → Archiveはxcodebuild … archiveで処理する- Organizerのアプリ配布は
xcodebuild -exportArchiveで代替できる - Organizerアップロードは
xcrun notarytool submit --waitで行う - 自動チケット添付には
xcrun stapler stapleを使う /Applicationsへドラッグ&ドロップするインストール手順はcp -Rとlsregisterで処理する- 実機iPhoneでのRun動作は
xcodebuild archiveとdevicectl device installで置き換える - XcodeとXcodeGenのインストール、最初の資格情報設定、
release.shとCLAUDE.mdの作成まで終えれば、以後の新規ビルド配布は一文または1コマンドで実行できる
1件のコメント
Hacker Newsの意見
サンドボックスでは解決できないことが多いため、AIエージェントをMac上で直接実行しているが、xAIがSSHキーまで含むホームディレクトリをアップロードした事件を見て、この選択を真剣に見直すようになった
マシン自体が壊れることはそれほど心配していない。たいていはバックアップしているかGitにコミットしてあり、数時間あれば復旧できるからだ。だが、
.sshディレクトリがAIエージェントに送信されるのは絶対に避けたいので、プロンプトで防ごうとするのではなく、システムレベルで遮断すべきだ。別ユーザーを作成し、ホームディレクトリに700権限を適用する方法を検討している。セキュリティは1990年代に後退したようだが、その代償として前例のない速さで仕事を終えられるという二面性がある。自分もこの危険なやり方を使っているので、他人を非難するつもりはないパッケージ化された解決策として、オープンソースのTart https://tart.run/ や VirtualBuddy https://github.com/insidegui/VirtualBuddy を使えるし、OS API https://developer.apple.com/documentation/virtualization をラップしたツールを自分でバイブコーディングしてもよい。最近は Appleコンテナ https://github.com/apple/container も登場した
agentという別ユーザーで実行するラッパーツールを作っている。1年前はOAuthとキーチェーンの問題で失敗したが、最近はCLAUDE_CODE_OAUTH_TOKENを使えるとClaudeから案内され、実装に成功したACLで権限を設定する補助ツールが必要だが、現在はmacOSで動作する。リスクは標準のClaudeサンドボックスと完全なコンテナ・マシン分離の中間くらいだ。Claude自体のサンドボックスが改善されるにつれて、この二重防御プロジェクトの投資対効果を疑っていたが、ホームディレクトリ流出の可能性を見ると、続ける理由は十分にある。まだオープンソースとして公開していないが、興味のある人とは話すつもりだ
興味本位でLinuxだけを使ってiOSアプリをビルドしてテストしているが、驚くほど簡単で https://github.com/xtool-org/xtool がとてもうまく動く
TestFlightやApp Storeに上げる必要がなく、LinuxでもUSB経由でiPhoneに直接インストールできる。自信がないなら、コーディングエージェントにHello WorldのiOSアプリを作ってアップロードしてくれと頼めば、簡単に始められる
私のオープンソースプロジェクト Axiom は、コーディング実行ツールがAppleオペレーティングシステム向けの開発作業を効果的に行えるよう支援する
さまざまな技術やエージェントに加え、LLM向けツールである
xclog、xcprof、xcsym、xcuiも含んでいる。これらは必要な機能をトークン効率よく公開し、Axiom外の技術やエージェントにも有用だ。Axiom: https://charleswiltgen.github.io/Axiom/、CLIツール: https://charleswiltgen.github.io/Axiom/tools/「Claude Codeに、Xcodeを開かずにアーカイブ、Developer ID署名、公証、ステープリング、
/Applicationsへのインストールまで行い、どの段階でも失敗したら明確に停止するスクリプトを作らせた」という文を読むと妙な気分になる。いま読んでいるこの文章自体も、Claudeが私たちに語りかけているように感じるからだ記事のほぼすべての段階で、「Claude Codeや好みのLLMコーディングツールにこの記事を見せて、あとは任せればいい」「分からなければLLMに聞いて設定を手伝ってもらえ」「LLMを使う目的は、やりたくない手作業を避けることだ」「ワークフローを作ってもらえ」といった、LLMにもう一度聞けという案内が繰り返されるのも奇妙に感じる
1年以上エージェントとしてMacとiOSアプリを開発してきた立場からすると、この記事は良くない助言だと思う。特に優れた Xcode MCPはXcodeが起動中である必要がある
MCPツールを使えば、
xcodebuildよりはるかに高速かつ簡潔にさまざまな機能へアクセスでき、Xcodeを閉じると不可能な#Previewの生成とレンダリングも可能だ。Xcode 27のMCPと内蔵エージェントは、DeviceHubを通じてシミュレーターも非常によく制御する。DeviceHubはSimulator.appを置き換えるもので、axeのようなツールでアクセシビリティAPIを裏技的に操作しなくても、エージェントが理解して扱えるようだXcode MCPが完璧というわけではない。エージェントを実行するたびに権限ダイアログが出る問題は、Keyboard Maestroで自動承認することで回避した。それでもXcode 27は Xcodeバイブコーディングの使い勝手 を大きく改善した。今ではMCPを個別に扱わず、Xcode自体のエージェントUIでCodexを動かしているが、この実行環境のほうがモデルをうまく誘導し、利用可能なツールを活用させてくれる。結果として、最も重要なより速いフィードバックと、より堅牢な検証を得られる
Xcode-mcp-proxyがある。macOSアプリのテストには Peekaboo を使っているのか、それとも通常のスクリーンショット自動化を使っているのか気になるiOSとAndroidのIDEをできるだけ触りたくないなら、React NativeとExpo も良い選択だ
XcodeとAndroid Studioはデバイスシミュレーターをインストールするときだけ使い、ストア配布まで残りはExpoのツールチェーンが処理する。XcodeをインストールできないWindowsでも、Expoの管理型アプリを使えばiOSアプリをiPhoneで直接実行できる。RNとExpoベースの出発点としては、よくメンテナンスされている Ignite が素晴らしい: https://github.com/infinitered/ignite
厳密に言えば、依然として Xcodeを使っている のだ。Xcodeは概して安定した複数のシステムレベルUNIXユーティリティやアプリを包んだ、非常にバグの多いGUIだ
CLIでアプリをリリースするやり方は昔からの慣行で、少なくとも自分が最初のXcodeアプリをリリースした2012年から可能だった
少なくとも一部のデバッグでは、誰もが Xcodeを使わざるを得ない
ビルドはエージェントがすでにコマンドラインで大半を処理でき、状況ごとに明示的な指示を与えればより速くビルドできる。Xcodeのファイルシステム同期グループを使うなら、XcodeGenは不要な付帯作業かもしれない
iOSでは、エージェントに App Store Connect機能 を追加することを最も勧めたい: https://github.com/rorkai/App-Store-Connect-CLI。そうすればXcodeを常時起動しておく必要がないだけでなく、MacBookの近くにいる必要もない。iPhoneでCodexから変更したあと、
ascにビルドとTestFlightへのアップロードを指示し、新バージョンをダウンロードして実行しながら反復すればよい予算が足りずMacを買えない開発者が、事実上 iOS開発から排除 されている現実がずっと気になっている。Apple製品の価格が上がるたび、開発に参加できない人がさらに増える
アプリをバイブコーディングするなら、長期的には Expo のほうがより良い選択だった。Reactベースなので、LLMの学習データにはSwiftよりReactコードのほうがはるかに多く、生成されるコードの品質も高い
すべての作業をコマンドラインで実行でき、Webビューなのでデバッグもしやすい。ネイティブiOSアプリとしてコンパイルしたあとは、ほかのネイティブアプリと同じような感触になる。ExpoとFastlane を組み合わせればiOS提出と配布を完全に自動化でき、コマンド一つでApp Storeに新バージョンを上げられる