xcrun simctl listappsは、シミュレータにインストール済みのアプリの一覧を、各アプリの詳細情報付きで表示するサブコマンドである。

基本的な使い方

listapps <device>の形式で実行する。

$ xcrun simctl listapps "TIL ListApps iPhone"
{
    "com.apple.Bridge" =     {
        ApplicationType = System;
        Bundle = "file:///Library/Developer/CoreSimulator/Volumes/.../Bridge.app/";
        CFBundleDisplayName = Watch;
        CFBundleIdentifier = "com.apple.Bridge";
        DataContainer = "file:///Users/xxx/Library/Developer/CoreSimulator/Devices/<UDID>/data/Containers/Data/Application/<UUID>/";
        IsRemovable = 0;
        ...
    };
    ...
}

出力は波かっこと;区切りの、Objective-Cの古い形式のプロパティリスト(NSDictionaryのdescription形式)である。 見た目はJSONに似ているが、キーを引用符で囲まない・区切りは;・末尾カンマなしなど文法が異なり、そのままjqなどのJSONパーサーには通せない。

SystemUserでアプリの種類を区別できる

ApplicationTypeフィールドで、プリインストール済みのシステムアプリか、後からinstallしたユーザーアプリかを区別できる。 新規作成したシミュレータの時点では、36個のシステムアプリだけが登録されており、いずれもApplicationType = Systemである。

ここにsimctl installでアプリを追加すると、ApplicationType = Userのエントリが増える。

$ xcrun simctl listapps "TIL ListApps iPhone" | grep -A10 '"com.til.fakeapp"'
"com.til.fakeapp" =     {
    ApplicationType = User;
    Bundle = "file:///Users/xxx/.../Containers/Bundle/Application/<UUID>/TILFakeApp.app/";
    BundleContainer = "file:///Users/xxx/.../Containers/Bundle/Application/<UUID>/";
    CFBundleIdentifier = "com.til.fakeapp";
    DataContainer = "file:///Users/xxx/.../Containers/Data/Application/<UUID>/";
    IsDeveloperApp = 1;
    IsFirstParty = 0;
    IsRemovable = 1;
    ...
};

システムアプリはBundleと実行ファイルの実体がランタイムの読み取り専用ボリューム上にあるのに対し、ユーザーアプリはBundleContainerDataContainerともにデバイスのデータディレクトリ配下に作成され、IsDeveloperAppIsRemovable1になる。 uninstallすると、対応するエントリは一覧から即座に消える。

CFBundleDisplayNameはローカライズ文字列で上書きされる場合がある

アプリのInfo.plistCFBundleDisplayNameを書き換えても、.lprojフォルダ配下のInfoPlist.stringsに同名のローカライズ済み文字列が残っていると、listappsにはそちらの値が表示される。

$ plutil -p ./TILFakeApp.app/Info.plist | grep CFBundleDisplayName
  "CFBundleDisplayName" => "TIL Fake App"

$ plutil -p ./TILFakeApp.app/en.lproj/InfoPlist.strings | grep CFBundleDisplayName
  "CFBundleDisplayName" => "Preview"

$ xcrun simctl listapps "TIL ListApps iPhone" | grep -A3 '"com.til.fakeapp"'
"com.til.fakeapp" =     {
    ApplicationType = User;
    Bundle = "file:///...TILFakeApp.app/";
    CFBundleDisplayName = Preview;

既存アプリを複製してbundle identifierだけ差し替えるような検証をする場合は、Info.plistだけでなく各.lprojInfoPlist.stringsも合わせて書き換えないと、意図した表示名にならないので注意する。

listappsに表示されないアプリも存在する

listappsは、シミュレータのランタイムに含まれる.appのうち一部だけを表示しており、すべての.appを網羅しているわけではない。 たとえばAccountAuthenticationDialog.appのような内部サービス用アプリを、bundle identifierだけ差し替えてcom.til.hiddenappとしてsimctl installでインストールすると、simctl launchで実際に起動できるにもかかわらずlistappsの一覧には表示されない。

$ xcrun simctl launch "TIL ListApps iPhone" com.til.hiddenapp
com.til.hiddenapp: 60557

$ xcrun simctl listapps "TIL ListApps iPhone" | grep -c "com.til.hiddenapp"
0

get_app_containerでも同様に、com.til.hiddenappのコンテナは取得できない。 listappsの一覧は「ユーザーが目にする可能性のあるアプリ」を対象にしたものであり、内部サービス用のアプリまでは対象にしていないとみられる。