メインコンテンツまでスキップ

リモート Mac ビルドサーバーでの iOS 開発

iOS remote Mac build setup with Cursor

iOS アプリのビルドにはどこかに macOS が必要ですが、だからといってエディタを Mac で実行する必要はありません。Cursor の Remote-SSH サポートがあれば、Windows または Linux での高速なローカル編集体験を保ちながら、Mac マシンがコンパイル、署名、シミュレータ実行を処理できます。このガイドでは、アーキテクチャの選択肢、セットアップ、ビルドコマンド、そして最も時間を浪費する落とし穴を順に説明します。

なぜ Mac が必要か​

Apple のツールチェーンは macOS でのみ動作し、iOS ワークフローのいくつかの部分はそれに直接依存しています。

  • コード署名:署名 ID は macOS のキーチェーンに存在し、署名済み成果物を生成するために使用されます。
  • プロビジョニングプロファイル:アプリ ID をデバイスと証明書に結びつけるプロファイルは、Apple のツールを通じて管理されます。
  • iOS シミュレータ:シミュレータは macOS アプリケーションです。実デバイスの忠実度に相当するサポートされた Linux または Windows の代替はありません。
  • xcodebuild ツールチェーン:コンパイラ、リンカ、および SDK は macOS の Xcode 内に同梱されています。
  • App Store への提出:App Store Connect へのアップロードには Apple のツールが必要で、通常は macOS 上の Transporter や altool/notarytool 経由です。

これらはすべて macOS に縛られているため、実用的な問いは Mac が必要かどうかではなく、その Mac がどこにあるかです。

アーキテクチャの選択肢​

単一の最善の答えはありません。正しい選択はチームの規模、予算、およびビルドの頻度に依存します。

選択肢仕組み長所短所
Mac への Remote-SSHCursor が SSH 経由で接続する。ファイルとターミナルは Mac 上にある単一の信頼できる情報源、完全なツールチェーンアクセス、本物のシミュレータ常時稼働で到達可能な Mac が必要
Mac への rsync / git pushローカルで編集し、Mac にプッシュし、そこでビルドする編集はオフラインでも機能、単純なメンタルモデル同期の漂流、大きな転送が遅い、ライブ LSP なし
クラウド CI の macOS ランナーGit にプッシュし、ホストされた macOS ランナーで CI がビルドする所有するハードウェアが不要、再現可能、リリースに適している対話的でない、分単位のコスト、コールドスタート
クラウド Mac プロバイダーSSH 経由で Mac(MacStadium、AWS EC2 Mac)をレンタルするオンデマンドの本物のハードウェア、スケーラブル時間単位のコスト、プロビジョニングと破棄のオーバーヘッド

日々の開発には、Mac への Remote-SSH が通常最良のバランスです。クラウド CI はリリースパイプラインに適しています。クラウド Mac プロバイダーは、デスクを所有せずにフリートを求めるチームに適しています。

タスクにツールを合わせる

対話的な開発には Remote-SSH、リリースにはクラウド CI、スケールや短いバーストにはクラウド Mac インスタンスを使用してください。成熟したチームのほとんどは、1 つの選択肢にすべてを強いるのではなく、少なくとも 2 つを運用しています。

リモート Mac のセットアップ​

これらの手順は、ビルドサーバーとして機能する Mac で 1 回実行します。管理者権限と安定したネットワーク接続が必要です。

1. Xcode とコマンドラインツールのインストール​

Xcode を Mac App Store からインストールし、xcodebuild と関連バイナリが使用可能になるようにコマンドラインツールをインストールします。

xcode-select --install

# Point the toolchain at the full Xcode app, not the standalone CLT
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer

2. ライセンスの承諾​

新規インストールでは、ライセンスが承諾されるまで xcodebuild は実行を拒否します。

sudo xcodebuild -license accept

3. ビルドユーザーの作成​

個人アカウントとしてビルドするのは避けてください。専用ユーザーを作成し、認証情報とキーチェーンを隔離します。

# Create a build user (adjust the short name and full name)
sudo dscl . -create /Users/builduser
sudo dscl . -create /Users/builduser UserShell /bin/zsh
sudo dscl . -create /Users/builduser RealName "Build User"
sudo dscl . -create /Users/builduser UniqueID 510
sudo dscl . -create /Users/builduser PrimaryGroupID 20
sudo dscl . -create /Users/builduser NFSHomeDirectory /Users/builduser
sudo passwd builduser

4. リモートログインの有効化​

SSH をオンにして、Mac が接続を受け入れるようにします。

sudo systemsetup -setremotelogin on

5. SSH 公開鍵のインストール​

ローカルの公開鍵をビルドユーザーの authorized_keys に追加し、パスワードなしで接続できるようにします。

mkdir -p /Users/builduser/.ssh
# Paste your public key (id_ed25519.pub) into authorized_keys
chmod 700 /Users/builduser/.ssh
chmod 600 /Users/builduser/.ssh/authorized_keys
chown -R builduser:staff /Users/builduser/.ssh
SSH アカウントをロックダウンする

インターネットに公開されたビルド Mac は、ビルドユーザーのパスワードログインを許可すべきではありません。パスワード認証を無効にし、可能な場合は許可するソースアドレスを制限し、秘密鍵はローカルマシンのみに保ってください。

Remote-SSH 経由での Cursor の接続​

Mac が到達可能になったら、Cursor をそれに接続します。リモートは、拡張機能、ターミナル、およびビルドが実行される環境になります。

1. Remote-SSH 拡張機能のインストール​

Cursor を開き、拡張機能ビュー(Ctrl+Shift+X)に移動し、Remote - SSH をインストールします(Microsoft がパッケージ化した拡張機能は Cursor のマーケットプレイスで利用可能です)。

2. SSH 構成エントリの追加​

ローカルの SSH 構成(通常は ~/.ssh/config)を編集し、ホストエイリアスを追加します。

# ~/.ssh/config
Host mac-build
HostName 203.0.113.10
User builduser
IdentityFile ~/.ssh/id_ed25519
ServerAliveInterval 30
ServerAliveCountMax 4
TCPKeepAlive yes

キープアライブ設定は、不安定なネットワークでの切断を減らします。ネイティブの macOS OpenSSH は mosh をサポートしないため、キープアライブが防御の第一線であることに注意してください。

3. 接続​

コマンドパレット(Ctrl/Cmd+Shift+P)を開き、Remote-SSH: Connect to Host を実行して、mac-build を選択します。あるいは、1 ステップで実行します。

cursor --remote ssh-remote+mac-build /Users/builduser/projects/MyiOSApp

4. ウィンドウが再読み込みされる​

Cursor はリモートウィンドウに再読み込みします。ステータスバーにリモートホスト名が表示されます。すべてのターミナル、タスク、および言語サーバーは Mac 上で実行され、UI とレンダリングはローカルに留まります。

5. 拡張機能はリモートにインストールされる​

Swift ファイルを開くと、Cursor が言語拡張機能をリモートにインストールするよう促します。それを受け入れてください。Swift ツール、デバッグアダプタ、および Swift 拡張機能は、ローカルマシンではなく Mac に属します。

ローカル拡張機能とリモート拡張機能

テーマなどの UI 専用拡張機能はローカルで実行されます。Swift 言語サーバーや任意のデバッグアダプタなど、プロセスを起動する拡張機能はリモート Mac で実行されます。Cursor に任せれば、Cursor がその分割を管理します。

ローカルで編集し、リモートでビルドする​

生産的なパターンは次のとおりです。Cursor で編集し、Mac 上のターミナルである統合ターミナルでビルドを実行します。ファイルは常に Mac 上にあるため、漂流する同期ステップはありません。

基本的なビルド​

特定のデバイスを必要としない汎用の iOS 宛先に対してスキームをビルドします。

xcodebuild \
-project MyApp.xcodeproj \
-scheme MyApp \
-configuration Debug \
-destination 'generic/platform=iOS' \
build

プロジェクトではなくワークスペースを使用する場合は、-project を -workspace に置き換えます。

xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -destination 'generic/platform=iOS' build

リリース用のアーカイブ​

アーカイブは .xcarchive を生成し、エクスポートステップがそれを配布用の .ipa に変換します。

xcodebuild \
-workspace MyApp.xcworkspace \
-scheme MyApp \
-configuration Release \
-destination 'generic/platform=iOS' \
-archivePath build/MyApp.xcarchive \
archive

アーカイブのエクスポート​

アーカイブ後、配布方法を記述したエクスポートオプションの plist を使用してエクスポートします。

xcodebuild -exportArchive \
-archivePath build/MyApp.xcarchive \
-exportPath build/export \
-exportOptionsPlist ExportOptions.plist

App Store 形式の配布用の ExportOptions.plist は、おおよそ次のようになります。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store-connect</string>
<key>teamID</key>
<string>ABCDE12345</string>
<key>signingStyle</key>
<string>automatic</string>
<key>uploadSymbols</key>
<true/>
</dict>
</plist>
CI では汎用の宛先を優先する

generic/platform=iOS 向けにビルドすると、ビルドを特定のデバイスやシミュレータの UDID に結びつけるのを避けられ、構成変更なしで同じコマンドが任意のビルドマシンで機能します。

テストの実行とログの確認​

テストはビルドと同じ方法で実行され、test アクションと具体的な宛先を使用するだけです。

テストの実行​

xcodebuild test \
-workspace MyApp.xcworkspace \
-scheme MyApp \
-destination 'platform=iOS Simulator,name=iPhone 16,OS=latest'

ログを読みやすくする​

生の xcodebuild 出力は膨大でスキャンしにくいものです。フォーマッタを通して、読みやすい行にまとめます。

# xcbeautify: modern, fast formatter
xcodebuild test -workspace MyApp.xcworkspace -scheme MyApp \
-destination 'platform=iOS Simulator,name=iPhone 16' | xcbeautify

# xcpretty: older but widely available alternative
xcodebuild test -workspace MyApp.xcworkspace -scheme MyApp \
-destination 'platform=iOS Simulator,name=iPhone 16' | xcpretty

永続的なログファイルと読みやすいストリームの両方が必要な場合は、出力を tee します。

xcodebuild build -scheme MyApp -destination 'generic/platform=iOS' \
2>&1 | tee build.log | xcbeautify

ターミナルがリモートであるため、ログファイルは Mac 上に置かれます。同じリモートウィンドウで Cursor のファイルエクスプローラから読むか、好みに応じて 2 つ目の SSH セッションでローカルにストリームします。

シミュレータのスクリーンショットの取得​

シミュレータは Mac で実行されるため、ローカルでスクリーンショットを撮るのではなく、SSH 経由で xcrun simctl を使ってキャプチャします。

シミュレータの一覧表示と起動​

# List available devices
xcrun simctl list devices available

# Boot a specific simulator by UDID or name
xcrun simctl boot "iPhone 16"

# Open the Simulator UI if a desktop session is available
open -a Simulator

アプリのインストールと起動​

# Install a built .app onto the booted simulator
xcrun simctl install booted ./build/Build/Products/Debug-iphonesimulator/MyApp.app

# Launch it by bundle identifier
xcrun simctl launch booted com.example.MyApp

スクリーンショットのキャプチャ​

# Save a screenshot to a file on the Mac
xcrun simctl io booted screenshot /Users/builduser/shots/home.png

そのファイルはリモートにあるため、Cursor のリモートウィンドウで直接開きます。画像プレビューは Remote-SSH 経由で機能するため、ローカルにコピーする必要はありません。

ヘッドレスシミュレータにはセッションが必要

simctl は GUI なしでシミュレータを起動できますが、一部の UI 主導のテストやよりリッチなスクリーンショットシナリオはログイン済みのデスクトップセッションを期待します。ヘッドレス Mac では、永続的なログイン済みセッションを保つか、シミュレータ UI 機能の制限を想定してください。

コード署名​

署名は、新しいマシンで最も失敗しやすい部分であるため、意図的にセットアップしてください。

キーチェーン内の証明書​

署名 ID はビルドユーザーのキーチェーンに存在します。Apple 開発者アカウントからエクスポートした .p12 をインポートします。

security import /path/to/distribution.p12 \
-k /Users/builduser/Library/Keychains/login.keychain-db \
-P "p12-password" \
-T /usr/bin/codesign \
-T /usr/bin/security

-T フラグは、インポートされた ID への codesign と security のアクセスを許可し、ビルドがアクセスプロンプトで止まらないようにします。

プロビジョニングプロファイル​

Xcode がビルドユーザーに対して期待する場所にプロファイルを配置します。

mkdir -p /Users/builduser/Library/MobileDevice/Provisioning\ Profiles
cp ~/profiles/MyApp_AppStore.mobileprovision \
/Users/builduser/Library/MobileDevice/Provisioning\ Profiles/

自動署名と手動署名​

  • 自動署名:より単純で、ビルドユーザーが Apple 開発者アカウントにログインし、Apple のサービスへのネットワークアクセスを持つ必要があります。単一の信頼できる Mac に適しています。
  • 手動署名:CI にとってより再現可能です。CODE_SIGN_STYLE=Manual を指定し、特定の ID とプロファイルを指し、アカウント依存の挙動を避けます。
xcodebuild archive \
-workspace MyApp.xcworkspace \
-scheme MyApp \
-destination 'generic/platform=iOS' \
CODE_SIGN_STYLE=Manual \
CODE_SIGN_IDENTITY="Apple Distribution: Example Corp (ABCDE12345)" \
PROVISIONING_PROFILE_SPECIFIER="MyApp AppStore Profile" \
-archivePath build/MyApp.xcarchive
CI ではキーチェーンをアンロックする

無人 Mac では、ビルド前にログインキーチェーンをアンロックし、codesign が GUI プロンプトなしで ID に到達できるようにします。アンロックパスワードはシークレットストアに保ち、コミットされたスクリプトには決して入れないでください。

コスト比較​

価格は変動するため、以下は現在の見積もりではなく、計画用のおおよその範囲として扱ってください。

選択肢おおよその月額コスト備考
ビルドサーバーとして Mac mini を所有ハードウェア購入後、ほぼ定期的コストはゼロ安定した使用で最安。保守は自分で行う
クラウド Mac プロバイダー(MacStadium、EC2 Mac)時間単位、24 時間稼働で月に数百になることが多いスケールアップまたはダウン可能。プロビジョニングのオーバーヘッド
クラウド CI の macOS ランナー分単位、ビルド中のみ課金時折のビルドに最安。コールドスタートが痛い
共有 Mac への rsync / gitMac のコストのみ個人には十分、チームには扱いにくい

1 日中ビルドする小規模チームには、通常、専用の Mac mini が最善です。ビルドがまれであるか厳密にリリース時である場合は、クラウド CI が勝ります。オンデマンドの容量が必要でハードウェアを所有したくない場合は、クラウド Mac インスタンスが勝ります。

一般的な落とし穴​

リモートビルドの苦痛のほとんどは、少数の繰り返し発生するミスから来ます。風変わりなものをデバッグする前に、これらを確認してください。

ライセンスが承諾されていない​

xcodebuild がライセンスエラーで即座に失敗します。次で一度修正します。

sudo xcodebuild -license accept

誤った Xcode バージョンが選択されている​

複数の Xcode インストールがツールチェーンを混乱させます。意図したものを明示的に選択します。

sudo xcode-select -s /Applications/Xcode-16.app/Contents/Developer
xcodebuild -version

署名 ID の欠如​

アーカイブは成功するがエクスポートに失敗するビルドは、通常、一致する ID またはプロファイルがありません。キーチェーンが実際に何を保持しているか確認します。

security find-identity -v -p codesigning

必要な ID に対して一覧が空の場合、.p12 を再度インポートし、プロファイルがアプリのバンドル ID と一致することを確認します。

遅い同期​

SSH 経由で編集するのではなくコードを同期している場合は、rsync より git を優先してください。Git は差分を転送し、履歴と競合処理を提供します。rsync はツリー全体を移動し、無言で漂流します。Remote-SSH 経由で直接編集する場合、同期はまったくなく、これが最速のオプションです。

ネットワーク切断による SSH の破壊​

不安定なリンクでの長時間ビルドは接続を落とし、ビルドを止めることがあります。構成内の SSH キープアライブで軽減し、長時間ビルドは Mac 上の tmux または screen 内で実行して切断に耐えられるようにします。

tmux new-session -s build 'xcodebuild archive -workspace MyApp.xcworkspace -scheme MyApp -destination "generic/platform=iOS" -archivePath build/MyApp.xcarchive'

後で再接続し、tmux attach -t build で再アタッチします。

シミュレータがヘッドレスで動作しない​

一部のテストフローは可視のシミュレータを前提としています。ヘッドレス Mac では、simctl でシミュレータを起動し、ログイン済みデスクトップセッションを保つか、シミュレータ UI のカバレッジ低下を受け入れてください。

初日からビルドを tmux で包む

完全に制御していないネットワーク越しにビルドする場合は、すべての長時間 xcodebuild を tmux 内で実行してください。SSH セッションが生き残るかどうかに関係なく、ビルドは Mac 上で実行し続け、再アタッチして完了を確認できます。

リモートビルドは、厳しいプラットフォームの制約を作業フローの選択に変えます。チームに合ったアーキテクチャを選び、Mac を一度セットアップし、macOS が必要とする作業を Mac に行わせながら、Cursor の Remote-SSH で編集体験を高速に保ちましょう。