mcpField
L
limrun-ios-simulator

limrun-ios-simulator

github.com/limrun-inc测试与质量243.2k 次安装1 天前更新
代码仓库

关于 limrun-ios-simulator

limrun-ios-simulator 是用于操作 Limrun 云端 iOS 模拟器的技能,适合从 Linux、Windows、macOS、虚拟机或容器测试已安装的应用。它通过 lim 命令行控制设备,并支持双屏折叠操作、网络隧道和 HTTP 流量检查。

主要功能

  1. 01创建并连接云端模拟器
  2. 02点击、输入及截屏
  3. 03操作 iPhone Duo 折叠与屏幕
  4. 04通过隧道访问本机或私有网络
  5. 05检查 HTTP 流量并导出 HAR

适用场景

  1. 01在远程模拟器中验证已构建的应用
  2. 02自动操作 iPhone Duo 的内外屏
  3. 03让模拟器应用访问开发机服务
  4. 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。

评论

支持 Markdown