一把椅子,三个小时
认识一个人,做电商客服外包,想把「每天早上把二十几个后台的订单状态过一遍」这件事交出去。
他先看了官方文档,发现函数一大堆,不知道从哪下手;又听说「免越狱、USB HID、代理模式、蓝牙」这些词,更糊涂了——四个词都听过,但不知道跟写脚本有什么关系。
那三天他的状态是这样的:第一天装环境,第二天试着看懂一个例子,第三天在纠结「到底该用哪个事件模块」。真正开始写代码,已经是第四天。
这篇就是把那四天压缩成一篇。顺序按「一个脚本从零到跑起来」的真实路径走,每个环节给出官方文档里的真实函数名和参数,不绕概念。写 iOS免越狱脚本 最耗时间的从来不是逻辑,是搞清楚每个函数到底返回什么。
一、先想清楚:脚本里的「免越狱」到底指什么
很多人一上来就纠结「免越狱能不能做 XX」,其实先要把这个词的含义定下来。
免越狱不等于「不装东西」 ,它指的是不改动系统。iOS 的沙箱、签名机制、系统完整性都不动,你的手机还是一台能正常升级、保修有效的手机。自动化之所以能做到这一点,是因为它只需要两件事:
- 看到屏幕(截图或投屏)
- 送出输入(点击、滑动、打字)
这两件事都有系统本身就开放的路子,不需要打开系统。所以「免越狱脚本」的完整含义是:在不改动系统的前提下,程序化地完成「看屏幕 + 送输入」这件事。
理解这一点之后,很多问题就自然消解了——比如「免越狱脚本能不能改系统设置」,答案是不能,因为它压根没碰系统;而绝大多数业务场景要的也不是改系统,是「按时把一批固定操作做完」。写 iOS自动化脚本 的门槛之所以比想象中低,也是这个道理:你要的能力就两样,别的都不用碰。
二、三条链路,脚本写法差在哪
这是新手最容易卡住的地方。同一个业务逻辑,在不同链路上要调用不同的事件模块:
| 链路 | 事件模块 | 系统要求 | 额外要准备 |
|---|---|---|---|
| 代理模式 | agentEvent |
iOS 13+ | 签名 IPA |
| USB HID | usbHidEvent |
iOS 17+ | 一根数据线 |
| 蓝牙 BLE | bleEvent |
iOS 17+ | ESP32 开发板 |
| OTG HID | otgEvent |
iOS 17+ | 开发板 + 转接头 |
iOS 17 是个硬门槛。官方文档的原话是:USB HID 和蓝牙 BLE 都要求 iOS 17 及以上,低于 iOS 17 只能用代理模式。手上是老机器的话,先确认版本再动手,能省掉大量无效尝试。
顺带说清免签名这件事:代理模式要签名 IPA,而 USB HID、蓝牙、OTG 这三条硬件链路都不需要签名——不用买证书,也不用管证书什么时候过期。这是很多人选硬件链路的第一个理由。
好消息是四个模块的方法名是一致的:
四个模块都有的方法:
clickPoint(x, y) 点击坐标
press(x, y, duration) 长按
doubleClickPoint(x, y) 双击
swipeToPoint(x1,y1,x2,y2,duration) 滑动
multiTouch(fingers) 多点触控
touchDown / touchMove / touchUp 按下/移动/抬起(自己拼手势)
systemKey(...) 系统键(Home、音量等)
keyPress / keyPressChar 按键与字符输入
也就是说:业务逻辑写一遍,换链路只改模块名。设计脚本时这一点值得利用——把点击操作包一层函数,链路切换只改那一处:
// 把事件模块包一层,换链路只改这里
const EV = usbHidEvent; // 代理模式换成 agentEvent,蓝牙换 bleEvent,OTG 换 otgEvent
function tap(x, y) {
return EV.clickPoint(x, y);
}
各自还有一点专属接口,用到时再查文档即可(比如蓝牙的 bleEvent.openSerial、setWifiInfo,OTG 的 otgEvent.scanOtgDevice、isOtgConnect)。
三、环境准备:动手前要确认的四件事
- 手机系统版本 —— 决定你能走哪条链路(见上表)
- 自动化服务状态 —— 中控里能看到服务是否就绪,这是脚本能不能跑的前提
- 授权类型 —— 执行脚本要 USB 设备授权,投屏要 USB 投屏授权,两者不是同一个。买错授权是这个环节最常见的坑
- 屏幕尺寸 —— 后面写坐标全靠它
前三项在装完环境后自然就绪,第四项要专门说一下:坐标和投屏画面、截图是同一套像素坐标,所以要么在投屏里量,要么用节点自带的坐标。USB HID 链路要显式设置屏幕尺寸(usbHidEvent.setScreenSize(w, h)),不设的话坐标换算没有依据,点哪偏哪。
四、第一个脚本的骨架
不管哪条链路,开头的结构是固定的。下面这份可以直接抄:
function main() {
logd("检查自动化环境...");
if (!autoServiceStart(3)) {
logw("自动化服务启动失败,无法执行脚本");
exit();
return;
}
// 节点抓取参数,脚本开头设一次就够
setFetchNodeParam({
"labelFilter": "2", // 只取有 label 的节点
"visibleFilter": "2", // 只取 visible = true 的
"maxDepth": "20", // 层级越少越快,建议 1-500
"excludedAttributes": "visible,selected,enable,accessible"
});
// ... 业务代码
logd("脚本执行结束");
}
// 环境检查:官方示例里给出的写法,直接用
function autoServiceStart(time) {
for (let i = 0; i < time; i++) {
if (isServiceOk()) {
return true;
}
let started = startEnv();
logd("第" + (i + 1) + "次启动服务结果: " + started);
if (isServiceOk()) {
return true;
}
}
return isServiceOk();
}
main();
关于 autoServiceStart :它不是系统函数,是官方示例里给出的辅助写法。思路很朴素——循环调 startEnv(),再用 isServiceOk() 检查,最多试 time 次。这比自己写一个 sleep(5000) 然后赌服务起来了要可靠得多,建议直接沿用。
关于 setFetchNodeParam :这个参数表对性能影响比你想象的大。文档里说 excludedAttributes「可以增加抓取速度」——设备一多,少抓几个用不上的属性,累积下来差异很明显。maxDepth 建议 1–500,越小越快。
如果走 USB HID ,开头换成 HID 自己的会话启动:
let r = usbHidEvent.sessionStart(true); // 参数是「是否尝试增强兼容模式」,默认 true
if (!(r == null || r === "")) {
logw("开启 HID 会话失败: " + r);
return;
}
r = usbHidEvent.setScreenSize(1170, 2532); // 坐标换算依赖这一步
返回值约定要记住:null 或空字符串代表成功,其它字符串是错误信息。这是全文所有代码判断的基础,也是新手最容易搞反的地方。
五、常用函数清单:五类够用
文档里函数很多,但真正每天用的就那么几十个。按用途分五类:
1. 环境与服务
| 函数 | 作用 |
|---|---|
isServiceOk() |
自动化服务是否就绪 |
startEnv() / closeEnv() |
启动 / 停止运行环境 |
isDeviceOnline() |
设备是否在线 |
isDeviceAuthOk() / getDeviceAuth() |
授权状态 |
sleep(毫秒) |
暂停执行 |
logd / logi / logw / loge |
四档日志输出 |
2. 交互(点击与手势)
四个事件模块的方法名一致,上面已列出。代理模式下还有 agentEvent.touchDown / touchMove / touchUp 用来拼自定义手势。
3. 节点(找元素,最常用的一类)
节点查询是链式写法,不是传配置对象:
// 按文字找(正则匹配),最多等 5 秒
let nd = labelMatch("发布").getOneNodeInfo(5000);
// 按 id 找
let nd2 = id("com.example.app:id/btn").getOneNodeInfo(3000);
// 拿全部匹配
let list = labelMatch(".*订单.*").getNodeInfo(5000);
可用的筛选器:id / idMatch、label / labelMatch、name / nameMatch、type / typeMatch、value / valueMatch、xpath,以及 visible、enable 这类属性。
节点对象上能拿到:id、xpath、label、name、type、value、bounds、index、depth、visible、enable,方法有 clickCenter、clickRandom、parent、child、allChildren、siblings、previousSiblings、nextSiblings。
每次查询前先 releaseNode() 再 lockNode() ——官方示例固定这么写。先释放上一轮数据,再锁定当前界面,否则可能读到上一屏的节点。
4. 输入文字
代理模式走输入法模块:
if (imeApi.isOk()) {
let r = imeApi.input("要输入的内容");
logd("输入结果: " + r); // 空字符串代表输入失败,非空是实际输入的内容
}
imeApi.setClipboard("#话题标签"); // 长文本建议走剪贴板
imeApi.paste();
imeApi.dismiss(); // 收起键盘,别漏
imeApi.dismiss() 这一步最容易漏。输入完不收键盘,键盘会一直占着下半屏,把下面的按钮挡住,点击落到键盘上,而失败不报错、只表现为「操作没生效」。
USB HID 链路是另一套:usbHidEvent.inputText()(走剪贴板粘贴)、usbHidEvent.typeText()(键盘打字,遇非英文自动粘贴)。
5. 图色、截图与设备
| 函数 | 作用 |
|---|---|
image.captureFullScreen() |
全屏截图 |
image.findImage(...) / findImageByColor(...) |
找图 / 偏色找图 |
image.cmpColor(...) |
比色 |
device.getDeviceInfo() |
设备信息(取实时屏幕宽高用它) |
device.getDeviceId() / getDeviceName() / getModel() / getOSVersion() |
设备标识与版本 |
device.getBattery() / isCharging() |
电量 |
device.applist() |
已安装应用列表 |
节点查不到的时候,图色是兜底手段;需要留证或排查的时候,截图是标配。
六、坐标怎么写才不会点歪
坐标问题占了新手失败原因的一半,值得单独讲。
优先用节点自己的坐标,不要手量。 节点对象带 bounds,直接算中心点:
let nd = labelMatch("确认").getOneNodeInfo(5000);
if (nd) {
clickPoint(nd.bounds.centerX(), nd.bounds.centerY());
}
这样写的好处是换机型、换分辨率都不用改——坐标是从当前设备实时算出来的。
确实要写硬坐标时,注意三件事:
- 坐标基准是投屏画面/截图的像素坐标,不是逻辑分辨率
- 分辨率变化或横竖屏切换后必须重设屏幕尺寸,USB HID 是
setScreenSize(w, h),蓝牙和 OTG 也有各自同名接口 - 竖屏和横屏是两套坐标系,混用时需要用
adjustScreenOrientation切换,别指望自动识别
能不用坐标就别用。 一条经验:如果某个位置只能用坐标,说明那里大概率是长度不固定的列表或图标,这种地方正是最容易被版本更新搞坏的。
七、等元素,还是等时间
这是脚本稳不稳的分水岭。
固定等待(sleep) 的问题在于它跟现实脱节:网络快的时候白等,网络慢的时候还没加载完就开始点。短期凑合可以,长期一定出问题。
正确做法是把等待交给查询函数 ——getOneNodeInfo(timeout) 里的超时参数就是天然的等待:传 15000 就是「最多等 15 秒」,元素提前出现会立刻返回。
// 不好的写法
sleep(3000);
clickPoint(585, 2280);
// 好的写法
let btn = labelMatch("下一步").getOneNodeInfo(15000);
if (btn) {
clickPoint(btn.bounds.centerX(), btn.bounds.centerY());
} else {
logw("15 秒内没出现,可能界面变了");
}
需要等「元素消失」时,用循环查 + sleep 小步轮询(每次 300–500ms,最多几十次),比一次性睡很久好。
八、调试:三层工具配合用
脚本跑不通的时候,按这个顺序查,比盯代码快得多。
第一层:日志。 在关键步骤前打一行 logd("步骤名"),成本一行,收益是「失败时知道停在哪」。这比事后猜要省事得多。
第二层:截图。 失败分支里加一句 image.captureFullScreen()。绝大多数失败是「停在了意料之外的页面」——弹窗、更新提示、登录过期。看到截图基本就定位了。
第三层:中控的实时日志和执行历史。 前者看单台设备的逐步输出,后者看多设备的结果汇总。执行历史里能直接看到哪台失败、失败在哪一步,是排查多设备问题的入口。
一个实用习惯:新脚本先在单台设备上跑,第一步只做最短的动作。比如只做「打开 App 并确认首页出现了」,跑通再往下加。一上来写完整流程,失败时你无法判断是环境问题、定位问题还是逻辑问题。
九、从一台到多台:脚本怎么下发
单台跑通之后,下一步是铺开。中控里的做法很直接:
- 把编译好的 iec 文件放进中控左下角的脚本目录,右键 → 刷新
- 选中目标设备(或先在分组栏分好组),右键脚本 → 执行脚本
分组这件事在设备超过十台之后就从“锦上添花”变成“必须” :几十台塞在一屏里,你根本看不出哪台失败了;分成几组,问题一眼可见。
前面提过的那条边界再强调一次:执行脚本要 USB 设备授权,投屏要 USB 投屏授权。另外,同一台设备同一时间只跑一个任务,所以批量提速靠的是设备数量,不是单台并发。
这也是 iOS群控 和“多写几个脚本”的分界线:脚本解决“一件事怎么做”,群控解决“同一件事怎么在几十台上同时做对、并且知道哪台没做对”。
需要和外部系统对接时(比如让电商后台自动触发),可以走开放接口,参数和返回值都是 JSON,用 Python、Node、cURL 都能调。这块展开是一篇单独的篇幅,先记住入口在中控的 8019 端口即可。
十、进阶:脚本写稳之后的三件事
工程化。 脚本一长,纯 JS 单文件就难维护了。文档里给了 npm 和 TypeScript 的支持方式,可以按工程结构组织代码。
保护。 脚本要交给别人用的时候,源码就是资产。文档里有专门的 JS 混淆说明,可以在发布前处理一遍。
参数化。 把账号、关键词、时间点这些经常改的东西抽成配置,而不是写死在流程里。这一条决定了你的脚本能用多久——改版一次就要重读源码的脚本,没人愿意维护。
十一、按场景推荐怎么配(想跳过过程就看这节)
如果你不想读完上面十节,这一节就是结论。下面五种情况基本覆盖了绝大多数团队,对应的都是同一套中控里的不同入口。
| 你的情况 | 推荐方案 | 为什么 |
|---|---|---|
| iOS 13–16,或流程复杂、需要精确识别界面元素 | 推荐 EasyClick 代理模式 | 功能最全,有节点选择器,调试最方便 |
| iOS 17+,想省掉签名成本,界面比较稳定 | 推荐 EasyClick 加 USB HID | 一根数据线,免签名免开发板,风控也更温和 |
| iOS 17+,风控压力大,或者设备分散摆放 | 推荐 EasyClick 加蓝牙 HID | 风控最友好,不受线长限制 |
| 完全不想写代码 | 推荐 EasyClick AI 智能体 | 中文对话描述任务,或拖拽出工作流 |
| 要让自己的系统来触发设备操作 | 推荐 EasyClick 开放接口 | 8019 端口,HTTP 加 JSON,任何语言都能接 |
这五种情况有一个共同点:它们共用同一个中控、同一套授权、同一批设备。所以实际落地时很少是“只选一种”——更常见的组合是先用代理模式把流程逻辑跑通,风控有压力了切到 HID,临时性的任务交给 AI 智能体,固定的批处理用脚本加定时。
一句话总结:选平台比选单点方案重要。单点方案决定某一件具体的事怎么做,平台决定这几条链路和入口能不能共用一套设备与授权管理。上面这张表里的五种情况,在 EasyClick 里对应的是同一个平台的不同入口,不是五套要分别学的工具。
十二、常见问题
「免越狱」是不是不能做需要登录的操作?
能做。登录属于应用层操作,跟越狱无关。要注意的是登录态会过期,脚本里得处理「发现要求登录 → 走一遍登录流程」这条分支。
要不要一开始就上最复杂的方案?
不要。三条链路各自能做到哪一步不一样,但业务逻辑是同一套。先用最容易跑通的那条把逻辑验证一遍,再按风控和规模要求换链路——换的时候只改事件模块。
脚本里的等待该设多长?
给足上限,不要卡得太紧。getOneNodeInfo(15000) 不会真的等 15 秒,元素出现就返回;但如果上限设 3000,网络稍慢就失败。用户不差这十几秒,但会介意脚本莫名失败。
为什么同样的脚本在另一台设备上就不行?
三个常见原因:分辨率不同但坐标写死了、系统版本不同导致界面差异、那台设备的自动化服务或授权状态不对。按这个顺序查。
写脚本这件事,门槛其实不在语言。那位做客服外包的朋友最后是这么开始的:先只写「打开后台 → 等订单列表出现 → 截图」,跑通之后再逐步加动作。四个小时后他有了第一个能用的脚本;那四天里花掉的三天,全都耗在「不知道该从哪开始」上。
先把环境和服务判断写对,再挑最短的一个动作跑通,剩下的就是加步骤——顺序对了,就不会再卡四天。iPhone自动化 这件事的入门成本,比大多数人预想的低,难的是把它做稳。
想先看具体某条链路怎么落地,可以读iOS免越狱自动化三条链路怎么选;USB HID 的原理和 API 细节,看苹果群控技术拆解:USB HID 的原理与 API 完整教程;一篇完整的实战代码参考,可以读苹果群控发短视频脚本怎么写。
iEasyClick:手机自动化脚本与苹果群控方案站,覆盖安卓免 root、iOS 免越狱、鸿蒙 Next,提供脚本开发教程、中控投屏与批量运维方案。官网 ieasyclick.net
想要真实跑起来?
本文介绍的方案均可基于 EasyClick 能力在 iEasyClick 落地。官网提供完整文档、开发工具与自动化产品,免费体验。