iOS 自动化支持

Midscene 可以驱动 WebDriver 工具来支持 iOS 自动化。

由于适配了视觉模型方案,整个自动化过程可以适配任意的 App 技术栈,无论是 Native、Flutter 还是 React Native 构建的 App 或小程序都能使用。开发者只需面向最终效果调试 UI 自动化脚本即可。

iOS UI 自动化方案具备 Midscene 的全部特性:

  • 支持使用 Playground 进行零代码试用。
  • 支持 JavaScript SDK。
  • 支持使用 YAML 格式的自动化脚本与命令行工具。
  • 支持生成 HTML 报告回放所有操作路径。

案例展示

Prompt : 打开美团,帮我下单一杯 manner 超大杯冰美式咖啡,要加浓少冰喔,到结算页面让我确认

查看此次任务的完整报告:report.html

Prompt : Open Twitter and auto-like the first tweet by @midscene_ai

查看此次任务的完整报告:report.html

查看更多案例:showcases

关于 WebDriverAgent

WebDriver 是 W3C 制定的浏览器自动化标准协议,提供统一的 API 来控制不同的浏览器和应用。该协议定义了客户端与服务端之间的通信方式,使自动化工具能跨平台地操纵各种界面。

得益于 Appium 团队以及其他开源社区的努力,业界已经出现了多个优秀的库,将桌面端和移动端的操作转化为 WebDriver 协议,例如:

  • Appium —— 跨平台移动自动化框架
  • WebDriverAgent —— 专注于 iOS 设备自动化的服务
  • Selenium —— Web 浏览器自动化工具
  • WinAppDriver —— Windows 应用自动化工具

Midscene 适配 WebDriver 协议,这意味着开发者可以在任何支持 WebDriver 的设备上使用 AI 模型执行智能自动化操作。通过该设计,Midscene 不仅能完成点击、输入等传统操作,还可以:

  • 理解界面内容与上下文
  • 执行复杂的多步骤操作
  • 进行智能断言与验证
  • 提取并分析界面数据

在 iOS 平台上,Midscene 通过 WebDriverAgent 连接 iOS 设备,让你可以用自然语言描述来控制 iOS App 和系统。

本指南会带你完成使用 Midscene 控制 iOS 设备的全部步骤:通过 WebDriverAgent 连接真机、配置模型 API Key、体验零代码 Playground,并运行你的首个 JavaScript 脚本。

配置 AI 模型服务

将你的模型配置写入环境变量,可参考 模型策略 了解更多细节。

export MIDSCENE_MODEL_BASE_URL="https://替换为你的模型服务地址/v1"
export MIDSCENE_MODEL_API_KEY="替换为你的 API Key"
export MIDSCENE_MODEL_NAME="替换为你的模型名称"
export MIDSCENE_MODEL_FAMILY="替换为你的模型系列"

更多配置信息请参考 模型策略模型配置

准备工作

继续之前,请确保 WebDriverAgent 可以与设备通信。

准备工作

安装 Node.js

安装 Node.js 18 或以上版本

准备 API Key

准备一个视觉语言(VL)模型的 API Key。

你可以在 模型策略 文档中查看 Midscene.js 支持的模型和配置。

准备 WebDriver 服务

在开始之前,你需要先设置 iOS 开发环境:

  • macOS(iOS 开发必需)
  • Xcode 和 Xcode 命令行工具
  • iOS 模拟器或真机设备

配置环境

在使用 Midscene iOS 之前,需要先准备 WebDriverAgent 服务。

版本要求

WebDriverAgent 版本需要 >= 7.0.0

请参考官方文档进行设置:

验证环境配置

配置完成后,可以通过访问 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 已经正确配置并运行。

试用 Playground(零代码)

Playground 是验证连接、观察 AI 驱动步骤的最快方式,无需编写代码。它与 @midscene/ios 共享相同的核心,因此在 Playground 中通过的流程,在脚本中运行会保持一致。

  1. 启动 Playground CLI:
npx --yes @midscene/ios-playground
  1. 点击窗口中的齿轮按钮进入配置页,粘贴你的 API Key 配置。如果还没有 API Key,请回到 模型配置 获取。

开始体验

配置完成后,你可以立即开始体验 Midscene。它提供了多个关键操作 Tab:

  • Act: 与网页进行交互,这就是自动规划(Auto Planning),对应于 aiAct 方法。比如
在搜索框中输入 Midscene,执行搜索,跳转到第一条结果
填写完整的注册表单,注意主要让所有字段通过校验
  • Query: 从界面中提取 JSON 结构的数据,对应于 aiQuery 方法。

类似的方法还有 aiBoolean(), aiNumber(), aiString(),用于直接提取布尔值、数字和字符串。

提取页面中的用户 ID,返回 { id: string } 结构的 JSON 数据
  • Assert: 理解页面,进行断言,如果不满足则抛出错误,对应于 aiAssert 方法。
页面上存在一个登录按钮,它的下方有一个用户协议的链接
  • Tap: 在某个元素上点击,这就是即时操作(Instant Action),对应于 aiTap 方法。
点击登录按钮

关于自动规划(Auto Planning)和即时操作(Instant Action)的区别,请参考 API 文档。

集成 Midscene Agent

当 Playground 工作正常后,就可以切换到可复用的 JavaScript 脚本。

第 1 步:安装依赖

npm
yarn
pnpm
bun
deno
npm install @midscene/ios dotenv --save-dev

第 2 步:编写脚本

下面的示例会在设备上打开 Safari,搜索 eBay,并断言结果列表。

./demo.ts
import 'dotenv/config'; // 通过 dotenv/config 自动加载 .env 文件中的环境变量
import {
  IOSAgent,
  IOSDevice,
  agentFromWebDriverAgent,
} from '@midscene/ios';

const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
Promise.resolve(
  (async () => {
    // 方式一:直接创建设备和 Agent
    const page = new IOSDevice({
      wdaPort: 8100,
      wdaHost: 'localhost',
    });

    // 👀 初始化 Midscene Agent
    const agent = new IOSAgent(page, {
      aiActionContext:
        'If any location, permission, user agreement, etc. popup appears, click agree. If login page appears, close it.',
    });
    await page.connect();

    // 方式二:使用便捷函数(推荐)
    // const agent = await agentFromWebDriverAgent({
    //   wdaPort: 8100,
    //   wdaHost: 'localhost',
    //   aiActionContext: 'If any location, permission, user agreement, etc. popup appears, click agree. If login page appears, close it.',
    // });

    // 👀 直接打开 ebay.com(推荐做法)
    await page.launch('https://ebay.com');
    await sleep(3000);

    // 👀 输入关键字并执行搜索
    await agent.aiAct('Search for "Headphones"');

    // 👀 等待加载完成
    await agent.aiWaitFor('At least one headphone product is displayed on the page');
    // 或简单地等待几秒:
    // await sleep(5000);

    // 👀 理解页面内容并提取数据
    const items = await agent.aiQuery(
      '{itemTitle: string, price: Number}[], find product titles and prices in the list',
    );
    console.log('Headphone product information', items);

    // 👀 使用 AI 断言
    await agent.aiAssert('Multiple headphone products are displayed on the interface');

    await page.destroy();
  })(),
);

第 3 步:运行示例

npx tsx demo.ts

第 4 步:查看报告

脚本成功后会输出 Midscene - report file updated: /path/to/report/some_id.html。在浏览器中打开对应 HTML 文件即可回放每一步交互、查询与断言。

API 参考与更多资源

需要查看构造函数、辅助方法或平台专属设备 API?请移步下方的 iOS API 参考 获取详细参数及自定义操作等高级主题。若想了解跨平台共用的 API,可阅读 通用 API 参考

常见问题

为什么 WebDriverAgent 已连接,但仍无法控制设备?

请检查以下事项:

  1. 开发者模式:在“设置 > 隐私与安全性 > 开发者模式”中确认已开启。
  2. UI Automation:在“设置 > 开发者 > UI Automation”中确认已开启。
  3. 设备信任:确保设备信任当前 Mac。

模拟器与真机有哪些区别?

特性真机模拟器
端口转发需要 iproxy不需要
开发者模式必须手动开启默认开启
UI Automation 设置需手动开启默认开启
性能真实设备性能取决于 Mac 性能
传感器真实硬件模拟数据

如何自定义 WebDriverAgent 的端口和 Host?

可以通过 IOSDevice 构造函数或 agentFromWebDriverAgent 来指定端口和 Host:

// 方式一:使用 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

如何在 Playground 中获得更流畅的实时画面?

Playground 的画面预览支持两种模式:

  • 轮询模式(默认):逐帧调用 WDA 截图 API,帧率约 5-10fps。
  • 原生 MJPEG 流(推荐):直接代理 WDA 内置的 MJPEG Server,帧率更高、延迟更低。

要启用原生 MJPEG 流,需要将 WDA MJPEG Server 的端口(默认 9100)转发到本机:

# 真机需要端口转发(模拟器不需要)
iproxy 9100 9100 YOUR_DEVICE_ID

Playground 启动时会自动探测 9100 端口。如果可用,日志会显示 MJPEG: streaming via native WDA MJPEG server;否则自动回退到轮询模式。

更多

API 参考

当你需要自定义 iOS 设备行为、将 Midscene 接入依赖 WebDriverAgent 的工作流,或排查 WDA 请求问题时,请查阅本节。关于通用构造函数(报告、Hook、缓存等),请参考平台无关的 API 参考

Action Space(动作空间)

IOSDevice 使用以下动作空间,Midscene Agent 在规划任务时可以使用这些操作:

  • Tap —— 点击元素。
  • DoubleClick —— 双击元素。
  • Input —— 输入文本,支持 replace/typeOnly/clear 模式(appendtypeOnly 的已废弃别名)。支持可选参数 autoDismissKeyboard
  • Scroll —— 以元素为起点或从屏幕中央向上/下/左/右滚动,支持滚动到顶/底/左/右。
  • DragAndDrop —— 从一个元素拖拽到另一个元素。
  • KeyboardPress —— 按下指定键位。
  • LongPress —— 长按目标元素,可选自定义时长。
  • Pinch —— 双指缩放手势。scale > 1 放大,scale < 1 缩小。
  • ClearInput —— 清空输入框内容。
  • Launch —— 打开网页、Bundle ID 或 URL Scheme。
  • Terminate —— 通过 Bundle ID 关闭正在运行的 iOS 应用。
  • RunWdaRequest —— 直接调用 WebDriverAgent REST 接口。
  • IOSHomeButton —— 执行 iOS 系统 Home 操作。
  • IOSAppSwitcher —— 打开 iOS 多任务视图。

IOSDevice

创建一个由 WebDriverAgent 支撑、供 IOSAgent 驱动的设备连接。

导入

import { IOSDevice } from '@midscene/ios';

构造函数

const device = new IOSDevice({
  // 设备参数...
});

设备选项

  • wdaPort?: number —— WebDriverAgent 端口,默认 8100
  • wdaHost?: string —— WebDriverAgent host,默认 'localhost'
  • iOSDeviceClassOverride?: string —— 使用 agentFromWebDriverAgent() 或 iOS Playground 时替换默认 IOSDevice 的 npm module path。目标模块必须导出 IOSDevice class 或 default class。
  • autoDismissKeyboard?: boolean —— 文本输入后自动隐藏键盘,默认 true
  • customActions?: DeviceAction<any>[] —— 向 Agent 暴露的额外设备动作。

使用说明

  • 请确认已开启开发者模式且 WDA 能访问设备;真机转发端口时可借助 iproxy
  • 通过 wdaHost/wdaPort 可指向远程设备或自建的 WDA。
  • 通用交互方法请查阅 API 参考(通用)

示例

快速开始
import { IOSAgent, IOSDevice } from '@midscene/ios';

const device = new IOSDevice({ wdaHost: 'localhost', wdaPort: 8100 });
await device.connect();

const agent = new IOSAgent(device, {
  aiActionContext: 'If any permission dialog appears, accept it.',
});

await agent.launch('https://ebay.com');
await agent.aiAct('Search for \"Headphones\"');
const items = await agent.aiQuery(
  '{itemTitle: string, price: Number}[], list headphone products',
);
console.log(items);
自定义 Host 与端口
const device = new IOSDevice({
  wdaHost: '192.168.1.100',
  wdaPort: 8300,
});
await device.connect();

IOSAgent

将 Midscene 的 AI 规划能力绑定到 IOSDevice,通过 WebDriverAgent 实现 UI 自动化。

导入

import { IOSAgent } from '@midscene/ios';

构造函数

const agent = new IOSAgent(device, {
  // 通用 Agent 参数...
});

iOS 特有选项

  • customActions?: DeviceAction<any>[] —— 通过 defineAction 扩展规划器的可用动作。
  • appNameMapping?: Record<string, string> —— 将友好的应用名称映射到 Bundle Identifier。当你在 launch(target)terminate(bundleId) 里传入应用名称时,Agent 会在此映射中查找对应的 Bundle ID;若未找到映射,则按原样使用 target。用户提供的 appNameMapping 优先级高于默认映射。
  • 其余字段与 API constructors 一致:generateReportreportFileNameaiActionContextmodelConfigcacheIdcreateOpenAIClientonTaskStartTip 等。

使用说明

Info
  • 一个设备连接对应一个 Agent。
  • launchterminaterunWdaRequest 等 iOS 专属辅助函数也可在 YAML 脚本中使用,语法见 iOS 平台特定动作
  • 通用交互方法请查阅 API 参考(通用)

iOS 特有方法

agent.launch()

打开网页、原生应用或自定义 Scheme。

function launch(target: string): Promise<void>;
  • target: string —— 目标地址(网页 URL、Bundle Identifier、URL scheme、tel/mailto 等)或应用名称。若传入应用名称且在 appNameMapping 中存在映射,将自动解析为对应 Bundle ID;若未找到映射,则直接按 target 启动。
await agent.launch('https://www.apple.com');
await agent.launch('com.apple.Preferences');
await agent.launch('myapp://profile/user/123');
await agent.launch('tel:+1234567890');
agent.terminate()

通过 Bundle ID 终止(关闭)正在运行的 iOS 应用。

function terminate(bundleId: string): Promise<void>;
  • bundleId: string —— 要终止的应用的 Bundle Identifier(如 com.apple.Preferences)。若传入应用名称且在 appNameMapping 中存在映射,将自动解析为对应 Bundle ID。
await agent.terminate('com.apple.Preferences');
await agent.terminate('com.apple.mobilesafari');
agent.runWdaRequest()

当你需要更底层的控制能力时,执行原始的 WebDriverAgent REST 请求。

function runWdaRequest(
  method: string,
  endpoint: string,
  data?: Record<string, any>,
): Promise<any>;
  • method: string —— HTTP 动词(GETPOSTDELETE 等)。
  • endpoint: string —— WebDriverAgent 接口路径。
  • data?: Record<string, any> —— 可选的 JSON 请求体。
const screen = await agent.runWdaRequest('GET', '/wda/screen');
await agent.runWdaRequest('POST', '/session/test/wda/pressButton', { name: 'home' });

应用间导航

  • agent.home(): Promise<void> —— 回到主屏。
  • agent.appSwitcher(): Promise<void> —— 打开多任务视图。

辅助工具

agentFromWebDriverAgent()

连接 WebDriverAgent 并返回可用的 IOSAgent。

function agentFromWebDriverAgent(
  opts?: PageAgentOpt & IOSDeviceOpt,
): Promise<IOSAgent>;
  • opts?: PageAgentOpt & IOSDeviceOpt —— 在一个对象中同时传入通用 Agent 选项与 IOSDevice 的配置。
  • 设置 MIDSCENE_IOS_DEVICE_CLASS_OVERRIDE 可通过环境变量应用同一个设备 class override。显式传入的选项优先级高于环境变量。
import { agentFromWebDriverAgent } from '@midscene/ios';

const agent = await agentFromWebDriverAgent({
  wdaHost: 'localhost',
  wdaPort: 8100,
  iOSDeviceClassOverride: '@your-scope/ios-device',
  aiActionContext: 'Accept permission dialogs automatically.',
});

扩展自定义交互动作

通过 defineAction 创建处理器并传入 customActions,即可扩展 Agent 的动作空间。这些动作会追加在内置动作之后,可在规划阶段被调用。

import { getMidsceneLocationSchema, z } from '@midscene/core';
import { defineAction } from '@midscene/core/device';
import { agentFromWebDriverAgent } 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({ locate, count }) {
    console.log('click target center', locate.center);
    console.log('click count', count);
  },
});

const agent = await agentFromWebDriverAgent({
  customActions: [ContinuousClick],
});

await agent.aiAct('Click the red button five times');

相关阅读