ios-simulator-skill について
ios-simulator-skill は、シミュレーター上の iOS アプリをビルド、テスト、自動化するスクリプト集です。画面座標ではなくアクセシビリティ情報で操作するため、開発者やテストエージェントに適しています。
主な機能
- 01Xcode のビルドとテスト実行
- 02アクセシビリティ情報による画面操作
- 03外観、位置情報、メディアの制御
- 04アクセシビリティ監査と画像差分
- 05テスト記録とデバッグ用スナップショット
活用シーン
- 01iOS プロジェクトのビルドとテスト
- 02ラベルやアクセシビリティ ID による画面操作
- 03位置情報や写真選択機能のテスト
- 04ログと状態スナップショットによる問題調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ conorluddy/ ios-simulator-skill --skill ios-simulator-skillインストール後、Claude Code で/ios-simulator-skillを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
agents/openai.yamlscripts/accessibility_audit.pyapp_launcher.pyapp_state_capture.pyappearance.pybuild_and_test.pyclipboard.pycontainer.pygesture.pyhang_watcher.pykeyboard.pylocalization_audit.pylocation.pylog_monitor.pymedia.pymodel_inspector.pynavigator.pyprivacy_manager.pypush_notification.pyscreen_mapper.pysim_health_check.shsim_list.pyscripts/common/__init__.pycache_utils.pydevice_utils.pyenv_config.pyhang_pipeline.pyhang_sessions.pyidb_utils.pyscreenshot_utils.py詳細
iOS Simulator スキル
ピクセル座標ではなく、アクセシビリティを活用したナビゲーションと構造化データを使用して、iOS アプリケーションのビルド、テスト、自動化を行います。
クイックスタート
# 1. Check environment
bash scripts/sim_health_check.sh
# 2. Launch app
python3 scripts/app_launcher.py --launch com.example.app
# 3. Map screen to see elements
python3 scripts/screen_mapper.py
# 4. Tap button
python3 scripts/navigator.py --find-text "Login" --tap
# 5. Enter text
python3 scripts/navigator.py --find-type TextField --enter-text "[email protected]"
パス: scripts/ は、ユーザーのプロジェクトではなく、この SKILL.md があるディレクトリからの相対パスです。
簡潔にするため、例では scripts/... と記載しています。相対的な Xcode プロジェクトパスを正しく解決できるよう、ユーザーのプロジェクトから絶対パスで実行してください
(例:python3 <skill-dir>/scripts/navigator.py ...)。
すべてのスクリプトで詳細なオプションを確認するには --help を指定します。大半のスクリプトでは、機械可読な出力を得るために --json も使用できます。
ナビゲーション戦略
ナビゲーションには、常にスクリーンショットよりアクセシビリティツリーを優先してください。 アクセシビリティツリーには要素の種類、ラベル、フレーム、タップ対象が含まれており、画像解析よりも低コストで信頼性の高い構造化データを得られます。
次の優先順位で使用してください。
screen_mapper.py→ 構造化された要素の一覧(5~7 行、約 10 トークン)navigator.py --find-text/--find-type/--find-id→ 意味に基づく操作- スクリーンショット → 視覚的な確認、バグ報告、または視覚的な差分の確認に限って使用
スクリーンショットは、サイズに応じて 1,600~6,300 トークンを消費します。アクセシビリティツリーは、デフォルトモードで 10~50 トークンです。
本番環境向けスクリプト 30 個
ビルドと開発(2 個のスクリプト)
-
build_and_test.py - Xcode プロジェクトをビルドしてテストを実行し、段階的に結果を表示します
- ビルド結果をリアルタイムでストリーミング
- xcresult バンドルからエラーと警告を解析
- 必要に応じて詳細なビルドログを取得
- オプション:
--project、--scheme、--clean、--test、--verbose、--json
-
log_monitor.py - インテリジェントなフィルタリングによるリアルタイムのログ監視
- ログのストリーミング、または指定時間分のログを取得
- 重要度(error/warning/info/debug)でフィルタリング
- 繰り返し表示されるメッセージを重複排除
- オプション:
--app、--severity、--follow、--duration、--output、--json
デバイスの状態(3 個のスクリプト)
-
appearance.py - シミュレーターの外観を制御します:ダークモード、Dynamic Type のサイズ、コントラスト、ロケール/地域
xcrun simctl uiでライト/ダークテーマを切り替え- わかりやすいエイリアス(XS~AX5)を使って Dynamic Type のサイズを設定
--contrast high|normalでコントラストを上げる設定を切り替え、読み戻して確認します。--resetでもコントラストが通常に戻ります- ロケールと地域のデフォルト値を設定。
--bundle-idでアプリを再起動することもできます - ar/he/fa/ur/yi ロケールでは RTL を自動的に指定
- オプション:
--theme、--text-size、--contrast、--locale、--region、--reset、--bundle-id、--udid、--json、--verbose
-
location.py - GPS 座標、都市名のプリセット、GPX シナリオの再生をシミュレートします
--lat/--lngで座標を指定するか、--cityで都市を選択--gpx <scenario>で組み込みシナリオ(City Run、Freeway Drive など)を再生--waypointsと--speedで速度を設定して複数の経由地点を含む経路をアニメーション表示--clearでシミュレートした位置情報を消去し、--list-scenariosで利用可能なシナリオを一覧表示- オプション:
--lat、--lng、--city、--gpx、--waypoints、--speed、--clear、--list-scenarios、--udid、--json、--verbose
-
media.py - 写真ピッカーのテスト用に、写真ライブラリに写真を追加、またはライブラリを空にします
xcrun simctl addmediaを使って--add a.jpg b.pngで写真を追加--clear --yesでidb photos clearを使ってライブラリを空にします(idb 1.7+)。--clear --yes --addでは、追加したファイルだけが残ります--yesを指定しない場合、ライブラリに写真があると--clearは実行されません(写真は復元できません)。先にユーザーに確認してください- ファイルが見つからない場合は、すぐに失敗します
- オプション:
--add、--clear、--yes、--udid、--json
ナビゲーションと操作(5 個のスクリプト)
-
screen_mapper.py - 現在の画面を分析し、操作可能な要素を一覧表示します
- 要素タイプの内訳
- 操作可能なボタンの一覧
- テキストフィールドの状態
- ラベル付き要素が画面の10%未満の場合、
Coverage:行を追加して--deepを提案します --deepは idb の axbridge バックエンドを読み取ります(idb 1.7+):Safari/WKWebView のコンテンツや、通常の読み取りで見落とされるアプリ内アラートに加え、ラベルのないコンテナビューも多数検出します。この機能を有効にすると、デバイスのアクセシビリティ自動化モードがオンになります(その後、元には戻りません)。Web コンテンツのフレームは Web ビューを基準とするため、要素の検索には--deepを使用し、タップ座標の取得には使用しないでください- オプション:
--verbose、--hints、--deep、--json
-
navigator.py - 要素を意味的に検索して操作します
- テキストで検索(あいまい検索、または
--find-exactによる完全一致検索)。同じラベルのコンテナより、操作可能なリーフ要素を優先して表示します - 要素タイプで検索
- アクセシビリティ ID で検索
- テキストを入力するか、要素をタップします
- 一意のアクセシビリティ ID またはラベルを持つ Button/Link/Switch/CheckBox/Tab に対して
--tapを指定すると、アクセシビリティプレスを使用します(idb 1.7+)。これにより、フローティング検索バーなどのオーバーレイに操作を妨げられません。出力には(accessibility press)と表示されます。それ以外の場合は、フレームの中心をタップします --set-value TEXTはキーボードを使わずにフィールドの値を置き換えます(任意の文字に対応、idb 1.7+)。フォーカスは移動しないため、その後 Return キーで送信する必要がある場合は--enter-textを使用してください 成功を報告する前に値を読み戻します(セキュアフィールドは対象外)。値が一致しない場合は 1 で終了します--tap --expect VALUEは、要素の値が VALUE の場合にのみタップします(トグルの場合:オンは1、オフは0)。それ以外はタップせずに 1 で終了するため、「X をオフにする」操作を安全に繰り返せます- iOS 27 では、トグルは
CheckBoxとして報告されます。トグルを切り替えるにはプレスが必要です(スイッチ自体に触れる場合も含め、HID タッチでは切り替わりません)。ID もラベルも一意でない場合、出力に値を確認するよう警告が表示されます - アプリがフリーズすると、メインスレッドが応答するまで、すべての読み取りとタップが停止します
- 失敗時には idb の標準エラー出力が含まれます(例:コンパニオンの停止)
- オプション:
--find-text、--find-exact、--find-type、--find-id、--index、--tap、--expect、--tap-at、--enter-text、--set-value、--list
- テキストで検索(あいまい検索、または
-
gesture.py - スワイプ、スクロール、ピンチ、複雑なジェスチャーを実行します
- 方向指定スワイプ(上/下/左/右)—
--swipeは指を動かす方向です - 複数回のスワイプによるスクロール —
--scrollはコンテンツが動く方向です(downで下方のコンテンツが表示されます) idb ui pinchによるピンチズーム(--pinch outで拡大、--scaleで倍率を明示的に指定)idb ui tap --durationによる長押し- 引っ張って更新
--double-tap X,Y [--tap-count N]- 指定した位置を複数回タップします(idb 1.7+)--drag SOURCE DEST- 長押ししてドラッグし、離します。両端はx,yまたはアクセシビリティラベルで指定します。例:--drag "Row 1" "Row 4"(idb 1.7+)--rotate portrait|portrait-upside-down|landscape-left|landscape-right- デバイスを物理的に回転させます(idb 1.7+)--shake- デバイスを振ります。デバッグメニューや取り消し操作に使用します(idb 1.7+)- オプション:
--swipe、--swipe-from/--swipe-to、--scroll、--scroll-amount、--pinch、--scale、--long-press、--duration、--refresh、--double-tap、--tap-count、--drag、--rotate、--shake
- 方向指定スワイプ(上/下/左/右)—
-
keyboard.py - テキスト入力とハードウェアボタンの操作
- テキストを入力します(高速または低速。ASCII のみ)
- 特殊キー(return、delete、tab、space、escape、矢印)、単一の文字、または生の HID コード
- キーシーケンス(
--key-sequence h,i,return) - キーの組み合わせ:
--key z --command --shift(やり直し)。--shift/--control/--optionには idb 1.7+ が必要です - フォーカス中のフィールドをクリアします(Cmd+A、delete)
- ハードウェアボタン:home、lock/power、side、siri、apple-pay、volume-up/down(音量操作には idb 1.6.3+ が必要です)
- オプション:
--type、--slow、--key、--shift、--control、--option、--command、--count、--key-sequence、--button、--clear、--dismiss
-
app_launcher.py - アプリのライフサイクル管理
- バンドル ID でアプリを起動
- アプリを終了
- .app バンドルからインストール/アンインストール(
--uninstallには--yesが必要です。アプリのデータは削除されます) - ディープリンクによるナビゲーション
- インストール済みアプリを一覧表示
- アプリの状態を確認
- 起動/再起動時にアプリへ起動引数(
--args)と環境変数(--env KEY=VALUE、SIMCTL_CHILD_*として挿入)を渡す - オプション:
--launch、--terminate、--restart、--install、--uninstall、--yes、--open-url、--list、--state、--args、--env、--wait-for-debugger
テストと分析(9 スクリプト)
-
accessibility_audit.py - 現在の画面の WCAG 準拠状況を確認
- 重大な問題(ラベルがない、ボタンが空、代替テキストがない)
- 警告(ヒントがない、タッチ対象が小さい)
- 情報(ID がない、ネストが深い)
- オプション:
--verbose、--output、--json
-
visual_diff.py - 2 つのスクリーンショットを比較し、視覚的な変更を確認
- ピクセル単位で比較
- しきい値に基づいて合否を判定
- 差分画像を生成
- オプション:
--threshold、--output、--details、--json
-
test_recorder.py - テストの実行を自動で記録
- 各ステップのスクリーンショットとアクセシビリティツリーを取得
- タイミングデータを含む Markdown レポートを生成
- オプション:
--test-name、--output、--verbose、--json
-
app_state_capture.py - 包括的なデバッグ用スナップショットを作成
- スクリーンショット、UI 階層、アプリのログ、デバイス情報
- バグレポート用の Markdown サマリー
- オプション:
--app-bundle-id、--output、--log-lines、--json
-
sim_health_check.sh - 環境が適切に構成されていることを確認
- macOS、Xcode、simctl、IDB、Python を確認
- 利用可能なシミュレーターと起動済みのシミュレーターを一覧表示
- Python パッケージ(Pillow)を確認
-
model_inspector.py - プロジェクトファイルから Core Data と SwiftData のモデルを調査
- .xcdatamodeld パッケージを解析(エンティティ、属性、リレーションシップ)
- モデルのバージョンと現在有効なバージョンを検出
- SwiftData の
@Modelクラスを可能な範囲で抽出 - 要求に応じて任意のモデルのソースをそのまま出力(
--raw ModelName) - オプション:
--project-path、--core-data-only、--swiftdata-only、--show-versions、--raw、--verbose、--json
-
container.py - アプリのサンドボックスを調査:ファイル、UserDefaults、Core Data ストアのパス
--lsで設定可能な深さのデータコンテナー内のファイルを一覧表示--catでファイルを読み取り、自動検出した plist デコードを適用(大きなファイルはキャッシュ)--userdefaultsで UserDefaults を key=value または JSON として出力--core-data-pathで.sqlite/.sqlite-wal/.sqlite-shmストアの場所を特定--exportでコンテナー全体のスナップショットをエクスポート- オプション:
--ls、--cat、--userdefaults、--core-data-path、--export、--udid、--json、--verbose
-
hang_watcher.py(HangBuster)- os_log のハングイベントを記録し、段階的開示で要約します
- セッションモード(HangBuster、エージェントネイティブ): レコーダーをデタッチして起動し、シミュレーターを操作してから停止し、トークンを節約した要約を取得します
--start→ セッション ID を返します。デタッチされたワーカーがイベントをその場で正規化し、しきい値を適用します--stop SESSION_ID→ 約80~120トークンの L1 要約(ヘッダー + 上位 N 件のクラスター + 詳細確認のヒント)を出力します--get-details SESSION_ID [--cluster N | --raw]→ L2 の全クラスター、または L3 のイベントごとの詳細を表示します--list-sessions/--clear-sessions [--older-than 24h]/--diff A B(セッション間のリグレッションレポート)- フィルターパイプライン:解析 → 正規化 → しきい値適用 → バケット化 → クラスタリング → 集約 → ランク付け → 書式設定(
common/hang_pipeline.py内) --budget-tokens Nは、指定した上限に収まる最も情報密度の高いレベル(L0/L1/L2)を選択します。--terseは L0 を強制します--auto-sampleは、クラスターごとの最初のイベントでメインスレッドのスタックを取得します(ソフト依存:main_thread_sampler.py#62。存在しない場合は何もせずに続行します)
- 生キャプチャモード(
jqでの調査向けに完全な情報を保持): クラスタリングパイプラインをスキップし、一致するログ行をすべてそのままraw.ndjsonに書き出します--start --raw-capture [--max-size-mb 10] [--no-gzip]—log stream --style ndjsonを起動します- セッションごとのサイズ上限(
--max-size-mb、デフォルトは 10)— 上限に達するとワーカーは正常に停止し、extras.truncated=trueが設定されます --stopはraw.ndjsonを gzip 圧縮してraw.ndjson.gzにします(約15~19倍に圧縮。--no-gzipで無効化できます)- 生キャプチャのセッションで
--get-details SESSION_IDを実行すると、zcat | jq ...のヒントとともにパスを表示します
- 堅牢性(ストリーム終了時に自動再起動): EOF またはサブプロセスの終了を検出すると
stream_diedイベントを発行し、2秒のバックオフで上限付き再起動を行います。IOS_SIM_HANG_MAX_RESTARTS(デフォルトは 3)に達すると、セッションはcrashedとしてマークされ、古いrunning状態のまま残ることはありません。--list-sessionsにはcapture=Xsとrestarts=Nが表示されます。 - クリーンアップは自動です: TTL による削除(
IOS_SIM_HANG_SESSION_TTL_HOURS、デフォルトは 24h)と集計容量の上限(IOS_SIM_HANG_TOTAL_CAP_MB、デフォルトは 100 MB、古い順に削除)は、どちらも--startのたびに実行されます。 - レガシーモード(後方互換性のため変更なし):
--watch [--duration N](ライブストリーム)と--since 5m(過去のログ) - フィルター:
--bundle-id(解析後に適用。ハングのキャプチャはシミュレーター全体を対象とするため、RunningBoard/SpringBoard のイベントも保持されます)、--predicate(IOS_SIM_HANG_PREDICATEでも指定できます) - すべての出力は
--jsonに対応しています。セッションは~/.ios-simulator-skill/sessions/<id>/{meta.json,events.jsonl,summary.json,raw.ndjson.gz}に保存されます
クイックスタート(要約モード):
SID=$(python3 scripts/hang_watcher.py --start --min-hang-ms 200) # ... シミュレーターを操作します(シートを開く、スクロールする、画面遷移するなど)... python3 scripts/hang_watcher.py --stop $SID # トークンを節約した L1 要約 python3 scripts/hang_watcher.py --get-details $SID --cluster 1 # クラスター 1 の詳細を確認 python3 scripts/hang_watcher.py --diff $SID_BASELINE $SID # セッション間のリグレッションクイックスタート(生キャプチャ +
jqによる調査):SID=$(python3 scripts/hang_watcher.py --start --raw-capture --max-size-mb 5) # ... シミュレーターを操作します ... python3 scripts/hang_watcher.py --stop $SID # → "Session ...: raw mode, 737 lines, 0.96 MB → 0.05 MB gzipped" # イベント数が多いプロセスの上位: zcat ~/.ios-simulator-skill/sessions/$SID/raw.ndjson.gz \ | jq -s 'group_by(.processImagePath) | map({proc: (.[0].processImagePath | split("/") | last), n: length}) | sort_by(-.n) | .[:5]' # RunningBoard のアサーション無効化イベントをすべて表示: zcat .../raw.ndjson.gz | jq -c 'select(.subsystem == "com.apple.runningboard" and (.eventMessage | startswith("Invalidating")))' # 1分あたりのハング数: zcat .../raw.ndjson.gz | jq -r '.timestamp[:16]' | sort | uniq -c - セッションモード(HangBuster、エージェントネイティブ): レコーダーをデタッチして起動し、シミュレーターを操作してから停止し、トークンを節約した要約を取得します
-
localization_audit.py - 文字列カタログの不足、キーの欠落、プレースホルダーの不一致を検出します
.xcstringsカタログで、ロケールごとに欠落しているキーとneeds_review/newのキーを報告します--sourceを使用して、カタログのキーを Swift ソース(String(localized:)/NSLocalizedString)と照合します- ロケール間のプレースホルダー数(
%d、%@、%s、%lld)の不一致を検出します plistlibによる従来の.stringsと.stringsdictのサポート- CI に適した
--strictは、検出事項があると終了コード 2 を返します - オプション:
--catalog、--source、--locale、--strict、--json、--verbose
高度なテストと権限(4 個のスクリプト)
-
clipboard.py - シミュレーターのクリップボードを管理して貼り付けをテストします
- テキストをクリップボードにコピーします
- 手動入力なしで貼り付けのフローをテストします
- オプション:
--copy、--test-name、--expected、--json
-
status_bar.py - シミュレーターのステータスバーの表示を上書きします
- プリセット:clean(9:41、バッテリー 100%)、testing(11:11、50%)、low-battery(20%)、airplane(オフライン)
- 時刻、ネットワーク、バッテリー、WiFi の設定をカスタマイズできます
- オプション:
--preset、--time、--data-network、--battery-level、--clear、--json
-
push_notification.py - シミュレートしたプッシュ通知を送信します
- シンプルモード(タイトル+本文+バッジ)
- カスタム JSON ペイロード
- 通知の処理とディープリンクをテストします
--listは配信済み通知を表示し、--clearはすべて取り下げます(どちらも idb 1.7 以降。送信は引き続き simctl で行います)- アプリがフォアグラウンドにある間にプッシュ通知を送信しても、通知センターには配信されないため、
--listには表示されません - オプション:
--bundle-id、--title、--body、--badge、--payload、--list、--clear、--json
-
privacy_manager.py - アプリの権限を付与、取り消し、リセットします
- 13 個のサービスに対応しています(カメラ、マイク、位置情報、連絡先、写真、カレンダー、ヘルスケアなど)
- 一括操作(カンマ区切りのサービス)
- テストシナリオの追跡を含む監査証跡
- オプション:
--bundle-id、--grant、--revoke、--reset、--list、--json
シミュレーターの検索(2 個のスクリプト)
-
sim_list.py - 段階的に詳細を表示してシミュレーターを一覧表示します
- デフォルトでは簡潔な概要を表示します(合計/利用可能/起動済み)
- キャッシュ ID を指定すると、必要に応じて詳細を表示します
- デバイスタイプで絞り込みます
--suggestで推奨シミュレーターを提案します- 生の
simctl listと比べてトークン数を 96% 削減します(57k → 2k トークン) - オプション:
--get-details、--suggest、--device-type、--json
-
simulator_selector.py - 用途に最適なシミュレーターを提案します
- 最近の使用状況(
config.jsonから)、最新の iOS、一般的なテストモデル、起動状態に基づいて候補をランク付けします --listですべての利用可能なシミュレーターを一覧表示します--bootで選択したシミュレーターを直接起動します- プログラムから利用できる JSON 出力
- オプション:
--suggest、--list、--boot、--json
- 最近の使用状況(
デバイスのライフサイクル管理(5 スクリプト)
-
simctl_boot.py - 必要に応じて準備完了を確認しながらシミュレーターを起動します
- UDID またはデバイス名で起動します
- タイムアウトを設定して、デバイスの準備完了を待機します
- 一括起動操作(--all、--type)
- パフォーマンス計測
- オプション:
--udid、--name、--wait-ready、--timeout、--all、--type、--json
-
simctl_shutdown.py - シミュレーターを正常にシャットダウンします
- UDID またはデバイス名でシャットダウンします
- シャットダウン完了を必要に応じて確認します
- 一括シャットダウン操作
- オプション:
--udid、--name、--verify、--timeout、--all、--type、--json
-
simctl_create.py - シミュレーターを動的に作成します
- デバイスの種類と iOS バージョンを指定して作成します
- 利用可能なデバイスの種類とランタイムを一覧表示します
- デバイス名をカスタマイズできます
- CI/CD 連携用に UDID を返します
- オプション:
--device、--runtime、--name、--list-devices、--list-runtimes、--json
-
simctl_delete.py - シミュレーターを完全に削除します
- UDID またはデバイス名で削除します
--yesを指定しないと実行されません(確認プロンプトは表示されません。シミュレーターとそのデータは復元できません):先にユーザーに確認してください- 一括削除操作
- スマート削除(--old N で、デバイスの種類ごとに N 台を残します)
- オプション:
--udid、--name、--yes、--all、--type、--old、--json
-
simctl_erase.py - 削除せずにシミュレーターを工場出荷時の状態にリセットします
- デバイス UUID を保持します(削除して再作成するより高速です)
- すべてのシミュレーター、種類別、または起動済みのシミュレーターを消去します
- 必要に応じて確認できます
--yesを指定しないと実行されません(アプリ、データ、設定は復元できません):先にユーザーに確認してください- オプション:
--udid、--name、--yes、--verify、--timeout、--all、--type、--booted、--json
よくあるパターン
UDID の自動検出:ほとんどのスクリプトは、--udid が指定されていない場合、起動済みのシミュレーターを自動検出します。
デバイス名の解決:UDID の代わりにデバイス名(例:「iPhone 16 Pro」)を使用できます。スクリプトが自動的に解決します。
一括操作:多くのスクリプトは、すべてのシミュレーターを対象とする --all や、デバイスの種類を絞り込む --type iPhone に対応しています。
出力形式:デフォルトでは、人が読みやすい簡潔な形式で出力します。CI/CD で機械処理可能な出力を使用するには、--json を指定してください。
ヘルプ:すべてのスクリプトで --help を指定すると、オプションや使用例を詳しく確認できます。
スクリーンショットのサイズ:スクリーンショットはトークンの消費を抑えるためにサイズ変更されます。プリセット:full(3~4 タイル、約 5K トークン)、half(1 タイル、約 1.6K トークン、デフォルト)、quarter(1 タイル、約 800 トークン、詳細度は低め)。すばやく画面を確認する場合は quarter、UI を読みやすく表示する場合は half、ピクセルレベルの詳細が必要な場合に限り full を使用してください。スクリーンショットを取得するスクリプト(app_state_capture.py、test_recorder.py)のデフォルトは half です。
一般的なワークフロー
- 環境を確認します:
bash scripts/sim_health_check.sh - アプリを起動します:
python3 scripts/app_launcher.py --launch com.example.app - 画面を分析します:
python3 scripts/screen_mapper.py - 操作します:
python3 scripts/navigator.py --find-text "Button" --tap - 検証します:
python3 scripts/accessibility_audit.py - 必要に応じてデバッグします:
python3 scripts/app_state_capture.py --app-bundle-id com.example.app
設定
運用上の制限のほとんどは、環境変数で調整できます。デフォルト値は一般的なローカル開発に適しています。処理の遅い CI ランナー、大規模なモノレポのビルド、複雑な画面のアクセシビリティ監査では、値を大きくしてください。
| 変数 | デフォルト | 制御内容 |
|---|---|---|
IOS_SIM_A11Y_LABEL_MAX | 80 | アクセシビリティ監査の出力に保持する AXLabel の最大文字数 |
IOS_SIM_A11Y_TOP_ISSUES | 10 | 監査ごとに表示するアクセシビリティの上位の問題数 |
IOS_SIM_APPS_PREVIEW | 30 | app_launcher.py が切り詰める前に表示するアプリの項目数 |
IOS_SIM_BOOT_SUBPROCESS_TIMEOUT | 60 | simctl boot サブプロセス自体のタイムアウト(秒) |
IOS_SIM_BOOT_TIMEOUT | 300 | 起動後、準備完了を待つタイムアウト(秒) |
IOS_SIM_BUILD_JSON_CAP | 50 | JSON 出力に含めるビルドエラー/失敗したテストの最大数 |
IOS_SIM_BUILD_LOG_PREVIEW | 4000 | デフォルト出力に表示するビルドログのプレビュー文字数 |
IOS_SIM_BUILD_TIMEOUT | 1800 | xcodebuild build の呼び出しを強制終了するまでの最大秒数 |
IOS_SIM_INTROSPECT_TIMEOUT | 60 | xcodebuild -list と simctl list の検索タイムアウト(秒) |
IOS_SIM_TEST_TIMEOUT | 2700 | xcodebuild test の呼び出しを強制終了するまでの最大秒数 |
IOS_SIM_BUILD_SUMMARY_CAP | 15 | デフォルトのビルド概要に含めるエラー/失敗の数 |
IOS_SIM_BUILD_VERBOSE_CAP | 100 | 詳細なビルド出力に含めるエラー/警告の数 |
IOS_SIM_CACHE_MAX_ENTRIES | 500 | 段階的開示キャッシュの最大項目数(LRU 方式で削除) |
IOS_SIM_CACHE_TTL_HOURS | 1 | キャッシュ項目の有効期限 |
IOS_SIM_ERASE_TIMEOUT | 90 | 消去完了を待つタイムアウト(秒) |
IOS_SIM_HANG_PREDICATE | (default) | hang_watcher.py が使用する os_log の述語を上書きします(デフォルトでは RunningBoard による強制終了、「Hang detected」、メインスレッドのハングを検出します)。ハングイベントはシステムデーモン(RunningBoard、SpringBoard)から発生するため、述語はシミュレーター全体に適用されます。--bundle-id は解析後に適用され、AND 条件にはなりません。 |
IOS_SIM_HANG_MIN_MS | 250 | HangBuster のしきい値 — この時間を下回るイベントはディスクに書き込まれません(値が小さいほど感度が高くなり、概要は大きくなります) |
IOS_SIM_HANG_SESSION_TTL_HOURS | 24 | HangBuster セッションのプルーニング対象となる経過時間。プルーニングは --start のたびに実行されます |
IOS_SIM_HANG_DEFAULT_TOP_N | 3 | --stop の L1 出力に表示するデフォルトの上位クラスタ数 |
IOS_SIM_HANG_BUDGET_TOKENS | (unset) | --stop のデフォルトのトークン予算(収まるように L0/L1/L2 を選択します) |
IOS_SIM_HANG_MAX_RESTARTS | 3 | HangBuster ワーカー:EOF/サブプロセスの終了時にセッションが crashed とマークされるまでの、log stream の再起動試行の最大回数 |
IOS_SIM_HANG_TOTAL_CAP_MB | 100 | HangBuster のディスク使用量の合計上限。--start 時にセッション状態の合計がこの値を超えると、古いセッションから削除されます。0 に設定すると無効になります。 |
IOS_SIM_LOG_JSON_CAP | 100 | log_monitor.py の JSON 出力に含めるエラー/警告の最大数 |
IOS_SIM_LOG_LINE_MAX | 300 | ログ概要における各行の切り詰め文字数 |
IOS_SIM_LOG_TAIL | 200 | 詳細/サンプル出力に含めるログ末尾の行数 |
IOS_SIM_LOG_TEXT_SUMMARY | 15 | テキストモードのログ概要に表示するエラー/警告の数 |
IOS_SIM_MAX_ELEMENTS | 25 | navigator.py --list と push_notification.py --list が表示する項目数 |
IOS_SIM_POLL_INTERVAL | 0.5 | 起動/消去状態のポーリング間隔(秒) |
IOS_SIM_RELAUNCH_DELAY_MS | 1000 | app_launcher.py で終了してから再起動するまでの遅延時間 |
IOS_SIM_SCREEN_BUTTONS_PREVIEW | 15 | screen_mapper.py が表示するボタン名の数 |
IOS_SIM_SCREEN_SECTION_ITEMS | 10 | screen_mapper.py が各セクションに表示する項目数 |
IOS_SIM_STATE_SUBPROCESS_TIMEOUT | 15 | app_state_capture.py のサブプロセスタイムアウト(秒) |
IOS_SIM_TAP_SETTLE_MS | 500 | navigator.py でタップ後に待機する時間 |
例:
# Slow GitHub Actions runner: give boot 10 minutes
IOS_SIM_BOOT_TIMEOUT=600 python3 scripts/simctl_boot.py --wait-ready
要件
- macOS 15(Sequoia)以降
- Xcode 26 以降(完全なアプリが必要です。Command Line Tools だけでは idb を利用できません)
- Apple シリコン搭載 Mac(Homebrew の idb ビルドは arm64 のみです)
- Python 3.12 以降
idb1.5.1 以降(1.6.3 以降を推奨)— すべてのインタラクティブスクリプト(タップ、スワイプ、入力)で必要です:brew install facebook/fb/idb(idb-cli と idb-companion の両方をインストールします)- Pillow(ビジュアル差分の比較にのみ必要):
pip3 install pillow
bash scripts/sim_health_check.sh を実行して確認します(構造化された出力を得るには --json を追加します)。
トラブルシューティング
タップ、スワイプ、入力が機能しませんが、読み取りはできます。 idb は成功を報告するのに、画面が変化しません。Xcode 27 では、idb-companion が 1.5.1 より古いことを意味します。Xcode 26 で使用されていたパスから SimulatorKit.framework を探しているためです。両方を一緒にアップグレードしてから、実行中の companion を再起動してください:brew upgrade facebook/fb/idb-cli facebook/fb/idb-companion && idb kill。
SimulatorKit is required for HID interactions。 原因も解決方法も同じです。
ヘルスチェックで「idb-companion の最終インストールより前に idb companion が起動されています」という警告が表示されます。 これらの companion はアップグレード前のバイナリを引き続き実行しています。idb kill を実行してください。次の idb 呼び出しで新しい companion が起動します。
すべての idb 呼び出しで Connection refused または No such file が表示されます。 終了した companion が idb のレジストリ /tmp/idb/state に残っているため、新しい companion を起動する代わりに、idb は誰も待ち受けていないソケットに接続しようとしています。解決方法:idb disconnect <udid>。
open -a Simulator が失敗します。 Xcode 27 には Simulator.app がなく、Xcode.app/Contents/Applications/ にある DeviceHub.app に置き換えられています。代わりにヘッドレスで起動してください:xcrun simctl boot <udid>。DeviceHub を終了すると、ホストしているシミュレーターも終了することに注意してください。
idb: command not found。 companion と CLI は別々のパッケージです。brew install facebook/fb/idb で両方をインストールできます。which -a idb に複数表示される場合は、PATH 上で最初のものが優先されます。そこにある古い pip 版 fb-idb は pip uninstall fb-idb で削除してください。
上流の既知の idb の問題(idb 1.7.6 時点で未解決):
- facebook/idb#964:一部の iOS 26 以降の画面では、
describe-allがアプリケーションノードのみを返し、SpringBoard のアラートをタップできません。デフォルトのaxバックエンドはプロセス間の境界を越えて認識できません。idb ui describe-all --json --api axbridgeを試してください。タップ座標はスクリーンショットのピクセルではなく、ポイント単位です。アプリの再起動後、デフォルトの読み取りもサイズが 0 の Application ノード 1 つだけを返し続けることがあります。その場合、screen_mapper.py --deepで読み取ると復旧します(この読み取り自体が一度タイムアウトする場合があります)。 - facebook/idb#978:アクセシビリティのカスタムアクションは
describe-allに一覧表示されますが、実行できません。 - facebook/idb#954:Homebrew ビルドは arm64 のみのため、Intel Mac はサポートされていません。
ドキュメント
- SKILL.md(このファイル)— スクリプトのリファレンス、要件、トラブルシューティング
- README.md — インストール、idb の更新、Xcode 27 に関する注意事項
- AGENTS.md — アーキテクチャと実装の詳細
主な設計原則
セマンティックナビゲーション:ピクセル座標ではなく、意味(テキスト、種類、ID)で要素を探します。UI の変更にも対応できます。
トークン効率:簡潔なデフォルト出力(3~5 行)に加え、詳細な結果を得るための詳細表示モードと JSON モードを用意しています。
アクセシビリティ優先:信頼性と互換性を高めるため、標準のアクセシビリティ API を基盤にしています。
設定不要:Xcode を搭載した macOS ですぐに動作します。セットアップは不要です。
構造化データ:スクリプトは未加工のログではなく、JSON または書式設定されたテキストを出力します。解析や統合が簡単です。
自動学習:ビルドシステムがデバイスの設定を記憶します。設定はプロジェクトごとに保存されます。
これらのスクリプトを直接使うか、リクエストがスキルの説明に当てはまる場合は Claude Code に自動実行させてください。