Skip to content

[iOS][WDA]用 xcodebuild 启动 WebDriverAgent 深度解析 #36

@nzcv

Description

@nzcv

用 xcodebuild 启动 WebDriverAgent (WDA) 深度解析

场景:基于 Appium 文档 wda-custom-server,深入理解如何用 xcodebuild 手动启动并定制 WebDriverAgent Server(用于 CI 长稳运行、并行测试、复用编译产物等场景)。

一、原理:xcodebuild 为什么能"启动"一个 HTTP Server

WebDriverAgent(WDA) 本质不是普通 App,而是一个 XCTest UI 测试 Target(WebDriverAgentRunner)。

xcodebuild test                    设备上
   │                          ┌──────────────────────┐
   ├─ 编译 WDA.app  ───────►  │ WebDriverAgentRunner  │
   ├─ 编译 Runner.xctest      │   (XCTest 测试进程)    │
   └─ 启动测试  ─────────────►│   起一个 HTTP Server   │ :8100
                              │   监听 /status /session│
                              └──────────────────────┘
                                        ▲
   你的脚本 ── HTTP ──────────────────────┘

关键点:WDA 把"跑测试"劫持成"常驻起一个 REST 服务"。测试方法里是一个死循环 server,所以 xcodebuild test 不会结束、会一直挂着——它就是 server 进程本身。

这解释了几个设计:

  • BUILD_ID=dontKillMe:防止 Jenkins 杀掉后台测试进程
  • isRunning() 同时检查 进程存活 + HTTP /status 可达

二、核心命令逐参数解析

xcodebuild clean build test \
  -project /path/to/WebDriverAgent/WebDriverAgent.xcodeproj \
  -scheme WebDriverAgentRunner \
  -destination 'id=<UDID>' \
  -configuration Debug \
  IPHONEOS_DEPLOYMENT_TARGET=<平台版本> \
  > /var/log/appium/build.log 2>&1 &
参数 作用 注意点
clean build test 清理→编译→跑测试,test 触发 server 生产常去掉 clean,改 build-for-testing + test-without-building
-project 指向 .xcodeproj workspace 用 -workspace
-scheme WebDriverAgentRunner 必须是 Runner(含 UITest target),不是 Lib
-destination 'id=<UDID>' 指定设备 真机用 xcrun xctrace list devices,模拟器用 xcrun simctl list
-configuration Debug 构建配置 一般 Debug 即可
IPHONEOS_DEPLOYMENT_TARGET 部署目标版本 匹配设备系统版本
> build.log 2>&1 & 日志重定向 + 后台运行 & 必须,test 进程永不退出

三、真机 vs 模拟器:区别在"网络"和"签名"

模拟器

  • 直接监听宿主机 localhost:8100不需要 iproxy
  • 不需要代码签名

真机

(1) 网络打通 —— iproxy 端口转发(通过 USB/usbmuxd)

iproxy 8100 8100 &      # 老语法: iproxy <本地端口> <设备端口>
# 新版语法: iproxy 8100:8100

文档里 npm install -g iproxy 已过时。现在 iproxy 来自 libimobiledevice,推荐 brew install libimobiledevice

(2) 代码签名 —— 真机必须签名

xcodebuild ... \
  -allowProvisioningUpdates \
  CODE_SIGN_IDENTITY="Apple Development" \
  DEVELOPMENT_TEAM=<你的TeamID> \
  PRODUCT_BUNDLE_IDENTIFIER=com.yourname.WebDriverAgentRunner

CI(Jenkins) 场景 keychain 默认锁定,构建前要解锁:

security list-keychains -s MyKeychain.keychain
security unlock-keychain -p <password> MyKeychain.keychain
security set-keychain-settings -t 3600 MyKeychain.keychain

四、验证 WDA 已启动

不能只看进程,要探活 HTTP 端点:

curl http://127.0.0.1:8100/status

返回示例:

{
  "value": {
    "ready": true,
    "ios": { "ip": "..." },
    "build": { "time": "..." }
  }
}

之后告诉 Appium 跳过自管理 WDA:

capabilities.setCapability("webDriverAgentUrl", "http://127.0.0.1:8100");

五、现代化改进(文档较老)

1) 分离编译与运行(CI 提速关键)

# 一次性编译,缓存到 derivedData
xcodebuild build-for-testing \
  -project WebDriverAgent.xcodeproj \
  -scheme WebDriverAgentRunner \
  -destination 'id=<UDID>' \
  -derivedDataPath /tmp/wda_build

# 之后每次只跑,不重新编译
xcodebuild test-without-building \
  -xctestrun /tmp/wda_build/Build/Products/*.xctestrun \
  -destination 'id=<UDID>'

2) 自定义端口(环境变量,不改代码)

xcodebuild test ... USE_PORT=8101

3) iOS 17+ 真机:推荐用 go-ios / pymobiledevice3 替代 iproxy(需要 RemoteXPC tunnel/DVT)。

4) 何时手动 xcodebuild:长时间运行稳定性 + CI 并行 + 复用已编译产物;否则让 Appium 自动构建更省心。

六、常见踩坑

现象 原因 / 解决
xcodebuild test 立刻退出 scheme 选成 Lib 不是 Runner;或没加 &
真机 Failed to install 签名问题,加 -allowProvisioningUpdates
curl /status 拒绝连接 真机没起 iproxy;端口被占;还在编译
CI 上签名失败 keychain 锁定,先 unlock-keychain
后台进程被 Jenkins 杀掉 BUILD_ID=dontKillMe
iOS 17+ 真机连不上 需要 RemoteXPC tunnel,改用 pymobiledevice3 / go-ios
进程冻结重启失败 重启前先 kill 残留的 xcodebuild/iproxy

总结

xcodebuild test 把 WDA 的 UITest target 当成一个永不退出的 HTTP server 进程跑起来;真机还需 iproxy 端口转发 + 代码签名;最后轮询 /status 确认就绪,再把 webDriverAgentUrl 交给 Appium。文档里那个 Java 类的全部复杂度,都是在解决 CI 长稳运行(日志、探活、防杀进程、keychain 解锁、失败重启)这一个目标。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions