参考: xcrun simctl launchでiOSシミュレータのアプリを環境変数・引数付きで起動する

xcrun simctl spawnは、launchのようにアプリのbundle identifierを指定するのではなく、実行ファイルのパスを直接指定してシミュレータ上でプロセスを実行するサブコマンドである。 アプリとしてインストールされていないコマンド(loglaunchctlなど)をシミュレータのコンテキストで実行したい場合に使う。

基本的な使い方

spawn <device> <path to executable> [<argv 1> <argv 2> ...]の形式で実行する。

$ xcrun simctl spawn "TIL Spawn iPhone" launchctl print system
system = {
	type = system
	handle = 0
	...

SIMCTL_CHILD_接頭辞を付けた環境変数の受け渡しはsimctl launch と同様に使える。

実行ファイルのパスは3通りの方法で指定できる

ヘルプに記載の通り、<path to executable>の指定方法によって解決のされ方が異なる。

スラッシュを含まない場合: デバイスの$PATHから検索する

コマンド名だけを指定すると、シミュレータのランタイム側の$PATHから実行ファイルを検索する。

$ xcrun simctl spawn "TIL Spawn iPhone" launchctl print system

/で始まる場合: ホストのファイルシステムのパスとして扱う

先頭が/のパスは、実行しているMac(ホスト)のファイルシステム上のパスとしてそのまま扱われる。

$ xcrun simctl spawn "TIL Spawn iPhone" /bin/pwd
/Users/xxx/Library/Developer/CoreSimulator/Devices/<UDID>/data

ホストの/bin/pwdを実行しているにもかかわらず、出力されるカレントディレクトリはデバイスのデータディレクトリになる。 spawnで実行したプロセスは、ホストのバイナリであってもデバイスのデータディレクトリを起点として動作することが分かる。

先頭以外に/を含む場合: カレントディレクトリを優先し、次にデバイスの$SIMULATOR_ROOTを探す

usr/bin/logのように途中に/を含み先頭が/でないパスは、まずsimctl実行時のカレントディレクトリを起点に探し、見つからなければデバイスの$SIMULATOR_ROOT(シミュレータのランタイムのルート)を起点に探す。

カレントディレクトリに同名のファイルを用意すると、そちらが優先されることを実機で確認できる。

$ mkdir -p usr/bin
$ printf '#!/bin/sh\necho "this-is-the-local-fake-log-script"\n' > usr/bin/log
$ chmod +x usr/bin/log

$ xcrun simctl spawn "TIL Spawn iPhone" usr/bin/log
this-is-the-local-fake-log-script

launchd経由での起動はシェルを介さず直接execするため、#!/bin/shのシバン行がないとExec format errorで失敗する。

カレントディレクトリにusr/bin/logが存在しない場合は、デバイスの$SIMULATOR_ROOT配下のusr/bin/log(実体はシミュレータランタイムのlogコマンド)が実行される。

-s(standalone)を付けると、デバイスが起動していなくても実行できる

-sを付けずに、シャットダウン中のデバイスに対してspawnを実行するとエラーになる。

$ xcrun simctl spawn "TIL Spawn iPhone" launchctl list
An error was encountered processing the command (domain=com.apple.CoreSimulator.SimError, code=405):
Process spawn via launchd failed because device is not booted.

-sを付けると、シャットダウン中のままでも実行できる。

$ xcrun simctl spawn -s "TIL Spawn iPhone" /bin/echo "hello-from-spawn"
hello-from-spawn

ヘルプに記載の通り-sは「NULLのmach bootstrapポートを使う」オプションで、他のサービスに接続できないプロセスとして実行される。 そのため、launchctl listのように他のシステムサービスとの通信が必要なコマンドを-s付きで実行しても、エラーにはならないが出力が得られない。 ヘルプにも非推奨(deprecated)と明記されている。

-wはプロセスをサスペンド状態で起動する

-wを付けて起動すると、プロセスはすぐには実行を開始せず一時停止した状態で生成される。 psで状態を確認すると、-wを付けない場合はsleepのような待機系プロセスの通常状態を示すS系のSTATだが、-wを付けた場合は一時停止を示すT系のSTATになる。

$ xcrun simctl spawn -w "TIL Spawn iPhone" /bin/sleep 30 &

$ ps aux | grep "sleep 30"
sue  89793   0.0  0.0  ...  Ts   ...  /bin/sleep 30

simctl launch--wait-for-debuggerと同様の仕組みで、デバッガのアタッチを待つ用途に使える。

存在しない実行ファイルを指定するとエラーになる

$ xcrun simctl spawn "TIL Spawn iPhone" nonexistent-command-xyz
An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=2):
The operation couldn't be completed. No such file or directory

活用例: シミュレータ内のログをGUI操作なしで直接確認する

spawnlog streamを実行すると、シミュレータ内の特定のデーモンやプロセスのログを、GUI操作を挟まずに直接確認できる。

$ xcrun simctl spawn "TIL Spawn iPhone" log stream --level debug --predicate 'eventMessage contains "raw coordinate"'

位置情報や通知など、GUIの許可ダイアログに阻まれて動作確認しづらい機能の検証に活用できる。