xcrun simctl pairは、Apple WatchシミュレータとiPhoneシミュレータを、実機同様の「ペア」として組み合わせるサブコマンドである。 watchOSアプリの開発やテストで、iPhone側のCompanion appとの連携確認に使う。

基本的な使い方

pair <watch device> <phone device>の形式で実行する。 watchOSシミュレータランタイムが未インストールの場合は、あらかじめxcodebuild -downloadPlatform watchOSでダウンロードしておく必要がある。

$ xcrun simctl pair "TIL Pair Watch" "TIL Pair iPhone"
8EC5FE87-61ED-4C18-93F2-5AF97423BD9D

標準出力にはペアのUUIDが出力される。 list pairsでペアの一覧と、それぞれの役割・状態を確認できる。

$ xcrun simctl list pairs
== Device Pairs ==
8EC5FE87-61ED-4C18-93F2-5AF97423BD9D (active, disconnected)
    Watch: TIL Pair Watch (50B08633-BDF4-4C70-BD17-50257392B874) (Shutdown)
    Phone: TIL Pair iPhone (8E084815-20C6-4D0C-85CB-6BB0B3922E07) (Shutdown)

引数の順序は結果に影響しない

ヘルプのUsageでは<watch device>が先、<phone device>が後と記載されているが、実際には引数の順序を入れ替えても正しくペアリングできる。 デバイスのfamily(Apple Watchなのか、iPhoneなのか)から役割を自動判定しているためである。

$ xcrun simctl pair "TIL Pair iPhone" "TIL Pair Watch"
8EC5FE87-61ED-4C18-93F2-5AF97423BD9D

list pairsの表示でも、引数の順序に関わらずWatch:/Phone:欄には正しいデバイスが振り分けられる。

既にペア済みの組み合わせは再度ペアリングできない

同じWatchとiPhoneの組み合わせを指定すると、引数の順序を問わずエラーになる。

$ xcrun simctl pair "TIL Pair Watch" "TIL Pair iPhone"
An error was encountered processing the command (domain=com.apple.CoreSimulator.SimError, code=403):
Unable to pair devices
The selected devices are already paired with each other.

互換性のないデバイスはペアリングできない

Apple WatchシミュレータをiPad等、Watchとペアリングできないfamilyのデバイスと組み合わせると、明確なエラーになる。

$ xcrun simctl pair "TIL Pair Watch 2" "TIL Pair iPad"
An error was encountered processing the command (domain=com.apple.CoreSimulator.SimError, code=403):
Unable to pair devices
At least one of the requested devices does not support pairing.

同じiPhoneに複数のWatchをペアリングできる

1台のiPhoneに、別のWatchをさらにペアリングできる。

$ xcrun simctl pair "TIL Pair Watch 2" "TIL Pair iPhone"
E4E40020-33AF-435F-AD25-A94BC43ACBCD

$ xcrun simctl list pairs
== Device Pairs ==
8EC5FE87-61ED-4C18-93F2-5AF97423BD9D (active, disconnected)
    Watch: TIL Pair Watch (50B08633-BDF4-4C70-BD17-50257392B874) (Shutdown)
    Phone: TIL Pair iPhone (8E084815-20C6-4D0C-85CB-6BB0B3922E07) (Shutdown)
E4E40020-33AF-435F-AD25-A94BC43ACBCD (inactive, disconnected)
    Watch: TIL Pair Watch 2 (EDF63674-B45E-4B59-AD65-5BCD5BCE9EC1) (Shutdown)
    Phone: TIL Pair iPhone (8E084815-20C6-4D0C-85CB-6BB0B3922E07) (Shutdown)

先に作成したペアはactiveのままで、後から追加したペアはinactiveとして作成される。 実機のApple Watchと同様、1台のiPhoneに対して実際に連携する(active)ペアは常に1つだけである。

bootにペアのUUIDを指定するとWatchとiPhoneをまとめて起動できる

bootはデバイス単体だけでなく、ペアのUUIDも受け付ける。

$ xcrun simctl boot "8EC5FE87-61ED-4C18-93F2-5AF97423BD9D"

$ xcrun simctl list pairs
== Device Pairs ==
8EC5FE87-61ED-4C18-93F2-5AF97423BD9D (active, disconnected)
    Watch: TIL Pair Watch (50B08633-BDF4-4C70-BD17-50257392B874) (Booted)
    Phone: TIL Pair iPhone (8E084815-20C6-4D0C-85CB-6BB0B3922E07) (Booted)

起動直後はdisconnectedのままだが、しばらく待つとactiveなペアの状態がconnectedに変わる。

$ xcrun simctl list pairs
== Device Pairs ==
8EC5FE87-61ED-4C18-93F2-5AF97423BD9D (active, connected)
    Watch: TIL Pair Watch (50B08633-BDF4-4C70-BD17-50257392B874) (Booted)
    Phone: TIL Pair iPhone (8E084815-20C6-4D0C-85CB-6BB0B3922E07) (Booted)

unpairは起動中でも実行できる

参考: xcrun simctl cloneでiOSシミュレータデバイスを複製する

参考: xcrun simctl eraseでiOSシミュレータデバイスの内容を初期化する

cloneeraseは、対象デバイスが起動中だとエラーになる。 一方unpairは違う。 Watch・iPhoneが起動中(Booted)のままでもエラーにならず解除できる。

$ xcrun simctl unpair "8EC5FE87-61ED-4C18-93F2-5AF97423BD9D"

$ xcrun simctl list pairs
== Device Pairs ==
E4E40020-33AF-435F-AD25-A94BC43ACBCD (inactive, disconnected)
    Watch: TIL Pair Watch 2 (EDF63674-B45E-4B59-AD65-5BCD5BCE9EC1) (Shutdown)
    Phone: TIL Pair iPhone (8E084815-20C6-4D0C-85CB-6BB0B3922E07) (Shutdown)

-jでペア情報をJSON形式で取得する

list -j pairsで、ペアのUUIDをキーとしたJSON形式で取得できる。

$ xcrun simctl list -j pairs
{
  "pairs" : {
    "E4E40020-33AF-435F-AD25-A94BC43ACBCD" : {
      "watch" : {
        "name" : "TIL Pair Watch 2",
        "udid" : "EDF63674-B45E-4B59-AD65-5BCD5BCE9EC1",
        "state" : "Shutdown"
      },
      "phone" : {
        "name" : "TIL Pair iPhone",
        "udid" : "8E084815-20C6-4D0C-85CB-6BB0B3922E07",
        "state" : "Booted"
      },
      "state" : "(inactive, disconnected)"
    }
  }
}

CIなどでスクリプトからペアの状態を判定する際は、テキスト出力を正規表現でパースするより-jを使いjqで該当ペアのstateを取り出す方が確実である。

活用例: watchOSアプリのCI検証環境を用意する

watchOSアプリをXCTestで自動テストする場合、単体のWatchシミュレータだけでなく、Companion appをインストールしたiPhoneシミュレータとのペアが必要になることがある。 CI実行時にcreateでiPhone・Watchを作成し、そのつどpairでペアを組んでからテストを実行すれば、実機の手動ペアリングなしにwatchOS連携込みのテストを再現できる。