xcrun simctl launchは、シミュレータにインストール済みのアプリをbundle identifier指定で起動するサブコマンドである。 単純な起動だけでなく、起動時の引数や環境変数の指定、デバッガのアタッチ待ちなど、開発・デバッグ用途のオプションを備える。

基本的な使い方

launch <device> <app bundle identifier> [<argv 1> <argv 2> ...]の形式で実行する。

$ xcrun simctl launch "TIL Launch iPhone" com.apple.mobilesafari
com.apple.mobilesafari: 3104

標準出力には<bundle identifier>: <PID>の形式で、起動したプロセスのPIDが出力される。

既にそのアプリが起動中の場合、launchは新しいプロセスを起動せず、実行中のプロセスのPIDをそのまま返す。

$ xcrun simctl launch "TIL Launch iPhone" com.apple.mobilesafari
com.apple.mobilesafari: 4433

$ xcrun simctl launch "TIL Launch iPhone" com.apple.mobilesafari
com.apple.mobilesafari: 4433

末尾の引数はそのままプロセスのargvとして渡る

<app bundle identifier>の後ろに指定した引数は、起動するプロセスのargvにそのまま渡る。 psでプロセスのコマンドラインを確認すると、指定した引数がそのまま付与されていることが分かる。

$ xcrun simctl launch "TIL Launch iPhone" com.apple.mobilesafari --til-test-arg foo
com.apple.mobilesafari: 3602

$ ps -p 3602 -o command=
/Library/Developer/.../MobileSafari.app/MobileSafari --til-test-arg foo

SIMCTL_CHILD_接頭辞を付けた環境変数が、起動したアプリの環境変数として渡る

ヘルプに記載の通り、simctl launchを実行するシェルの環境変数にSIMCTL_CHILD_接頭辞を付けて設定すると、接頭辞を除いた名前で起動したアプリのプロセスに渡る。 ps ewwでプロセスの環境変数を確認すると、接頭辞なしの変数として存在することが分かる。

$ SIMCTL_CHILD_TIL_TEST_VAR="hello-from-til" xcrun simctl launch "TIL Launch iPhone" com.apple.mobilesafari
com.apple.mobilesafari: 3386

$ ps eww 3386 | grep -o "TIL_TEST_VAR=[^ ]*"
TIL_TEST_VAR=hello-from-til

--terminate-running-processで実行中のプロセスを終了してから起動し直す

前述の通り、既に起動中のアプリに対してlaunchを実行しても新しいプロセスにはならないが、--terminate-running-processを付けると実行中のプロセスを終了してから改めて起動し直す。 起動し直された後のPIDは、それまで実行中だったプロセスのPIDとは異なる値になる。

$ xcrun simctl launch "TIL Launch iPhone" com.apple.mobilesafari
com.apple.mobilesafari: 4433

$ xcrun simctl launch --terminate-running-process "TIL Launch iPhone" com.apple.mobilesafari
com.apple.mobilesafari: 4691

--wait-for-debuggerはデバッガがアタッチするまでプロセスを一時停止させる

--wait-for-debuggerを付けて起動すると、プロセスは生成されるがすぐには実行を開始せず、デバッガのアタッチを待つ状態になる。 psでプロセスの状態(STAT列)を確認すると、通常起動時は実行中を示すRsだが、--wait-for-debugger時は一時停止を示すTsになっていることが確認できる。

$ xcrun simctl launch --wait-for-debugger "TIL Launch iPhone" com.apple.mobilesafari
com.apple.mobilesafari: 4308

$ ps -p 4308 -o pid,stat,command
 PID STAT COMMAND
4308 Ts   /Library/Developer/.../MobileSafari.app/MobileSafari

(比較: --wait-for-debuggerなしの通常起動)
$ ps -p 4433 -o pid,stat,command
 PID STAT COMMAND
4433 Rs   /Library/Developer/.../MobileSafari.app/MobileSafari

Xcodeを介さずlldb等を直接アタッチしてデバッグしたい場面での利用が想定される。

短縮形-wは使えない

ヘルプのUsageには-w--wait-for-debuggerの短縮形として記載されているが、実際に-wを指定すると引数解析に失敗し、ヘルプが再表示されるだけのエラーになる。

$ xcrun simctl launch -w "TIL Launch iPhone" com.apple.mobilesafari
Launch an application by identifier on a device.
Usage: simctl launch [-w | --wait-for-debugger] ...
(以下ヘルプ全体が表示される)

長い形式の--wait-for-debuggerを使えば問題なく動作するため、デバッガのアタッチ待ちをする場合はこちらを使う。

--consoleは接続したまま標準出力・標準エラー出力を表示し続ける

--consoleを付けると、launchコマンド自体がフォアグラウンドでブロックし、アプリの標準出力・標準エラー出力をそのまま端末に表示し続ける。

$ xcrun simctl launch --console "TIL Launch iPhone" com.apple.mobilesafari
com.apple.mobilesafari: 5501
(Ctrl-Cで打ち切るまでブロックし続ける)

--console-ptyも同様にブロックするが、PTY経由で出力する点が異なる。 どちらも--stdout--stderrとの併用はできない。

--stdout--stderrに指定するパスは、ホストではなくシミュレータ内のファイルシステムを指す

--stdout=<path>--stderr=<path>で、アプリの標準出力・標準エラー出力をファイルにリダイレクトできる。 このとき指定するパスは、実行しているMac(ホスト)のファイルシステムではなく、シミュレータ内から見たファイルシステムのパスとして解釈される。

$ xcrun simctl launch --stdout=/tmp/til-out.log --stderr=/tmp/til-err.log "TIL Launch iPhone" com.apple.mobilesafari
com.apple.mobilesafari: 5094

/tmp/til-out.logを指定しても、ホスト側の/tmp/til-out.logにはファイルが作成されない。 lsofでプロセスの標準出力・標準エラー出力のファイルディスクリプタを確認すると、実際にはデバイスのデータディレクトリ配下に作成されていることが分かる。

$ lsof -a -p 5094 -d 0,1,2
COMMAND    PID USER   FD   TYPE DEVICE SIZE/OFF      NODE NAME
MobileSaf 5094  sue    0r   CHR    3,2      0t0       336 /dev/null
MobileSaf 5094  sue    1u   REG   1,15        0 ... /Users/xxx/Library/Developer/CoreSimulator/Devices/<UDID>/data/tmp/til-out.log
MobileSaf 5094  sue    2u   REG   1,15        0 ... /Users/xxx/Library/Developer/CoreSimulator/Devices/<UDID>/data/tmp/til-err.log

シミュレータの/tmpは、ホスト側の~/Library/Developer/CoreSimulator/Devices/<UDID>/data/tmpに対応している。 出力先ファイルを直接確認したい場合は、指定したパスをホストのパスとして探すのではなく、対象デバイスのデータディレクトリ配下を確認する必要がある。