limrun-ios-simulator について
limrun-ios-simulatorは、LimrunのクラウドiOSシミュレーター上のアプリを操作するスキルです。Linux、Windows、macOS、VM、コンテナからインストール済みアプリをテストでき、Duoの折りたたみ操作、宛先トンネル、HTTP通信の検査にも対応します。
主な機能
- 01クラウドシミュレーターの作成と接続
- 02タップ、入力、スクリーンショット
- 03iPhone Duoのヒンジと画面の操作
- 04ローカルまたはプライベートサービスへのトンネル
- 05HTTP通信の検査とHAR出力
活用シーン
- 01リモートシミュレーターでビルド済みアプリを確認
- 02iPhone Duoの内外画面を自動操作
- 03シミュレーターから開発環境のサービスへ接続
- 04調査用にアプリの通信を記録
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ limrun-inc/ skills --skill limrun-ios-simulatorインストール後、Claude Code で/limrun-ios-simulatorを入力して読み込まれたか確認します。
詳細
Limrun iOS Simulator
Linux、Windows、macOS、VM、コンテナなど、あらゆる環境から Limrun クラウド iOS シミュレーター上で実行中のアプリを操作できます。このスキルはビルド方法に依存しません。ビルドスキル(Bazel の場合は limrun-bazel、xcodebuild の場合は limrun-xcode)によって、アプリがすでにビルドされ、インストールされていることを前提としています。ビルドに関する処理はそれらのスキルに任せ、このスキルでは実行中のシミュレーターを操作します。
ローカルの Xcode、ローカルのシミュレーター、ローカルの macOS ツールは決して使用しないでください。
認証と CLI
必要に応じてインストールします:npm install --global lim。認証には lim login または LIM_API_KEY を使用します(.env やシェルに表示されなくても、ユーザーの環境にすでに設定されている場合があります。ユーザーに確認を求める前に確認してください)。CLI を信頼できる情報源として使用してください。このスキルに記載されているコマンドは検証済みですが、フラグでエラーが発生した場合や、ここに記載されていないフラグが必要な場合は、推測せず lim ios <subcommand> --help を確認してください。
アプリバンドルのインストール
Limrun のリモート Bazel サービスまたは Xcode サービスを使ってアプリバンドルをビルドし、シミュレーターに自動インストールできます。または、ビルド済みのローカル .ipa ファイルや .app フォルダーをシミュレーターに同期できます。重要なのは、シミュレーター向けにビルドされていることです。
ビルドとインストール
通常、ビルドスキルによってシミュレーターが割り当てられます(lim xcode rbe --ios、または lim xcode build . を実行してから割り当てます)。すでに割り当てられているか確認してください:
lim xcode get # is a simulator attached to the current build target?
lim ios list # all running iOS instances
割り当てられていない場合は、新しく作成してください。最後にビルドしたものがすぐにインストールされるため、再ビルドは不要です:
lim ios create --attach
作成コマンド(または lim xcode rbe --ios)の出力に署名付きストリーム URL が含まれている場合は、[Live simulator](<signed-stream-url>) のような Markdown リンクとしてユーザーに共有してください。ユーザーが見られるブラウザーがあれば、そこで URL を開いて伝えてください。そうでない場合は、create に --no-open を渡してください。ローカルで URL を開かず、共有用の URL は引き続き出力されます。
lim xcode get は代わりに Limrun コンソールの URL を出力します。同じライブビューが開きますが、コンソールへのログインが必要なため、共有には署名付きストリーム URL を優先してください。コンソール URL しかない場合は、それを共有し、ログインが必要であることを伝えてください。
署名付きリンクを開くと、ログインなしで共有デバイスのワークスペースにアクセスできます。フルインスタンストークンでは、Inspect、Connect、およびログ、イベント、ネットワークインスペクターが表示されます。ストリーム専用トークンでは、それらのツールは表示されず、画面と入力コントロールのみが表示されます。停止とアプリの選択には、認証済みのコンソールが必要です。
ローカルからアプリをインストール
新しいシミュレーターを作成します:
lim ios create
署名付きストリーム URL を、[Live simulator](<signed-stream-url>) のような Markdown リンクとしてユーザーに共有してください。ユーザーが見られるブラウザーがあれば、そこで URL を開いて伝えてください。
次のコマンドを実行して、ローカルからバンドルをアップロードできます:
lim ios sync <path to .ipa file or .app folder>
新しいバージョンのバンドルをインストールするたびに、同じコマンドを実行できます。差分を適用し、シミュレーターで再読み込みします。
iPhone Duo を折りたたむ
Duo を利用できるリージョンでインスタンスを作成します。
lim ios create --model iphone-duo
lim ios fold --json --id <instance-ID>
lim ios fold 90 --orientation landscape-left --id <instance-ID>
lim ios fold 90 --id <instance-ID>
lim ios fold 180 --id <instance-ID>
lim ios screenshot ./inner.png --display inner --id <instance-ID>
lim ios tap 300 200 --display inner --id <instance-ID>
ヒンジは、**0°(閉じた状態)から180°(平らな状態)**までの小数角度を受け付けます。
角度を省略すると、折りたたみ状態を読み取ります。--orientation は portrait、pud
(上下逆のポートレート)、landscape-left、または landscape-right を受け付けます。向きは
ヒンジ角度とは独立して変更できます。
これによりシミュレーターのネイティブなヒンジが変更されるため、アプリは Apple のヒンジおよび
レイアウトの更新を受け取ります。ブラウザのストリームは 2D で開始し、必要に応じて読み込まれる 3D
フレームを表示できます。どちらのモードにもヒンジと回転のコントロールがあり、カバーと内側の
ディスプレイでタッチ入力を利用できます。フレームの
スリープ/ウェイクボタンと音量ボタンは、位置がロックされていてもクリックや長押しを受け付けます。
自動化する場合は、client.performActions で button を
side、volumeUp、または volumeDown に設定した buttonDown アクションと buttonUp アクションを組み合わせます。
すでに接続済みの TypeScript デバイスクライアントがある場合:
const fold = await client.getFoldState(); // null on an ordinary simulator
await client.setHingeAngle(110);
await client.setDuoOrientation('landscape-left');
const inner = await client.screenshotDisplay('inner');
await client.tapDisplay('inner', inner.width / 2, inner.height / 2);
setDuoOrientation は portrait、landscape-left、landscape-right、
および pud(上下逆)を受け付けます。ディスプレイのスクリーンショットは正立した状態で、
寸法はポイント単位で報告されます。tapDisplay はその座標を使用します。カバーには outer を、
展開するディスプレイには inner を使用します。iOS によって電源がオフにされたディスプレイは、
黒い画像を返します。
Duo の自動化には、ディスプレイ専用のこれらのメソッドを使用してください。既存のスクリーンショット、 録画、およびアクセシビリティの各コマンドは、内側のディスプレイに自動的には追従しません。3D ビューアーは 1 本指でのタッチとドラッグに対応しています。表示を回転するとカメラが変わり、デバイスを回転すると ネイティブの向きが変わります。ラップトップ表示 はヒンジと向きを設定しますが、Apple の独立したテーブルモードは有効にしません。
適切なインスタンスを指定する
ほとんどの lim ios コマンドは、最後に作成されたインスタンスをデフォルトで使用し、cwd の
Git リポジトリ/ワークツリーから「現在の」インスタンスを特定します。そのため、シミュレーターが
接続されていて lim xcode get に表示されていても、cwd がインスタンスを作成した Git ワークツリーでは
ない場合(または Git リポジトリでない場合)、lim ios コマンドは
No instance ID provided and no recent ios instance found と報告することがあります。新しいプロジェクトで
lim xcode rbe --ios を実行した直後に、よくこの問題が発生します。
その場合は、次の方法が確実です。
lim xcode get # shows the attached simulator's ID
lim ios element-tree --id <that-id> # pass --id to EVERY lim ios command
lim xcode get は、接続されたシミュレーターの ID を確認するための確実な情報源です
(lim ios list でも確認できます)。ID がわかったら、セッションの残りの間はすべての lim ios
呼び出し(スクリーンショット、タップ、入力、要素ツリー、録画)に --id <ios-instance-id> を渡します。
または、プロジェクトで git init を実行すると、ワークスペースが自動的に特定されます。複数のインスタンスを
操作する場合は、必ず --id を渡してください。
ローカルマシン上のサービスへの接続
宛先トンネルを使うと、iPhone シミュレーターのアプリは通常の宛先への接続を続けながら、CLI が lim を実行しているマシンからその宛先に接続できます。localhost:port またはリテラルの IP:port を完全一致で指定するか、お使いのマシンまたは VPN からのみ到達できるドメインを選択します。
lim ios tunnel \
--id <ios-instance-id> \
--selector localhost:3000 \
--selector localhost:8081 \
--selector "*.staging.example" \
--detach
http://localhost:3000 など、アプリで通常使う URL を使用します。localhost:3000 を指定すると、127.0.0.1:3000 や [::1]:3000 などのループバック形式に加え、[::ffff:127.0.0.1]:3000 も捕捉されます。ドメインセレクター(api.corp.example の完全一致、またはラベル境界に一致するワイルドカード "*.staging.example")はシミュレーター上でインターセプトされ、その名前がパブリック DNS で解決できるかどうかにかかわらず、マシンから接続されます。そのため、DNS と VPN が適用されます。アプリが HTTPS 経由で独自に DNS を解決する場合、ドメインのインターセプトは回避されます。
トンネルが転送するのは TCP のみです。完全一致セレクターは最大 10 個、ドメインセレクターは最大 64 個まで指定でき、ポートは 53 を除く 1~65535 です。CIDR と UDP はサポートされません。アプリの起動またはページを開く前にトンネルを開始してください。先に確立された接続は元の経路を使い続けるため、先に接続していた場合はアプリを再起動してください。
1 つのインスタンスで有効にできる宛先トンネルは 1 つだけで、セレクターセットは変更できません。宛先を追加または削除するには、トンネルを停止し、セレクターの完全なリストを指定して再起動します。
lim ios tunnel status --id <ios-instance-id> --json
lim ios tunnel stop --id <ios-instance-id>
シミュレーターがローカルサービスの停止中に経路への接続を試みても、トンネルは有効なままで、connection_refused が報告されます。シミュレーターやトンネルを作り直さずに、サービスを再起動してください。
HTTP トラフィックの検査、HAR の取得、ネットワークログの保存
検査はデフォルトで有効です。トンネルを通過するすべての HTTP および HTTPS リクエストがデコードされ、リクエストごとに 1 行の概要が出力されます(デタッチ時はトンネルのログファイルに出力されます)。また、コンソールのネットワークパネルにリアルタイムで表示されます。
lim ios tunnel --selector "*.api.example" --har ./traffic.har --detach # write HAR 1.2 with bodies
lim ios tunnel --selector "*.api.example" --persist --detach # network log survives the instance
--persist を指定すると、トンネルの停止時またはインスタンスの終了時に、本文を含むネットワークログがセッションアーティファクトとしてアップロードされます。コンソールのインスタンスのセッションページに表示され、HAR をダウンロードできます(デフォルトの保存期間は 3 日間、--ttl <seconds> で最大 30 日間)。HTTPS はシミュレーターが信頼する CA を使ってデコードされるため、証明書ピンニングを行うアプリは、検査対象のセレクターを経由すると失敗します。ピンニング対象のホストをセレクターに含めないか、TLS をエンドツーエンドで維持するために --no-inspect を指定してください(概要、HAR、保存は行われません)。
プライベートトンネル経由でプライベートネットワーク内のサービスに接続する
組織はプライベートトンネルを実行できます。これは、管理者が lim tunnel create またはコンソールの Network で作成し、lim tunnel run で実行するコネクターで、名前を指定したすべてのインスタンスにサービスを提供します。ユーザーがトンネル名を指定した場合は、自分でトンネルを開始するのではなく、インスタンスの作成時にその名前を渡します。
lim ios create --tunnel <tunnel-name>
アプリは、localhost:3000 や *.internal.example などのトンネルのセレクターに、そのアドレスで接続します。これらのアドレスはお使いのマシンではなく、コネクターのマシン上で解決されます。このトンネルはインスタンスの宛先トンネルを 1 つ使用するため、そのインスタンスで lim ios tunnel を開始または停止しないでください。
作成時にトンネルの使用可否が確認され、使用できない場合は作成に失敗します。
tunnel <name> does not exist:名前が間違っているか、トンネルが作成されていません。正しい名前をユーザーに確認してください。tunnel <name> is offline:コネクターが実行されていません。起動するようユーザーに依頼してください。did not attach within 30s:コネクターがインスタンスに接続できません。コネクターの出力を確認するようユーザーに依頼してください。
いずれの場合も、作成をループで再試行しても解決しません。
トンネルのトークンはコネクターのマシンに置いてください。ユーザーに会話へ貼り付けるよう求めてはいけません。
すべてのデバイス通信をプロキシ経由にする
トンネルが転送するのは、宛先として宣言されたものだけです。すべての通信を対象にする包括的なセレクターはありません。ユーザーが Safari を含むすべての通信をプロキシ(たとえば、通信記録用プロキシ)経由で送信したい場合は、シミュレーターのシステムプロキシを、トンネルが到達できるプロキシに設定します。トンネルの検査では、プロキシ宛てのリクエストは送信されたものとして通過します。
lim ios tunnel --selector localhost:3128 --detach --id <ios-instance-id>
lim ios proxy set localhost:3128 --id <ios-instance-id>
# later, before stopping the tunnel:
lim ios proxy clear --id <ios-instance-id>
セレクターをプロキシに設定したプライベートトンネルでは、lim ios proxy set だけで十分です。ユーザーのネットワーク内の別の場所にあるプロキシでも同様に動作します。
--selector proxy.example.internal と lim ios proxy set proxy.example.internal:3128 を指定します。localhost 宛てのリクエストは直接送信されます。新しい接続にはすぐにプロキシが使われます。アプリは、再起動するまで、起動前に開いた接続を維持します。lim ios proxy clear を実行すると、以前の設定に戻ります。
CA 証明書を信頼する
HTTPS 通信をプロキシが読み取れるように、PEM CA 証明書(たとえば、ユーザーの通信記録用プロキシの CA)をシミュレーターの信頼済みルートに追加します。
lim ios ca add ./proxy-ca.pem --id <ios-instance-id>
Safari とアプリは再起動することなく、インスタンスが存続する間、次回の接続からこの証明書を信頼します。ファイルごとに追加できる CA 証明書は 1 つだけです。リーフ証明書は拒否されます。証明書をピン留めするアプリと、シミュレーターの Apple サービスは、引き続きこの証明書を拒否します。ユーザー自身のプロキシを使わずにアプリの通信を確認するには、代わりに上記のトンネル検査を使用します。
アプリの起動
ビルドスキルはビルドが成功するたびにアプリを再インストールして再起動するため、通常は自分で起動する必要はありません。アプリが閉じている場合(古いビルドへの新しい接続時、または終了させた後)は、バンドル ID を指定して起動します。
lim ios launch-app <bundle-id> # foregrounds it if already running
lim ios launch-app <bundle-id> --mode RelaunchIfRunning # restart for a clean state
lim ios launch-app <bundle-id> --env API_URL=https://api.example.com --env FEATURE_FLAG=1
lim ios terminate-app <bundle-id> # stop it, e.g. to reset app state
バンドル ID がわからない場合は、lim ios list-apps を実行します。
--env KEY=VALUE は繰り返し指定でき、変数を反映するためにアプリが再起動されます。変数とともに --mode ForegroundIfRunning を明示的に指定することはできません。空の値を指定するには KEY= を使います。同じキーを複数回指定した場合は、最後の値が有効になります。管理対象の Detox 起動では、--runtime detox と --env を組み合わせられます。
launch-app はアプリのログをストリーミングし、アプリが終了するか、コマンドが中断されると戻ります。起動後すぐに戻るには --detach を渡します。アプリが終了すると、launch-app は終了理由(crash、exit、terminated)を出力します。クラッシュの場合は、例外とシグナル、クラッシュメッセージ(たとえば、Swift の Fatal error 行や、捕捉されなかった例外の理由)、アプリ独自の関数名を含むクラッシュスレッドのフレームも出力されます。このレポートでアプリが終了した理由を確認できます。フレームにはファイル名と行番号が含まれないため、ビルドの dSYM が必要です(レポートの Binary Images 行にあるアドレスを使って atos -l <load address> を実行します)。
React Native または Expo の赤いエラー画面は終了ではありません。アプリのプロセスは実行を続けるため、何も報告されません。TypeScript SDK では、launchApp の onExit(logs, info) が同じレポートを受け取ります。また、monitorApp(bundleId, { onExit }) は、たとえば lim ios open-url で開いたアプリや、テストランナーによって再起動されたアプリなど、自分で起動していないアプリの終了を stop() が呼び出されるまで毎回報告します。次の終了のみを報告する watchApp は非推奨です。
アプリの実行中、launch-app はメインスレッドのハングもそれぞれ出力します。メインスレッドが 1 秒間応答しないとハングが始まり、各レポートには経過時間、スレッドの状態と CPU 時間、スタックの先頭が表示されます。CPU 時間が経過時間に近い running は、アプリがメインスレッドをビジー状態にしていたことを意味します。CPU 時間が少ない waiting は、アプリが待機していたことを意味し、スタックの先頭で待機先を確認できます。ハングが終わると、その継続時間が表示されます。画面の読み取りではメインスレッドを待つため、ハングの情報から、アプリがフリーズする原因や自動化の呼び出しが停止する原因を把握できます。TypeScript SDK では、launchApp の onHang が同じレポートを受け取ります。また、monitorApp(bundleId, { onHang }) は、再起動をまたいでアプリのすべてのハングを stop() が呼び出されるまで報告します。
アプリのログ
ログコマンドは直近の行を出力して終了します。中断するまでストリーミングを続ける場合にのみ、--follow を渡してください。
lim ios launch-app <bundle-id> --detach # launch and return, no log stream
lim ios app-log <bundle-id> # last 100 lines, then exit
lim ios app-log <bundle-id> --tail 100 # last N lines
lim ios app-log <bundle-id> --follow # stream until interrupted
lim ios syslog # up to 100 captured syslog lines, then exit
lim ios syslog --follow # stream simulator syslog
Syslog スナップショットは、現在または以前の syslog ストリームによって蓄積されたバッファを読み取ります。バッファが空の場合、行は返されません。スナップショットの読み取りには、syslogTail をサポートするランタイムが必要です。
変更のテスト
シミュレーターとのやり取りが作業に含まれる場合は、ビルドのたびにインタラクションコマンドを使って、新規または変更した機能をテストしてください。変更点を中心に確認し、主要なフローも簡単にスモークテストします。操作を行う前に、まず画面に表示されているものを確認するため、要素ツリーを読み取ってください。
lim ios element-tree
アプリとのやり取り
アクセシビリティ ID、ラベルの順にタップを試し、座標でのタップは最後の手段として使用してください。
lim ios tap-element --ax-unique-id startButton
lim ios tap-element --ax-label "Save"
lim ios tap 201 450
tap-element は、実際のタッチイベントを合成してタップします。アクセシビリティツリーから確認できる要素は、自動的に画面内にスクロールされます。ツリー内で一致するものがないセレクターは、約 1 秒で失敗します。iOS ではリストの行は遅延生成されるため、画面の下方にある行はツリーに存在しないことがよくあります。そのような場合は --scroll-search を渡してください。CLI が画面をページ単位でスクロール(数ページ下へ移動してから上へ戻る)し、行が現れるまでタップを再試行します。これには約 10 秒かかることがあります。タッチではなくアクセシビリティのプレスを使うには、--activate ax を渡してください(スクロールは行わず、使用可能なフレームがない要素でも動作します)。
ツールバーやナビゲーションバーの項目は、通常 ID ではタップできません。 SwiftUI はツールバーの子要素を 1 つのナビゲーションバーグループにまとめます。そのため、.accessibilityIdentifier(...) を設定していても、それらの項目は AXUniqueId: null を返します(通常のコンテンツ内の Button では ID が公開されます)。したがって、tap-element --ax-unique-id ではナビゲーションバーのボタンが見つかりません。ドキュメント用に .accessibilityLabel / .accessibilityIdentifier は設定しておきますが、実際にタップするには、要素ツリーから AXFrame を読み取り、座標で中央をタップしてください。
lim ios element-tree --id <id> | grep -i -A6 -B2 moon # find the item's AXFrame
lim ios tap <x> <y> --id <id> # tap the frame's center
テキスト入力では、まずフィールドをタップしてフォーカスし、その後に入力してください。
lim ios type "hello world" # real key events; errors if no field is focused
lim ios type "hi" --no-require-focus # skip the focus check: for fields focused by coordinate taps when the accessibility focus scan is unreliable
lim ios press-key backspace
lim ios press-key @ # shifted symbols work directly
type は実際のキーを押すため、テキストデリゲートが呼び出され、フィールド固有のキーボード動作が適用されます(たとえば、デフォルトのテキストフィールドでは先頭の文字が大文字になります)。キーボード動作を適用せずに値をそのまま設定するには、set-text を使用してください。
lim ios set-text "P@ssw0rd!" --focused # into the focused field
lim ios set-text "hello" --ax-unique-id emailField # by selector
スクロールやドラッグには、以下を使用してください。
lim ios scroll down --amount 300 # from the screen center
lim ios scroll down --amount 300 --coordinate 200,400 # from a specific point
lim ios swipe --from 200,600 --to 200,200 # explicit drag; --duration 800 for a slower, precise one
操作のたびに element-tree を再実行して、UI が遷移したことを確認してください。タップと element-tree の間に待機する必要はありません。タップは完了するまでブロックされます。
lim ios element-tree
perform を使うと、複数のアクションを正確なタイミングで連続実行できます。
lim ios perform --action type=tap,x=100,y=200 --action "type=typeText,text=Hello World"
lim ios perform --action type=wait,durationMs=1000 --action type=pressKey,key=enter
lim ios perform --file ./actions.yaml
アクションの文法全体については、lim ios perform --help を実行してください。
スクリーンショットと動画
スクリーンショットの出力先は位置引数のパスで指定します(-o ではありません)。
lim ios screenshot screenshot.png
lim ios screenshot screenshot.png --id <ios-instance-id>
lim ios screenshot app-store.png --format png --size full --id <ios-instance-id>
アルファチャンネルなしのフル解像度 PNG を取得するには、ストアへのアップロード用スクリーンショットも含め、--format png --size full を使用してください。Duo では --display inner または --display outer と組み合わせます。SDK で同等の処理を行うには client.screenshot({ format: 'png', size: 'full' }) を使用します。これは Base64 PNG データとピクセル寸法を返します。--format は jpeg または png を受け付け、デフォルトは jpeg です。--size は scaled または full を受け付け、デフォルトは scaled です。これらはそれぞれ個別に選択してください。scaled はポイント単位の寸法を使用し、full はネイティブのピクセル解像度を維持します。タップ座標はポイント単位のままなので、ネイティブ解像度のスクリーンショットから測定した座標には tapWithScreenSize を使用してください。
機能のアサーション(要素の存在、ラベル、状態の変化)には要素ツリーを使い、スクリーンショットは視覚的な特性の確認にのみ使用してください。動き(アニメーション、ゲームプレイ、ストリーミング UI)を伴う場合は、動画を優先してください。
lim ios record start # non-blocking
lim ios record stop -o /tmp/recording.mp4
UI の変更では、プルリクエストにデモ動画を含めて、ユーザーが変更内容を確認できるようにしてください。
アプリのコンテナ内のファイル
ファイルを取得する前にアプリのデータコンテナを一覧表示し、アプリが作成した正確なパスを使用してください。一覧表示、取得、アップロード、削除では、同じ --bundle-id フラグと --container-type フラグを使用します。
lim ios ls Documents --bundle-id com.example.app --container-type data
lim ios pull-file Documents/recording.mov ./recording.mov \
--bundle-id com.example.app --container-type data
lim ios push-file ./fixture.json Documents/fixture.json \
--bundle-id com.example.app --container-type data
lim ios delete-file Documents/fixture.json \
--bundle-id com.example.app --container-type data
lim ios ls は、デフォルトでステージングフォルダーのルートを対象とします。--bundle-id を指定すると、デフォルトでアプリのバンドル(--container-type app)が対象になります。アプリの書き込み可能な Documents、Library、tmp ディレクトリを対象にするには、data を使用してください。ls の出力にあるパスは、選択したルートからの相対パスで、ほかのファイルコマンドにそのままコピーできます。
動画を使ってカメラをシミュレートする
カメラを使うフロー(QRコードのスキャン、書類の撮影、ビデオ通話)では、ローカルの動画ファイルをシミュレーターのカメラとして再生できます。アプリは通常のキャプチャーパイプラインを通じてフレームを受け取ります。
lim ios camera play ./fixtures/qr-scan.mp4 # loops by default
lim ios camera play ./fixtures/intro.mp4 --no-loop # play once, freeze on last frame
lim ios camera clear # restore the default camera
AVFoundationでデコードできるファイル(.mp4/.mov の H.264/HEVC)であれば、どれでも使用できます。アプリでクリップの終了を正確に1回だけ検出する必要がある場合は、--no-loop を指定してください(再生が停止するのではなく、最後のフレームで映像が止まります)。
クリップボード
シミュレーターのクリップボードに値を設定したり、値を読み取ったりできます。アプリではシミュレーター内でコピーしたテキストと同じように貼り付けられるため、編集メニューの「ペースト」を選んでも許可を求めるプロンプトは表示されません。たとえば、ログインフォームにワンタイムコードを貼り付けるには、次のようにします。
lim ios clipboard set "123456" # or pipe it: echo 123456 | lim ios clipboard set
lim ios clipboard get
lim ios simctl -- pbcopy booted と pbpaste booted でも同じ操作ができます。
ユーザーデフォルト
defaults ツールを使って、シミュレーターのユーザーデフォルトを読み取り、書き込み、削除できます。引数は -- の後に指定してください。アプリは起動時にデフォルトを読み取るため、変更後はテスト対象のアプリを再起動してください。たとえば、UI 自動化を使わずにデバイスの言語と地域を切り替えるには、次のようにします。
lim ios defaults -- write -g AppleLanguages -array fr-FR
lim ios defaults -- write -g AppleLocale -string fr_FR
lim ios defaults -- read -g AppleLanguages
lim ios simctl -- spawn booted defaults ... でも同じ操作ができます。使用できるのは read、write、delete のみです。
Darwin 通知
シミュレーターの notifyutil ツールを使って、通知を投稿したり、通知の状態を設定・読み取りしたりできます。
lim ios notify post <name>
lim ios notify set <name> <state>
lim ios notify get <name>
たとえば、シミュレーターはこれらの通知を通じて Face ID を操作します。まず一度登録し、その後は Face ID の各プロンプトに対して、認証成功または認証失敗を返してください(Touch ID 搭載デバイスでは、pearl の代わりに fingerTouch を使用します)。
lim ios notify set com.apple.BiometricKit.enrollmentChanged 1
lim ios notify post com.apple.BiometricKit.enrollmentChanged
lim ios notify post com.apple.BiometricKit_Sim.pearl.match # the next scan succeeds
lim ios notify post com.apple.BiometricKit_Sim.pearl.nomatch # the next scan fails
lim ios simctl -- spawn booted notifyutil -p|-s|-g ... でも同じ操作ができます。
人が開くためのプレビュー URL
.ipa ファイル、または .app フォルダーの targz アーカイブを Limrun Asset Storage に直接アップロードし、ユーザーがブラウザーで開いてアプリを手動でテストできるプレビュー URL を返します。
アップロード方法は次のとおりです。
export ASSET_NAME=myapp.tar.gz # can be any name
lim asset push ${ASSET_NAME}
echo "https://console.limrun.com/preview?asset=${ASSET_NAME}&platform=ios"
コマンドの実行が完了したら、ユーザーに次の URL を渡してください。ユーザーがクリックするとシミュレーターのストリームが開き、その間にコンソールでこのバンドルがインストールされます。
https://console.limrun.com/preview?asset=${ASSET_NAME}&platform=ios
機密情報ではない iOS アプリの設定を渡すには、URL エンコードした env=KEY=VALUE パラメーターを複数追加できます。たとえば、&env=FEATURE_FLAG%3D1&env=API_URL%3Dhttps%3A%2F%2Fapi.example.com のように指定します。リンクを作成するときは、URLSearchParams.append("env", "KEY=VALUE") を使用してください。アプリは初回起動時にこれらの変数を受け取ります。値はリンクとブラウザーの履歴に表示されます。
起動後に URL またはアプリのディープリンクを開くには、openUrl を追加します。たとえば、&openUrl=myapp%3A%2F%2Fcheckout のように指定します。URL 全体とクエリ文字列をエンコードするには、URLSearchParams.set("openUrl", url) を使用してください。これは env の有無にかかわらず動作し、アプリのインストール、起動、URL のオープン中もストリームは表示されたままです。インストールの進行状況を示す Installing <asset name> オーバーレイは、プレビューの準備が整うと消えます。
クリーンアップ
作業が完了したら、iOS シミュレーターを削除できます。
lim ios delete
注意点
- Git ディレクトリ以外では、インスタンスの解決に失敗することがあります。 上記の「適切なインスタンスを指定する」を参照してください。迷った場合は
--idを指定してください。 element-treeは大きくなることがあります。 ツリー全体をコンテキストに出力するのではなく、grep/jqを通して必要な情報を抽出してください。type/perform typeTextでは、SwiftUI(または React Native)の状態を更新できないことがあります。 テキストの自動入力では、アクセシビリティ経由でフィールドの値を設定します。これは、実際のキー入力のように SwiftUI の@Binding/onChangeを常に発火させるわけではありません。症状として、テキストはフィールド(およびelement-tree)に表示されるものの、それに連動するリアクティブ UI が更新されず(送信ボタンが無効のまま、文字数カウンターが動かないなど)、送信ハンドラーが空の状態を受け取ることがあります。ライブストリームで実際のキーボードを使えば動作します。自動化する場合は、リアクティブな状態に連動するテキストに頼るのではなく、タップ可能なコントロール(ボタンや候補チップ)から送信するか、アプリにテスト用の操作手段を用意してください。- ツールバー/ナビゲーションバーの項目は、ID でタップできません。 上記の「アプリの操作」を参照してください。
element-treeからAXFrameを読み取り、座標を指定してタップしてください。 - バンドル ID の確認。 バンドル ID がわからない場合は、インストールに成功した後で
lim ios list-appsを実行してください。 - ビルドエラーはビルドスキルの担当です。 アプリをインストールできない場合、失敗の原因は上流にあります。
limrun-bazel/limrun-xcodeに戻ってください。