什么是 sonic-ios-bridge

sonic-ios-bridge(以下简称 sib),用于 pc 与 ios 通信的工具,当前版本包含以下基础功能
1、跨平台启动 wda
2、app 列表、安装、卸载、启动
3、设备上下线监听
4、设备详细信息
5、自动挂载开发者镜像
Github 地址
文档

只要你的 ios 有 wda 包,可以使用 sib 唤起之后,用 appium 等框架直接连接对应 url,就能实现跨平台(Linux、Win、Mac)自动化,可以不依赖 mac 和 xcode(当然打 wda 到 ios 的时候需要 xcode,后续跑自动化就不需要了)

Sonic 为什么考虑替换 tidevice

tidevice 是一个非常优秀的工具,但是这边如果单因为 tidevice 让用户部署 python 环境无疑是巨大的浪费,以往不少用户部署 Agent 的时候都是在 python 环境踩了坑。而 go 语言打包的可执行二进制文件可以不需要部署额外的环境运行,并且 go 语言天生的性能与速度都是非常优的。
引用网上一篇文章的话

Go 语言的特点表明它具备轻量级线程实现(Goroutine)、智能标准库、强大的内置安全性,且可使用最简语法进行编程。

所以,如果你自己是 Sonic 平台层面的用户,那么这个新技术带来的效果也许是不痛不痒,最明显收益是部署时不需要 py 环境。
如果你是 python 语言为主做 iOS 自动化,那么还是推荐继续使用 tidevice
如果你是 java 或其他语言为主做 iOS 自动化,那么可以考虑使用 sib,不要搭建 py 环境啦~

尝试自己造轮子

当时查找了大量 usbmuxd、lockdown 的文章,也有参考了 tidevice 的代码。用自己的方式实现了跨平台与 usbmuxd 的通信与 iOS 的 lockdown 通信,做出了

  1. 获取设备详情
  2. 监听设备上下线
  3. 设备端口转发,类似 iproxy

进展还是挺顺利,接下来搞启动 wda

发现宝藏

后面着手进行启动 wda 这最难的功能的时候,testmanager 等等一系列恶心人的逻辑让我痛不欲生,启动 wda 几乎围绕了整个 iOS 协议走了一圈,偶尔逛 github 的时候发现原来有小伙伴用 go 实现过 ios 通信了,叫gidevice,也有伴生了 cli 版。但是因为没有自动挂载开发者镜像的功能,还不能直接接到 sonic 里面,加上某些数据基础还是要结合 sonic 业务来展开。
于是放弃之前自己做的轮子,直接基于 gidevice 的基础上,来做一层 sonic 的 cli 与辅助扩展

扩展功能如下几点:

1. 获取设备型号的中文名称,如 iPhone14,5 -> iPhone 13

整理的映射表
主要从 apple 的 wiki 爬取下来的

2. 自定义输出 json 数据格式与格式化格式

这个主要用来结合 sonic 业务做的

3. 自动挂载开发者镜像

这里参考了 tidevice 的做法,在仓库下载对应版本号的开发者镜像并进行挂载操作

func downloadZip(url, version string) error {
    if versionMap[version] != "" {
        version = versionMap[version]
    }
    _, errT := os.Stat(fmt.Sprintf(".sib/%s.zip", version))
    if errT != nil {
        _, err := os.Stat(".sib")
        if err != nil {
            os.MkdirAll(".sib", os.ModePerm)
        }
        client := http.Client{
            Timeout: DownLoadTimeOut,
        }
        res, err := client.Get(fmt.Sprintf("%s/iOSDeviceSupport/raw/master/DeviceSupport/%s.zip", url, version))
        if err != nil {
            return err
        }
        defer res.Body.Close()
        r := bufio.NewReaderSize(res.Body, 32*1024)
        newFile, err := os.Create(fmt.Sprintf(".sib/%s.zip", version))
        w := bufio.NewWriter(newFile)
        io.Copy(w, r)
        abs, _ := filepath.Abs(newFile.Name())
        errZip := unzip(abs, ".sib", version)
        if errZip != nil {
            os.Remove(newFile.Name())
            return errZip
        }
    }
    return nil
}
4. 设备离线后填充对应序列号到对应字段

离线后只能拿到 deviceID,这个 id 是连接时候通过自增定义的,并不是设备的序列号,需要自己做处理

5. 监听时展示设备详细信息

这个属于扩展功能,搭配 sonic 业务使用

6. 获取 app 信息,包括中文名与长版本号

本来 gidevice 提供的 applist 只有 app 的英文名称和短版本号,需要用 gidevice 另一个 InstallationProxyBrowse 的方法做,并且筛选用户的应用

if device.Properties().SerialNumber != "" {
                result, errList := device.InstallationProxyBrowse(giDevice.WithApplicationType(giDevice.ApplicationTypeUser))
                if errList != nil {
                    return util.NewErrorPrint(util.ErrSendCommand, "appList", errList)
                }
                var appList entity.AppList
                for _, app := range result {
                    a := entity.Application{}
                    mapstructure.Decode(app, &a)
                    appList.ApplicationList = append(appList.ApplicationList, a)
                }
                data := util.ResultData(appList)
                fmt.Println(util.Format(data, isFormat, isJson))
            } else {
                fmt.Println("device no found")
                os.Exit(0)
            }

如何使用

  1. Sonic 平台用户可以不用关心,使用起来跟以往无异,Agent 层会自动调用 sib,并解决了以往版本我还没修复的很多 bug,理论上更稳定。
  2. 非直接使用平台的用户可以前往这里下载对应版本文件
sib run wda -b 你的wda包名

执行之后可以浏览器打开 localhost:9100,有手机画面代表成功
包名如果没有.xctrunner 会自动补全.xctrunner,默认使用第一台手机,如果想指定手机,可以

sib run wda -u 序列号 -b 你的wda包名

如果想更改端口号,可以

sib run wda -h 来查看对应参数

获取序列号可以通过

sib devices

更多用法可以通过sib -h或者 github 的文档查看哦~

结语

这里还是非常感谢 gidevice 作者雷系泡泡,给我省去了不少造轮子的时间。
sib 也将在 v1.3.2-beta 开始接入 sonic,希望大家多多期待


↙↙↙阅读原文可查看相关链接,并与作者交流