limrun-ios-simulator

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Limrun iOS Simulator

Limrun iOS模拟器

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.
可从任意环境(Linux、Windows、macOS、虚拟机、容器)与运行在Limrun云iOS模拟器上的应用进行交互。本技能与构建方式无关:它假设应用已由构建技能(Bazel项目使用
limrun-xcode-bazel
,xcodebuild项目使用
limrun-xcode
)完成构建并安装。构建相关事宜请交由那些技能处理;本技能专注于操控运行中的模拟器。
切勿使用本地Xcode、本地模拟器或本地macOS工具。

Auth and CLI

认证与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.
若需安装:
npm install --global lim
。认证方式为
lim login
或设置
LIM_API_KEY
(该密钥可能在项目外部设置,因此不要仅因
.env
或shell中未包含就向用户索要)。CLI是权威来源:本技能中的命令均经过验证,但如果某个标记报错或你需要此处未展示的标记,请查看
lim ios <subcommand> --help
,而非自行猜测。

Get a simulator attached

获取已连接的模拟器

A build skill usually attaches the simulator for you (
lim xcode rbe --ios
, or
lim xcode build .
then attach). Check what's already there:
bash
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:
bash
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.
构建技能通常会自动为你连接模拟器(例如
lim xcode rbe --ios
,或执行
lim xcode build .
后进行连接)。检查当前已有的模拟器:
bash
lim xcode get      # 当前构建目标是否已连接模拟器?
lim ios list       # 所有运行中的iOS实例
如果没有已连接的模拟器,请创建一个。它会立即安装最新构建的应用,因此无需重新构建:
bash
lim ios create --attach
如果创建(或
lim xcode rbe --ios
)的输出包含已签名的流URL,请将其作为Markdown链接分享给用户,例如实时模拟器。如果用户可以看到你的浏览器,可在此打开该URL并告知用户。
lim xcode get
会打印Limrun控制台URL。它会打开相同的实时视图,但需要控制台登录,因此优先选择已签名流URL进行分享。如果只有控制台URL,可分享并说明需要登录。

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:
bash
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
.
大多数
lim ios
命令默认使用最后创建的实例,并根据当前工作目录的git仓库/工作区解析“当前”实例。因此,即使模拟器已连接且
lim xcode get
显示了它,
lim ios
命令仍可能提示
No instance ID provided and no recent ios instance found
,这是因为你的当前工作目录并非创建该实例的git工作区(或根本不是git仓库)。这种情况在新项目中执行
lim xcode rbe --ios
后最常出现。
遇到这种情况时的可靠解决方法:
bash
lim xcode get                          # 显示已连接模拟器的ID
lim ios element-tree --id <该ID>    # 为所有lim ios命令添加--id参数
lim xcode get
是获取已连接模拟器ID的可靠来源(
lim ios list
也可用)。获取ID后,在本次会话的所有
lim ios
调用(截图、点击、输入、元素树、录制)中都添加
--id <ios实例ID>
参数。或者,为项目执行
git init
,使工作区可自动解析实例。当操控多个实例时,务必添加
--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:
bash
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
.
构建技能会在每次构建成功后重新安装并重启应用,因此通常无需手动启动。当应用已关闭(例如连接到旧构建的新模拟器,或应用被终止后),可通过Bundle ID启动:
bash
lim ios launch-app <Bundle ID>                            # 若应用已运行则将其置于前台
lim ios launch-app <Bundle ID> --mode RelaunchIfRunning   # 重启应用以获得干净状态
lim ios terminate-app <Bundle ID>                         # 停止应用,例如重置应用状态
若不知道Bundle ID,请运行
lim ios list-apps

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:
bash
lim ios element-tree
当任务包含模拟器交互时,每次构建后使用交互命令测试新增或修改的功能。重点关注变更内容,再快速进行核心流程的冒烟测试。操作前先读取元素树,查看当前屏幕显示的内容:
bash
lim ios element-tree

Interacting with the app

与应用交互

Prefer tapping by accessibility id, then by label, then coordinates as a last resort:
bash
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
Button
s 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:
bash
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:
bash
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.
bash
lim ios element-tree
Chain multiple actions with precise timing via
perform
:
bash
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.
优先通过无障碍ID点击,其次是标签,最后才考虑通过坐标点击:
bash
lim ios tap-element --ax-unique-id startButton
lim ios tap-element --ax-label "Save"
lim ios tap 201 450
工具栏/导航栏项通常无法通过ID点击。SwiftUI会将工具栏子项折叠为单个导航栏组,即使你设置了
.accessibilityIdentifier(...)
,这些项仍会报告
AXUniqueId: null
(常规内容中的
Button
会暴露该ID)。因此
tap-element --ax-unique-id
无法找到导航栏按钮。无论如何仍需设置
.accessibilityLabel
/
.accessibilityIdentifier
用于文档记录,但实际点击时,需从元素树中读取其
AXFrame
,然后点击坐标中心:
bash
lim ios element-tree --id <ID> | grep -i -A6 -B2 moon   # 查找该项的AXFrame
lim ios tap <x> <y> --id <ID>                           # 点击框架中心
文本输入操作:
bash
lim ios type "hello world"
每次交互后,重新运行
element-tree
以确认UI已完成过渡。点击和
element-tree
之间无需等待;点击操作会阻塞直到完成。
bash
lim ios element-tree
通过
perform
命令按精准时间链执行多个动作:
bash
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
查看完整的动作语法。

Screenshots and video

截图与视频

Screenshot takes a positional path (not
-o
):
bash
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:
bash
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.
截图命令使用位置路径(而非
-o
参数):
bash
lim ios screenshot screenshot.png
lim ios screenshot screenshot.png --id <iOS实例ID>
使用元素树进行功能断言(元素存在性、标签、状态变化),仅使用截图验证视觉属性。对于涉及动态效果(动画、游戏、流式UI)的场景,优先使用视频:
bash
lim ios record start                       # 非阻塞式录制
lim ios record stop -o /tmp/recording.mp4
对于UI变更,请在拉取请求中包含演示视频,以便用户查看。

Cleanup

清理

When the user is done with the dev session:
bash
lim ios delete
If they're still iterating in a dev-client / Metro session, leave the simulator running and tell them it's still available.
当用户完成开发会话后:
bash
lim ios delete
如果用户仍在开发客户端/Metro会话中迭代,请保持模拟器运行并告知用户它仍可用。

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
    .
  • 在非git目录中实例解析可能失败:请参考上文“定位正确的实例”部分;如有疑问,添加
    --id
    参数。
  • element-tree
    输出可能很大
    :通过
    grep
    /
    jq
    管道提取所需内容,而非将整个树输出到上下文。
  • type
    /
    perform typeText
    可能无法驱动SwiftUI(或React Native)状态
    :自动化文本注入通过无障碍接口设置字段值,但这并不总是像真实按键那样触发SwiftUI的
    @Binding
    /
    onChange
    。症状:文本显示在字段中(且在
    element-tree
    中可见),但与之绑定的响应式UI未更新(发送按钮保持禁用状态,字符计数器未变化),提交处理程序会看到空状态。实时流中的真实键盘可正常工作。自动化时,通过可点击控件(按钮、建议芯片)触发提交,而非依赖绑定到响应式状态的文本,或让应用暴露测试接口。
  • 工具栏/导航栏项无法通过ID点击:请参考上文“与应用交互”部分:从
    element-tree
    读取
    AXFrame
    并通过坐标点击。
  • Bundle ID查找:若不知道Bundle ID,请在安装成功后运行
    lim ios list-apps
  • 构建错误由构建技能处理:如果应用无法安装,问题出在构建环节;请回到
    limrun-xcode-bazel
    /
    limrun-xcode
    处理。