乐于分享
好东西不私藏

UniApp开发BLE蓝牙,为什么iOS死活拿不到MAC地址?

UniApp开发BLE蓝牙,为什么iOS死活拿不到MAC地址?

前言

    最近在搞 UniApp 的低功耗蓝牙(BLE)开发,安卓端顺风顺水,代码一写,MAC地址到手,通讯畅通无阻。结果一到iOS端,直接“翻车”——连接倒是能连接,但一涉及到需要校验 MAC 地址的协议,直接报错。排查了半天才发现,原来是苹果在搞“隐私保护”。今天就来和大家好好唠唠这个让无数开发者头秃的“MAC地址差异”问题,以及我们该如何优雅地填坑。

01 核心矛盾:Android vs iOS 的“平行宇宙”

在 UniApp中调用 uni.getBluetoothDevices 或监听 uni.onBluetoothDeviceFound 时,返回的 deviceId 字段在两个平台上完全是两个概念。
Android 端:deviceId = MAC 地址格式通常是 AA:BB:CC:DD:EE:FF。简单粗暴,拿来就能用,直接作为设备的唯一身份证。
iOS 端:deviceId = UUID (通用唯一识别码)格式通常是 E1C2A3B4-… 这种长字符串。这是苹果系统为了保护用户隐私,随机生成的一个临时标识符。它不是真实的物理 MAC 地址,而且每次重新扫描或重连,这个 ID 甚至可能会变(取决于系统版本和缓存策略)。
结论:如果你的业务逻辑强依赖 MAC 地址(比如某些老旧的硬件协议),在 iOS 上直接用 deviceId 是绝对行不通的。

02 为什么苹果要这么干?

苹果在 iOS 13 之后大大加强了蓝牙隐私权限。为了防止 App 开发者通过蓝牙 MAC 地址追踪用户的物理位置和行为习惯,Apple 屏蔽了获取真实 MAC 的 API。
所以,别费劲去破解 deviceId 了,那个 UUID 就是 iOS 能给你的极限。

03 解决方案:如何在 iOS 上获取“真身”?

既然 deviceId 靠不住,我们得想办法从别的地方把 MAC 地址“抠”出来。这里有几招实战中总结的方案:

方案一:从广播数据(advertisData)中“偷”数据

这是最常用的一招。很多蓝牙硬件在广播包(Advertising Packet)里,会携带厂商自定义的数据。MAC 地址往往就藏在 ManufacturerData 或者 ServiceData 里。
操作步骤:
1.抓包分析:使用BLE Debug 等工具查看蓝牙广播包,确认 MAC 地址藏在哪个字段(通常是 ManufacturerData)。
2.代码解析:在 UniApp 中监听设备发现,读取 advertisData。
3.进制转换:拿到的数据通常是 ArrayBuffer,需要转成 16 进制字符串。
代码片段示例:
uni.onBluetoothDeviceFound((res) => {const device = res.devices[0];// Android 直接拿if (uni.getSystemInfoSync().platform === 'android') {console.log('Android MAC:', device.deviceId);}// iOS 需要解析广播数据else {// 注意:advertisData 是 ArrayBuffer,需要转换// 这里假设 MAC 地址在 ManufacturerData 的特定偏移量位置// 具体偏移量需要找硬件工程师确认const mac = parseMacFromAdData(device.advertisData);console.log('iOS 解析出的 MAC:', mac);}});// 伪代码:ArrayBuffer 转 Hex 并截取 MACfunction parseMacFromAdData(buffer) {// 实际开发中需要根据协议文档,找到 MAC 所在的字节位置// 比如从第 10 个字节开始截取 6 个字节// ...转换逻辑...return macString;}

方案二:连接后“握手”获取

如果广播包里没藏 MAC 地址,那就只能“先上车后补票”。
1.先利用 iOS 的 deviceId (UUID) 连接设备。
2.连接成功后,通过蓝牙特征值(Characteristic)发送一条“获取设备信息”的指令。
3.硬件端收到指令后,返回包含真实 MAC 地址的数据包。
4.App 端收到后,将 MAC 地址与当前的 UUID 绑定缓存,后续通讯就用这个 MAC。

方案三:利用设备名称(简单粗暴)

如果硬件允许,可以让厂家在广播名称(localName)里做文章。比如设备名称直接叫 MyDevice_AA:BB:CC。虽然 iOS 拿不到 MAC,但能拿到 localName,直接字符串截取就能拿到 MAC。
缺点:设备名称长度有限,且不够安全,容易被伪造。

04 避坑指南

●缓存策略:iOS 的 UUID 虽然会变,但在短时间内(未重启蓝牙或未清除缓存)通常是稳定的。建议在 App 本地(uni.setStorage)存一份 UUID -> MAC 的映射表,避免每次都重新解析。
●权限问题:Android 12+ 对蓝牙权限做了细分(BLUETOOTH_SCAN, BLUETOOTH_CONNECT),别忘了在 manifest.json 里配置好,否则连扫描都扫不到。
●加密传输:如果在广播包里透传 MAC 地址,建议让硬件端做个简单的加密(如 AES),防止被恶意第三方扫描到用户的真实物理地址。

05 总结

做蓝牙开发,尤其是跨平台开发,“ deviceId 不靠谱”是必须建立的第一认知。
●Android:拿来主义,直接用。
iOS:曲线救国,解析广播包或连接后握手。
最后推荐一款蓝牙开发调试工具