xcrun simctl installxcrun simctl uninstallは、シミュレータへアプリをインストール・削除するサブコマンドである。

参考: xcrun simctl listappsでiOSシミュレータのインストール済みアプリ一覧を表示する

参考: xcrun simctl get_app_containerでiOSシミュレータのアプリのコンテナパスを取得する

基本的な使い方

install <device> <path>.appバンドルをインストールし、uninstall <device> <bundle identifier>でアプリを削除する。

$ xcrun simctl install "TIL Install iPhone" ./TILInstallApp.app
$ xcrun simctl listapps "TIL Install iPhone" | grep -c "com.til.installtest"
2

$ xcrun simctl uninstall "TIL Install iPhone" com.til.installtest
$ xcrun simctl listapps "TIL Install iPhone" | grep -c "com.til.installtest"
0

<path>はカレントディレクトリ基準の相対パスとしても解釈される。 grep -cの結果が2件になっているのは、listappsの出力形式(NSDictionaryのdescription)では辞書のキーとCFBundleIdentifierフィールドの両方に同じbundle identifierが出現するためである。

対象デバイスは起動中(Booted)である必要がある

installuninstallいずれも、対象デバイスが起動中でないとUnable to lookup in current state: Shutdown(code=405)でエラーになる。 appinfolistappsと同じ制約である。

uninstallは未インストールのアプリを指定してもエラーにならない

存在しないパスをinstallに指定すると明確なエラーになる。

$ xcrun simctl install "TIL Install iPhone" /tmp/does-not-exist.app
An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=2):
Simulator device failed to install the application.
Underlying error (domain=NSPOSIXErrorDomain, code=2):
	lstat of /tmp/does-not-exist.app failed: No such file or directory

一方uninstallは未インストールのbundle identifierを指定してもエラーにならず、終了コードも0のままである。

$ xcrun simctl uninstall "TIL Install iPhone" com.til.neverinstalled
$ echo $?
0

uninstallは「対象が存在しなければ何もしない」という冪等な動作をするため、CIのクリーンアップ処理で存在確認なしに呼び出しても安全である。

既にインストール済みのbundle identifierへの再インストールはアプリデータを保持する

同じbundle identifierのアプリが既にインストールされている状態でinstallを実行すると、.app本体は新しいものに置き換わるが、Documentsなどのアプリデータは保持される。

$ xcrun simctl get_app_container "TIL Install iPhone" com.til.installtest data
.../Containers/Data/Application/491583AB-.../
$ echo "test-data" > "$(xcrun simctl get_app_container "TIL Install iPhone" com.til.installtest data)/Documents/test.txt"

$ xcrun simctl install "TIL Install iPhone" ./TILInstallApp.app
$ xcrun simctl get_app_container "TIL Install iPhone" com.til.installtest data
.../Containers/Data/Application/1BC308ED-.../
$ cat "$(xcrun simctl get_app_container "TIL Install iPhone" com.til.installtest data)/Documents/test.txt"
test-data

DataContainerのUUID自体は再インストールのたびに変わるが、中身のファイルは新しいコンテナへ引き継がれる。 CFBundleShortVersionStringを変更していない場合でも同様にデータが引き継がれる。 バージョン番号の変化は関係なく、「既にインストール済みかどうか」だけで判定されているとみられる。 古い方のDataContainerディレクトリは、データ移行後に削除される。

対照的に、uninstallしてからinstallし直した場合は新規インストール扱いになり、Documentsは空の状態から始まる。

$ xcrun simctl uninstall "TIL Install iPhone" com.til.installtest
$ xcrun simctl install "TIL Install iPhone" ./TILInstallApp.app
$ ls "$(xcrun simctl get_app_container "TIL Install iPhone" com.til.installtest data)/Documents/"
(空)

アプリの実機同様のアップデート動作(データを引き継ぐ)を再現したい場合はuninstallを挟まずにinstallし直し、まっさらな状態から検証したい場合はuninstallしてからinstallする、という使い分けができる。