ios脚本开发

iOS 免越狱脚本怎么写?苹果群控从环境准备到跑通第一个脚本

写 iOS 免越狱脚本 最耗时间的不是逻辑,是搞清楚每个函数返回什么。这篇按「环境准备 → 脚本骨架 → 函数地图 → 坐标与等待 → 调试 → 多机下发」的顺序走一遍,每个环节都给出官方文档里的真实函数名和参数,照着能写出自己的第一个脚本。

约 16 分钟更新于

免越狱脚本是什么,三条链路怎么写

有个做电商客服外包的朋友,想把「每天早上把二十几个后台的订单状态过一遍」交出去。看官方文档函数一大堆不知从哪下手,又听说「免越狱、USB HID、代理模式、蓝牙」更糊涂——装环境、看例子、纠结用哪个事件模块,真正写代码已经是第四天。这篇把四天压缩成一篇,按「一个脚本从零到跑起来」的真实路径走,每步给出官方文档里的真实函数名和参数。写 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); }

动手前确认四件事:手机系统版本(决定走哪条链路)、自动化服务状态、授权类型(执行脚本要 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 或空字符串代表成功、其它字符串是错误信息——这是所有代码判断的基础。常用函数按用途分几类。环境与服务:

函数 作用
isServiceOk() 自动化服务是否就绪
startEnv() / closeEnv() 启动 / 停止运行环境
isDeviceOnline() 设备是否在线
isDeviceAuthOk() / getDeviceAuth() 授权状态
sleep(毫秒) 暂停执行
logd / logi / logw / loge 四档日志输出

节点查询是链式写法,不是传配置对象:

let nd = labelMatch("发布").getOneNodeInfo(5000);      // 按文字找,最多等 5 秒
let nd2 = id("com.example.app:id/btn").getOneNodeInfo(3000);  // 按 id 找
let list = labelMatch(".*订单.*").getNodeInfo(5000);   // 拿全部匹配

筛选器支持 id/label/name/type/value 的精确与正则匹配(如 labelMatch)、xpath 与 visible、enable 等属性;节点对象带 bounds、index、depth 等字段和 clickCenter、clickRandom、parent、child、allChildren、siblings 等方法。每次查询前先 releaseNode() 再 lockNode(),否则可能读到上一屏的节点。输入文字,代理模式走输入法模块:

if (imeApi.isOk()) {
    let r = imeApi.input("要输入的内容");   // 空字符串代表失败,非空是实际输入的内容
    logd("输入结果: " + r);
}
imeApi.setClipboard("#话题标签");   // 长文本建议走剪贴板
imeApi.paste();
imeApi.dismiss();                  // 收起键盘,别漏

imeApi.dismiss() 最容易漏——输入完不收键盘,键盘会一直占着下半屏把按钮挡住,点击落到键盘上,而失败不报错、只表现为「操作没生效」。USB HID 链路是另一套:usbHidEvent.inputText()(剪贴板粘贴)、usbHidEvent.typeText()(键盘打字,遇非英文自动粘贴)。图色、截图与设备:

函数 作用
image.captureFullScreen() 全屏截图
image.findImage(...) / findImageByColor(...) 找图 / 偏色找图
image.cmpColor(...) 比色
device.getDeviceInfo() 设备信息(取实时屏幕宽高用它)
device.getDeviceId() / getDeviceName() / getModel() / getOSVersion() 设备标识与版本
device.getBattery() / isCharging() 电量
device.applist() 已安装应用列表

坐标和等待,新手最容易翻车的两处

坐标占了新手失败原因的一半。优先用节点自己的坐标、不要手量:

let nd = labelMatch("确认").getOneNodeInfo(5000);
if (nd) { clickPoint(nd.bounds.centerX(), nd.bounds.centerY()); }

确实要写硬坐标时注意三点:基准是截图的像素坐标、不是逻辑分辨率;分辨率变化或横竖屏切换后必须重设屏幕尺寸;竖屏和横屏是两套坐标系,混用要用 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 秒内没出现,可能界面变了"); }

调试、下发、工程化和怎么配

脚本跑不通时按这个顺序查比盯代码快。日志:关键步骤前打 logd("步骤名"),失败时知道停在哪。截图:失败分支里加 image.captureFullScreen(),绝大多数失败是「停在了意料之外的页面」(弹窗、更新提示、登录过期),看到截图基本就定位。习惯上:新脚本先在单台跑、第一步只做最短的动作,跑通再往下加;一上来写完整流程,失败时无法判断是环境还是逻辑问题。

实时日志界面,可以查看每台设备每一步的执行输出

单台跑通后铺开:把编译好的 iec 文件放进中控左下角的脚本目录、右键刷新;选中目标设备(或先在分组栏分组)右键执行脚本。分组在设备超过十台后从“锦上添花”变成“必须”。边界再强调一次:执行脚本要 USB 设备授权、投屏要 USB 投屏授权;同一台设备同一时间只跑一个任务,批量提速靠设备数量、不是单台并发。要和外部系统对接,可以走开放接口——参数和返回值都是 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,固定批处理用脚本加定时。选平台比选单点方案重要。

常见问题

「免越狱」是不是不能做需要登录的操作?

能做。登录属于应用层操作,跟越狱无关。要注意的是登录态会过期,脚本里得处理「发现要求登录 → 走一遍登录流程」这条分支。

要不要一开始就上最复杂的方案?

不要。三条链路各自能做到哪一步不一样,但业务逻辑是同一套。先用最容易跑通的那条把逻辑验证一遍,再按风控和规模要求换链路,换的时候只改事件模块。

脚本里的等待该设多长?

给足上限,不要卡得太紧。getOneNodeInfo(15000) 不会真的等 15 秒,元素出现就返回;但如果上限设 3000,网络稍慢就失败。用户不差这十几秒,但会介意脚本莫名失败。

为什么同样的脚本在另一台设备上就不行?

三个常见原因:分辨率不同但坐标写死了、系统版本不同导致界面差异、那台设备的自动化服务或授权状态不对。按这个顺序查。


写脚本这件事,门槛不在语言。那位做客服外包的朋友最后是这么开始的:先只写「打开后台 → 等订单列表出现 → 截图」,跑通之后再逐步加动作,四个小时后他有了第一个能用的脚本;那四天里花掉的三天,全都耗在「不知道该从哪开始」上。先把环境和服务判断写对,再挑最短的一个动作跑通,剩下的就是加步骤——顺序对了就不会再卡四天。

想先看具体某条链路怎么落地,可以读iOS 免越狱自动化三条链路怎么选;USB HID 的原理和 API 细节,看苹果群控技术拆解:USB HID 的原理与 API 完整教程;一篇完整的实战代码参考,可以读苹果群控发短视频脚本怎么写。

关于 EasyClick:手机自动化 AI 智能体平台,覆盖安卓免 root、iOS 免越狱、鸿蒙 Next 三大生态,提供脚本开发、苹果群控、本地中控投屏与云控系统。→ 了解全部产品

想要真实跑起来?

本文介绍的方案均可在 EasyClick 手机自动化平台落地。官网提供完整文档、开发工具与群控云控产品,免费体验。

访问 EasyClick 官网 →