ホーム/モバイル/limrun-ios-simulator
L

limrun-ios-simulator

by @limrun-incv
4.6(120)

LimrunクラウドiOSシミュレーター上でアプリを操作:起動、タップ、入力、アクセシビリティ要素ツリーの読み取り、スクリーンショット、動画録画、タイムドアクションチェーンを実行。このスキルにより、開発者はローカルmacOSなしでiOSアプリのテストと操作が可能になり、UIテストとデバッグを効率化します。

ios-simulatorui-testingmobile-testingcloud-simulatorGitHub
インストール方法
npx skills add https://github.com/limrun-inc/skills --skill limrun-ios-simulator
compare_arrows

Before / After 効果比較

1 组
使用前

開発者はローカルのmacOSとXcodeをセットアップし、依存関係をインストール、手動でシミュレーターを起動してアプリをインストールする必要があり、平均30分かかる。

使用後

このSkillは1つのコマンドでクラウドシミュレーターを作成しアプリをインストール、1分で操作可能。

SKILL.md

Limrun iOS Simulator

Interact with an app running on a Limrun cloud iOS simulator, from any environment (Linux, Windows, macOS, VM, container). This skill is build-agnostic: it assumes the app was already built and installed by a build skill (limrun-xcode-bazel for Bazel, limrun-xcode for xcodebuild). Keep build concerns in those skills; this one is about driving the running simulator.

Never use local Xcode, local simulators, or local macOS tools.

Auth and CLI

Install if needed: npm install --global lim. Auth is lim login or LIM_API_KEY (it may be set outside the project, so don't ask for it just because it's missing from .env or the shell). The CLI is the source of truth: the commands in this skill are verified, but if a flag errors or you need one not shown here, check lim ios <subcommand> --help instead of guessing.

Installing an app bundle

You can either use Limrun remote Bazel or Xcode services to build the app bundle and have it installed to the simulator automatically or you can sync a pre-built local .ipa file or .app folder to the simulator. The main requirement is that it must be built for simulator.

Build and install

A build skill usually attaches the simulator for you (lim xcode rbe --ios, or lim xcode build . then attach). Check what's already there:

lim xcode get      # is a simulator attached to the current build target?
lim ios list       # all running iOS instances

If none is attached, create one. It installs the last build immediately, so you don't need to rebuild:

lim ios create --attach

If the create (or lim xcode rbe --ios) output includes a signed stream URL, share it with the user as a Markdown link, like Live simulator. If you have a browser the user can see, open the URL there and tell them.

lim xcode get prints a Limrun console URL instead. It opens the same live view but requires a console login, so prefer the signed stream URL for sharing. If the console URL is all you have, share it and mention it needs login.

Install app from local

Create a new simulator:

lim ios create

Share the signed stream URL with the user as a Markdown link, like Live simulator. If you have a browser the user can see, open the URL there and tell them.

You can then run the following command to upload a bundle from local:

lim ios sync <path to .ipa file or .app folder>

You can run the same command every time you need to install a new version of the bundle. It will patch with the difference and reload it in the simulator.

Targeting the right instance

Most lim ios commands default to the last created instance and resolve the "current" one from the git repo / worktree of your cwd. So even when a simulator is attached and lim xcode get shows it, a lim ios command can still report No instance ID provided and no recent ios instance found, because your cwd isn't the git worktree where the instance was created (or isn't a git repo at all). This bites most often right after lim xcode rbe --ios in a fresh project.

The reliable recipe when that happens:

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 is the dependable source for the attached simulator's ID (lim ios list also works). Once you have it, pass --id <ios-instance-id> to all lim ios calls for the rest of the session (screenshot, tap, type, element-tree, record). Alternatively, git init the project so the workspace resolves on its own. When controlling multiple instances, always pass --id.

Launching the app

The build skills reinstall and relaunch the app after every successful build, so you usually don't need to launch it yourself. When the app is closed (a fresh attach to an old build, or after a terminate), launch it by bundle 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 terminate-app <bundle-id>                         # stop it, e.g. to reset app state

If you don't know the bundle ID, run lim ios list-apps.

The lim ios launch-app will stream the logs from the app in realtime for you to debug. If you'd like to launch and forget, you can use --detach flag.

Testing changes

When simulator interaction is part of the task, test new or changed functionality with the interaction commands after each build. Focus on what changed, plus a quick smoke test of core flows. Start by reading the element tree to see what's on screen before acting:

lim ios element-tree

Interacting with the app

Prefer tapping by accessibility id, then by label, then coordinates as a last resort:

lim ios tap-element --ax-unique-id startButton
lim ios tap-element --ax-label "Save"
lim ios tap 201 450

Toolbar / nav-bar items usually can't be tapped by id. SwiftUI collapses toolbar children into a single nav-bar group, and those items report AXUniqueId: null even when you set .accessibilityIdentifier(...) (regular content Buttons do expose it). So tap-element --ax-unique-id finds nothing for a nav-bar button. Set an .accessibilityLabel / .accessibilityIdentifier anyway for documentation, but to actually tap it, read its AXFrame from the element tree and tap the center by coordinate:

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

For text input:

lim ios type "hello world"

After every interaction, re-run element-tree to confirm the UI transitioned. No sleep is needed between a tap and element-tree; the tap blocks until done.

lim ios element-tree

Chain multiple actions with precise timing via 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

Run lim ios perform --help for the full action grammar.

Screenshots and video

Screenshot takes a positional path (not -o):

lim ios screenshot screenshot.png
lim ios screenshot screenshot.png --id <ios-instance-id>

Use the element tree for functional assertions (element existence, labels, state changes) and screenshots only for visual properties. For anything involving motion (animations, gameplay, streaming UI), prefer video:

lim ios record start                       # non-blocking
lim ios record stop -o /tmp/recording.mp4

For UI changes, include a demo video in the pull request so the user can see it.

Preview URL for humans

Upload the .ipa file directly or targz archive of the .app folder to Limrun Asset Storage and return a preview URL for the user to open it and test the app manually in the browser.

Here is how to upload it:

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"

Once the command finishes, you can give the following URL to the user to click to see a simulator where this bundle is pre-installed.

https://console.limrun.com/preview?asset=${ASSET_NAME}&platform=ios

Cleanup

When the work is completed, you can delete the iOS simulator.

lim ios delete

Gotchas

  • Instance resolution can miss in a non-git dir. See "Targeting the right instance" above; pass --id when in doubt.
  • element-tree can be large. Pipe through grep / jq to extract what you need rather than dumping the whole tree into context.
  • type / perform typeText may not drive SwiftUI (or React Native) state. Automated text injection sets the field's value through accessibility, which does not always fire a SwiftUI @Binding / onChange the way a real keystroke does. Symptom: the text appears in the field (and in element-tree), but reactive UI tied to it doesn't update (a send button stays disabled, a character counter doesn't move) and submit handlers see empty state. A real keyboard on the live stream works. When automating, drive submit through a tappable control (a button, a suggestion chip) rather than relying on text bound to reactive state, or have the app expose a test affordance.
  • Toolbar / nav-bar items aren't tappable by id. See "Interacting with the app" above: read the AXFrame from element-tree and tap by coordinate.
  • Bundle ID discovery. If you don't know the bundle ID, run lim ios list-apps after a successful install.
  • Build errors are the build skill's job. If the app isn't installing, the failure is upstream; go back to limrun-xcode-bazel / limrun-xcode.

ユーザーレビュー (0)

レビューを書く

効果
使いやすさ
ドキュメント
互換性

レビューなし

統計データ

インストール数39.5K
評価4.6 / 5.0
バージョン
更新日2026年10月23日
比較事例1 件

ユーザー評価

4.6(120)
5
37%
4
43%
3
13%
2
5%
1
2%

この Skill を評価

0.0

対応プラットフォーム

🤖claude-code

タイムライン

作成2026年7月26日
最終更新2026年10月23日
🎁 Agent 知識カード
アンケート