与 iOS(WebDriverAgent) 集成
在使用 WebDriverAgent 连接 iOS 设备后,你可以使用 Midscene javascript SDK 来控制 iOS 设备。
关于 WebDriver 和 Midscene 的关系
WebDriver 是一套由 W3C 制定的用于浏览器自动化的标准协议,它提供了一个统一的 API 来控制不同的浏览器和应用程序。WebDriver 协议定义了客户端和服务器之间的通信方式,使得自动化工具能够跨平台地控制各种用户界面。
在 Appium 团队及其他开源社区的努力下,业界已经有了许多优秀的库将桌面、移动端等设备的自动化操作转化为 WebDriver 协议。这些工具包括:
- Appium - 跨平台移动自动化框架
- WebDriverAgent - 专门用于 iOS 设备自动化的服务
- Selenium - Web 浏览器自动化工具
- WinAppDriver - Windows 应用程序自动化工具
Midscene 适配了 WebDriver 协议,这意味着开发者可以使用 AI 模型对支持 WebDriver 的任何设备进行智能化的自动化操作。通过这种设计,Midscene 不仅能够控制传统的点击、输入等基础操作,还能够:
- 理解界面内容和上下文
- 执行复杂的多步骤操作
- 进行智能断言和验证
- 提取和分析界面数据
在 iOS 平台上,Midscene 通过 WebDriverAgent 连接 iOS 设备,让你能够使用自然语言描述的方式来控制 iOS 应用和系统。
准备 WebDriver 服务
在开始之前,你需要先设置 iOS 开发环境:
- macOS(iOS 开发必需)
- Xcode 和 Xcode 命令行工具
- iOS 模拟器或真机设备
配置环境
在使用 Midscene iOS 之前,需要先准备 WebDriverAgent 服务。请参考官方文档进行设置:
验证环境配置
配置完成后,可以通过访问 WebDriverAgent 的状态接口来验证 服务是否启动:
访问地址:http://localhost:8100/status
正确响应示例:
{
"value": {
"build": {
"version": "10.1.1",
"time": "Sep 24 2025 18:56:41",
"productBundleIdentifier": "com.facebook.WebDriverAgentRunner"
},
"os": {
"testmanagerdVersion": 65535,
"name": "iOS",
"sdkVersion": "26.0",
"version": "26.0"
},
"device": "iphone",
"ios": {
"ip": "10.91.115.63"
},
"message": "WebDriverAgent is ready to accept commands",
"state": "success",
"ready": true
},
"sessionId": "BCAD9603-F714-447C-A9E6-07D58267966B"
}
如果能够正常访问该端点并返回类似上述的 JSON 响应,说明 WebDriverAgent 已经正确配置并运行。
配置 AI 模型服务
将你的模型配置写入环境变量。更多信息请查看 选择 AI 模型。
# 替换为你的 API Key
export OPENAI_API_KEY="sk-abcdefghijklmnopqrstuvwxyz"
# 可能需要更多配置,如模型名称、接入点等,请参考 《选择 AI 模型》文档
export OPENAI_BASE_URL="..."
集成 Midscene
第一步:安装依赖
npm install @midscene/ios --save-dev
第二步:编写脚本
这里以使用 iOS Safari 浏览器搜索耳机为例。
编写下方代码,保存为 ./demo.ts
./demo.ts
import {
IOSAgent,
IOSDevice,
agentFromWebDriverAgent,
} from '@midscene/ios';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
Promise.resolve(
(async () => {
// 方法一:直接创建设备和代理
const page = new IOSDevice({
wdaPort: 8100,
wdaHost: 'localhost',
});
// 👀 初始化 Midscene agent
const agent = new IOSAgent(page, {
aiActionContext:
'如果出现位置、权限、用户协议等弹窗,点击同意。如果出现登录页面,关闭它。',
});
await page.connect();
// 方法二:或者使用便捷函数(推荐)
// const agent = await agentFromWebDriverAgent({
// wdaPort: 8100,
// wdaHost: 'localhost',
// aiActionContext: '如果出现位置、权限、用户协议等弹窗,点击同意。如果出现登录页面,关闭它。',
// });
// 👀 打开 ebay.com 网页
await page.launch('https://ebay.com');
await sleep(3000);
// 👀 输入关键词,执行搜索
await agent.aiAction('在搜索框输入 "Headphones" ,敲回车');
// 👀 等待加载完成
await agent.aiWaitFor('页面中至少有一个耳机商品');
// 或者你也可以使用一个普通的 sleep:
// await sleep(5000);
// 👀 理解页面内容,提取数据
const items = await agent.aiQuery(
'{itemTitle: string, price: Number}[], 找到列表里的商品标题和价格',
);
console.log('耳机商品信息', items);
// 👀 用 AI 断言
await agent.aiAssert('界面中有多个耳机产品');
await page.destroy();
})(),
);
第三步:运行
使用 tsx 来运行
稍等片刻,你会看到如下输出:
[
{
itemTitle: 'AirPods Pro (2nd generation) with MagSafe Charging Case (USB-C)',
price: 249
},
{
itemTitle: 'Sony WH-1000XM4 Wireless Premium Noise Canceling Overhead Headphones',
price: 278
}
]
第四步:查看运行报告
当上面的命令执行成功后,会在控制台输出:Midscene - report file updated: /path/to/report/some_id.html, 通过浏览器打开该文件即可看到报告。
构造函数与接口
IOSDevice 的构造函数
IOSDevice 的构造函数支持以下参数:
opts?: IOSDeviceOpt - 可选参数,用于初始化 IOSDevice 的配置
wdaPort?: number - 可选参数,WebDriverAgent 端口。默认值为 8100。
wdaHost?: string - 可选参数,WebDriverAgent 主机。默认值为 'localhost'。
autoDismissKeyboard?: boolean - 可选参数,是否在输入文本后自动关闭键盘。默认值为 true。
customActions?: DeviceAction<any>[] - 可选参数,自定义设备动作列表。
iOS Agent 上的更多接口
除了 API 参考 中的通用 Agent 接口,IOSAgent 还提供了一些其他接口:
agent.launch()
启动一个网页或原生 iOS 应用。
function launch(uri: string): Promise<void>;
-
参数:
uri: string - 要打开的 uri,可以是网页 url、原生 app 的 bundle identifier 或自定义 URL scheme
-
返回值:
-
示例:
import { IOSAgent, IOSDevice, agentFromWebDriverAgent } from '@midscene/ios';
// 方法一:手动创建设备和代理
const page = new IOSDevice();
const agent = new IOSAgent(page);
await page.connect();
// 方法二:使用便捷函数(推荐)
const agent = await agentFromWebDriverAgent();
await agent.launch('https://www.apple.com'); // 打开网页
await agent.launch('com.apple.mobilesafari'); // 启动 Safari
await agent.launch('com.apple.Preferences'); // 启动设置应用
await agent.launch('myapp://profile/user/123'); // 打开应用深度链接
await agent.launch('tel:+1234567890'); // 拨打电话
await agent.launch('mailto:example@email.com'); // 发送邮件
agentFromWebDriverAgent() (推荐)
通过连接 WebDriverAgent 服务创建 IOSAgent,这是最简便的方式。
function agentFromWebDriverAgent(
opts?: PageAgentOpt & IOSDeviceOpt,
): Promise<IOSAgent>;
-
参数:
opts?: PageAgentOpt & IOSDeviceOpt - 可选参数,用于初始化 IOSAgent 的配置,其中 PageAgentOpt 参考 构造器,IOSDeviceOpt 的配置值参考 IOSDevice 的构造函数
-
返回值:
Promise<IOSAgent> 返回一个 IOSAgent 实例
-
示例:
import { agentFromWebDriverAgent } from '@midscene/ios';
// 使用默认 WebDriverAgent 地址 (localhost:8100)
const agent = await agentFromWebDriverAgent();
// 使用自定义 WebDriverAgent 地址
const agent = await agentFromWebDriverAgent({
wdaHost: 'localhost',
wdaPort: 8100,
aiActionContext: '如果出现弹窗,点击同意',
});
扩展自定义交互动作
使用 customActions 选项,结合 defineAction 定义的自定义交互动作,可以扩展 Agent 的动作空间。这些动作会追加在内置动作之后,方便 Agent 在规划阶段调用。
import { getMidsceneLocationSchema, z } from '@midscene/core';
import { defineAction } from '@midscene/core/device';
import { IOSAgent, IOSDevice } from '@midscene/ios';
const ContinuousClick = defineAction({
name: 'continuousClick',
description: 'Click the same target repeatedly',
paramSchema: z.object({
locate: getMidsceneLocationSchema(),
count: z
.number()
.int()
.positive()
.describe('How many times to click'),
}),
async call(param) {
const { locate, count } = param;
console.log('click target center', locate.center);
console.log('click count', count);
// 在这里结合 locate + count 实现自定义点击逻辑
},
});
const agent = await agentFromWebDriverAgent({
customActions: [ContinuousClick],
});
await agent.aiAction('点击红色按钮五次');
更多关于自定义动作的细节,请参考 集成到任意界面。
更多
FAQ
为什么我连接了设备,但是通过 WebDriverAgent 仍然无法控制?
请检查以下几点:
- 开发者模式:确保在设置 > 隐私与安全性 > 开发者模式 中已开启
- UI 自动化:确保在设置 > 开发者 > UI 自动化,启 用 UI 自动化
- 设备信任:确保设备已信任当前 Mac
模拟器和真机有什么区别?
| 特性 | 真机 | 模拟器 |
|---|
| 端口转发 | 需要 iproxy | 不需要 |
| 开发者模式 | 需要启用 | 自动启用 |
| UI 自动化设置 | 需要手动启用 | 自动启用 |
| 性能 | 真实设备性能 | 依赖 Mac 性能 |
| 传感器 | 真实硬件 | 模拟数据 |
如何使用自定义的 WebDriverAgent 端口和主机?
你可以通过 IOSDevice 的构造函数或 agentFromWebDriverAgent 来指定 WebDriverAgent 的端口和主机:
// 方法一:使用 IOSDevice
const device = new IOSDevice({
wdaPort: 8100, // 自定义端口
wdaHost: '192.168.1.100', // 自定义主机
});
// 方法二:使用便捷函数(推荐)
const agent = await agentFromWebDriverAgent({
wdaPort: 8100, // 自定义端口
wdaHost: '192.168.1.100', // 自定义主机
});
对于远程设备,还需要相应地设置端口转发:
iproxy 8100 8100 YOUR_DEVICE_ID