如何在iOS应用中获取iBeacon的原始字节数据数组?
一、不修改iBeacon的排查与解决
检查CoreBluetooth扫描配置
先确保CoreBluetooth扫描权限和配置正确:let centralManager = CBCentralManager(delegate: self, queue: DispatchQueue.main) // 扫描所有外设并允许重复上报,避免漏扫 centralManager.scanForPeripherals(withServices: nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey: true])同时确认Info.plist中添加了iOS 13+要求的蓝牙权限描述:
NSBluetoothAlwaysUsageDescription。正确解析Manufacturer Data
iBeacon的广播数据属于苹果厂商(ID: 0x004C)的Manufacturer Data,你可能之前没正确匹配解析。在扫描回调里提取完整原始字节:func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { guard let manufacturerData = advertisementData[CBAdvertisementDataManufacturerDataKey] as? Data else { return } // 验证厂商ID(小端序对应字节[0x4C, 0x00]) guard manufacturerData.count >= 2, manufacturerData.subdata(in: 0..<2) == Data([0x4C, 0x00]) else { return } // manufacturerData就是完整的iBeacon原始字节数组 let rawBytes = manufacturerData // 标准iBeacon数据占25字节,自定义电池电量应在这之后 if rawBytes.count > 25 { let batteryLevel = rawBytes[25] // 按你的实际字节偏移调整 print("电池电量:\(batteryLevel)%") } }如果还是拿不到Manufacturer Data,大概率是iBeacon的广播包长度超过BLE最大限制(31字节),或者设备广播配置有误。
CoreLocation的限制说明
CoreLocation的CLBeacon对象只封装了标准iBeacon字段(UUID、Major、Minor、RSSI等),不会暴露原始广播数据,所以这条路走不通,只能依赖CoreBluetooth。
二、修改iBeacon的可靠方案
如果上面的方法无效,修改iBeacon的广播配置是最稳妥的选择,有两种常用方式:
扩展iBeacon的Manufacturer Data
标准iBeacon的Manufacturer Data固定为25字节:0-1字节:厂商ID(0x4C00)
2字节:iBeacon类型(0x02)
3字节:数据长度(0x15)
4-19字节:UUID
20-21字节:Major
22-23字节:Minor
24字节:校准RSSI
你可以在第25字节之后添加1字节的电池电量数据(注意总长度不能超过31字节),这样CoreBluetooth扫描到的Manufacturer Data就会包含这部分,按上面的解析代码即可获取。同时广播iBeacon和自定义BLE服务
让iBeacon同时作为BLE外设,广播一个自定义服务,并把电池电量存放在该服务的特征中。iOS端先通过CoreLocation定位到目标iBeacon,再用CoreBluetooth连接外设读取特征值:// CoreLocation扫描到目标iBeacon后触发连接 func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) { for beacon in beacons where beacon.proximityUUID == UUID(uuidString: "你的目标UUID") { centralManager.scanForPeripherals(withServices: [CBUUID(string: "自定义服务UUID")], options: nil) } } // 连接外设后读取电池电量特征 func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { peripheral.delegate = self peripheral.discoverServices([CBUUID(string: "自定义服务UUID")]) } func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { guard let service = peripheral.services?.first else { return } peripheral.discoverCharacteristics([CBUUID(string: "电池电量特征UUID")], for: service) } func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { guard let characteristic = service.characteristics?.first else { return } peripheral.readValue(for: characteristic) } func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { guard let data = characteristic.value, data.count > 0 else { return } let batteryLevel = data[0] print("电池电量:\(batteryLevel)%") }
内容的提问来源于stack exchange,提问作者Sunbey13

