关于 limrun-ios-simulator
limrun-ios-simulator 是用于操作 Limrun 云端 iOS 模拟器的技能,适合从 Linux、Windows、macOS、虚拟机或容器测试已安装的应用。它通过 lim 命令行控制设备,并支持双屏折叠操作、网络隧道和 HTTP 流量检查。
主要功能
- 01创建并连接云端模拟器
- 02点击、输入及截屏
- 03操作 iPhone Duo 折叠与屏幕
- 04通过隧道访问本机或私有网络
- 05检查 HTTP 流量并导出 HAR
适用场景
- 01在远程模拟器中验证已构建的应用
- 02自动操作 iPhone Duo 的内外屏
- 03让模拟器应用访问开发机服务
- 04捕获应用的网络请求以排查问题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ limrun-inc/ skills --skill limrun-ios-simulator安装后在 Claude Code 中输入/limrun-ios-simulator确认已加载。
详细介绍
Limrun iOS 模拟器
从任意环境(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 和 shell 中看不到,用户的环境中也可能已经设置了它;索取前请先检查)。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
如果 create(或 lim xcode rbe --ios)的输出中包含签名流 URL,请将其作为 Markdown 链接分享给用户,例如
[Live simulator](<signed-stream-url>)。如果有用户可查看的浏览器,请在那里打开 URL 并告知用户。否则,在 create 命令中传入 --no-open:这会跳过在本地打开 URL,但仍会打印该 URL,供你分享。
lim xcode get 会打印 Limrun 控制台 URL。它会打开相同的实时视图,但需要登录控制台,因此分享时优先使用签名流 URL。
如果只有控制台 URL,请分享该 URL 并说明需要登录。
签名链接无需登录即可打开共享设备工作区。完整实例令牌可显示 Inspect、Connect,以及日志、事件和网络检查器。仅流令牌会显示屏幕和输入控件,但不包含这些工具。停止模拟器和选择应用需要通过已验证身份的控制台进行。
从本地安装应用
创建一个新模拟器:
lim ios create
将签名流 URL 作为 Markdown 链接分享给用户,例如
[Live simulator](<signed-stream-url>)。如果有用户可查看的浏览器,请在那里打开 URL 并告知用户。
然后可以运行以下命令,从本地上传应用包:
lim ios sync <path to .ipa file or .app folder>
每次需要安装新版本的应用包时,都可以运行同一条命令。它会仅同步差异部分,并在模拟器中重新加载应用。
折叠 iPhone Duo
在提供 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 中配对使用 buttonDown 和 buttonUp 操作,并将 button 设为 side、volumeUp 或 volumeDown。
使用已连接的 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 查看器支持单指触控和拖动。旋转视图会改变摄像机;旋转设备会改变原生方向。笔记本视图会设置铰链和方向;它不会启用 Apple 独立的 Table Mode。
定位正确的实例
大多数 lim ios 命令默认使用最近创建的实例,并根据当前工作目录所在的 git 仓库 / 工作树解析“当前”实例。因此,即使模拟器已连接且 lim xcode get 能显示它,lim ios 命令仍可能报告 No instance ID provided and no recent ios instance found,因为当前工作目录不是创建该实例的 git 工作树(或根本不是 git 仓库)。在新项目中运行 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 调用(screenshot、tap、type、element-tree、record)中都传入 --id <ios-instance-id>。或者,对项目运行 git init,让工作区能够自行解析。控制多个实例时,务必传入 --id。
访问本机上的服务
目标隧道可以让 iPhone 模拟器应用继续调用其常用目标,同时由运行 lim 的机器拨号连接这些目标。选择精确的 localhost:port 或字面量 IP:port 目标,或仅本机或 VPN 可访问的域名:
lim ios tunnel \
--id <ios-instance-id> \
--selector localhost:3000 \
--selector localhost:8081 \
--selector "*.staging.example" \
--detach
使用应用的常用 URL,例如 http://localhost:3000。声明 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 个域名选择器,端口范围为 1-65535,但不包括 53;不支持 CIDR 和 UDP。请在启动应用或打开页面之前启动隧道:较早建立的连接会保留原有路由,因此若应用先连接,请重新启动应用。
一个实例只能接受一个活动的目标隧道,且其选择器集合不可变。要添加或移除目标,请停止隧道,然后使用完整的选择器列表重新启动:
lim ios tunnel status --id <ios-instance-id> --json
lim ios tunnel stop --id <ios-instance-id>
如果模拟器尝试连接时本地服务已停止,隧道仍会保持活动状态,并报告 connection_refused;无需重新创建模拟器或隧道,只需重启服务。
检查 HTTP 流量、捕获 HAR、持久化网络日志
检查功能默认开启:隧道传输的每个 HTTP 和 HTTPS 请求都会被解码,并按每个请求一行摘要的形式输出(分离运行时写入隧道日志文件),同时实时显示在控制台的网络面板中。
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 解码,因此启用了证书固定的应用通过受检查的选择器连接时会失败:请勿将固定证书的主机加入选择器,或传入 --no-inspect 以保持 TLS 端到端连接(不生成摘要、HAR 或持久化日志)。
通过专用隧道访问专用网络中的服务
组织可以运行专用隧道:由管理员通过 lim tunnel create 或在控制台的 Network 中创建、并通过 lim tunnel run 运行的连接器,供所有指定该隧道的实例使用。用户指定隧道时,请在创建实例时传入它,而不要自行启动隧道:
lim ios create --tunnel <tunnel-name>
应用可以通过 localhost:3000 或 *.internal.example 等地址访问隧道的选择器。这些地址会在连接器所在的机器上解析,而不是在你的机器上解析。该隧道会占用实例唯一的目标隧道,因此请勿在该实例上启动或停止 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 证书
将 PEM CA 证书添加到模拟器的受信任根证书中,例如用户录制代理的 CA,以便代理读取 HTTPS 流量:
lim ios ca add ./proxy-ca.pem --id <ios-instance-id>
Safari 和应用会在下次连接时信任该证书,无需重启;在实例的生命周期内均有效。每个文件只能包含一张 CA 证书;叶证书会被拒绝。固定证书的应用以及模拟器的 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 时间,以及栈顶信息。running 且 CPU 时间接近卡顿时长,表示应用一直占用主线程;waiting 且 CPU 时间很少,表示应用处于等待状态,栈顶帧会显示等待的对象。卡顿结束时会显示其持续时间。卡顿可以解释应用冻结或自动化调用停滞的原因,因为读取屏幕需要等待主线程。在 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
系统日志快照会读取由当前或之前的系统日志流填充的缓冲区。缓冲区为空时不会返回任何行。快照读取需要运行时支持
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 可使用无障碍按压代替触摸(不会滚动,适用于没有可用 frame 的元素)。
**工具栏/导航栏项目通常无法通过 ID 点击。**SwiftUI 会将工具栏子项折叠到一个导航栏组中,即使设置了 .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>
使用 --format png --size full 可获取不含 alpha 的全分辨率 PNG,包括用于应用商店上传的截图。在 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 更改,请在拉取请求中附上演示视频,让用户可以直观查看。
App 容器文件
拉取文件前,先列出 App 的数据容器,以使用 App 创建的确切路径。列出、拉取、推送和删除时,都使用相同的 --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 后,默认使用 App bundle(--container-type app);对于 App 可写的 Documents、Library 和 tmp 目录,请使用 data。ls 输出中的路径相对于所选根目录,可直接复制到其他文件命令中。
使用视频模拟摄像头
对于由摄像头驱动的工作流(扫描二维码、拍摄文档、视频通话),可将本地视频文件作为模拟器的摄像头播放。App 会通过正常的捕获流程接收视频帧:
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)。当 App 必须恰好观察一次片段结束时,请使用 --no-loop(视频流会停在最后一帧,而不会卡住)。
剪贴板
设置或读取模拟器剪贴板。App 粘贴的内容就像是在模拟器内复制的文本,因此编辑菜单中的“粘贴”不会显示权限提示。例如,将一次性验证码粘贴到登录表单中:
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 工具读取、写入或删除用户默认设置。参数放在 -- 之后。App 会在启动时读取默认设置,因此更改后请重新启动正在测试的 App。例如,无需 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 设备使用 fingerTouch 代替 pearl):
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,供用户在浏览器中打开并手动测试 App。
上传方法如下:
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 提供给用户,让其点击以打开模拟器串流,同时控制台会安装此 bundle。
https://console.limrun.com/preview?asset=${ASSET_NAME}&platform=ios
对于非机密的 iOS App 配置,可附加可重复使用的 URL 编码 env=KEY=VALUE 参数,例如 &env=FEATURE_FLAG%3D1&env=API_URL%3Dhttps%3A%2F%2Fapi.example.com。构建链接时,请使用 URLSearchParams.append("env", "KEY=VALUE")。App 会在首次启动时接收这些变量。变量值会显示在链接和浏览器历史记录中。
添加 openUrl,即可在启动后打开 URL 或 App 深层链接,例如 &openUrl=myapp%3A%2F%2Fcheckout。使用 URLSearchParams.set("openUrl", url) 对完整 URL 及其查询字符串进行编码。此功能可与 env 配合使用,也可单独使用;在 App 安装、启动并打开 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,并按坐标点击。 - 发现 Bundle ID。 如果不知道 Bundle ID,请在安装成功后运行
lim ios list-apps。 - 构建错误应由构建技能处理。 如果应用无法安装,问题出在上游;请返回
limrun-bazel/limrun-xcode。