Swift蓝牙代码显示设备为Unknown,但iOS设置可正常显示设备名称
解决CoreBluetooth扫描设备显示Unknown但系统设置可见名称的问题
问题原因
部分蓝牙设备不会在初始广播中直接提供设备名称(peripheral.name为空),但iOS系统会通过以下方式获取并缓存名称:
- 从广播数据的
CBAdvertisementDataLocalNameKey字段提取本地名称 - 后续主动获取设备的Device Information服务(UUID 180A)中的Device Name特征(UUID 2A00)
- 系统缓存之前连接过的设备名称
当前代码仅依赖初始的peripheral.name,未处理上述场景,导致部分设备显示为Unknown。
解决方案
1. 优先从广播数据提取名称
在didDiscover方法中,先从广播数据读取本地名称,再回退到peripheral.name:
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi: NSNumber) { // 优先从广播数据取本地名称,再用peripheral.name,最后默认Unknown let advertisedLocalName = advertisementData[CBAdvertisementDataLocalNameKey] as? String let deviceName = peripheral.name ?? advertisedLocalName ?? "Unknown" os_log("Discovered peripheral: %@", deviceName) // 去重逻辑优化:用identifier判断,而非对象引用 let isPeripheralExists = peripherals.contains { $0.identifier == peripheral.identifier } if !isPeripheralExists { peripherals.append(peripheral) // 监听设备名称变化,后续系统填充名称时更新UI peripheral.addObserver(self, forKeyPath: #keyPath(CBPeripheral.name), options: [.new], context: nil) // 添加名称到列表(如果未被过滤) if !isPeripheralNameFiltered(deviceName) { peripheralNames.append(deviceName) os_log("Peripheral name added: %@", deviceName) } else { os_log("Peripheral name %@ is filtered out", deviceName) } } else { // 如果设备已存在,更新名称(当名称从Unknown变为实际名称时) if let existingIndex = peripherals.firstIndex(where: { $0.identifier == peripheral.identifier }), let existingNameIndex = peripheralNames.firstIndex(where: { $0 == "Unknown" || $0 == advertisedLocalName }) { if deviceName != "Unknown" && peripheralNames[existingNameIndex] != deviceName { peripheralNames[existingNameIndex] = deviceName os_log("Updated peripheral name to: %@", deviceName) } } } }
2. 监听设备名称变化
通过KVO监听CBPeripheral的name属性,当系统后续填充名称时自动更新UI:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { guard keyPath == #keyPath(CBPeripheral.name), let peripheral = object as? CBPeripheral, let newName = change?[.newKey] as? String, newName != "Unknown" else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) return } // 更新peripherals列表中对应设备的名称显示 if let index = peripherals.firstIndex(where: { $0.identifier == peripheral.identifier }) { if peripheralNames.count > index { peripheralNames[index] = newName } else { peripheralNames.append(newName) } os_log("Peripheral name updated via KVO: %@", newName) } } // 销毁时移除观察者 deinit { peripherals.forEach { $0.removeObserver(self, forKeyPath: #keyPath(CBPeripheral.name)) } }
3. 主动获取设备名称(可选)
对于始终不广播名称的设备,可以在扫描到后尝试短暂连接,读取Device Information服务的Device Name特征:
// 在didDiscover中,当名称为Unknown时,尝试连接获取名称 if deviceName == "Unknown" { cbManager?.connect(peripheral, options: nil) } // 修改didConnect方法,仅发现Device Information服务 func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { peripheral.delegate = self peripheral.discoverServices([CBUUID(string: "180A")]) } // 在didDiscoverServices中,读取Device Name特征 func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { guard let services = peripheral.services else { return } for service in services where service.uuid == CBUUID(string: "180A") { peripheral.discoverCharacteristics([CBUUID(string: "2A00")], for: service) } } // 在didUpdateValueFor中,获取名称后断开连接 func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { if characteristic.uuid == CBUUID(string: "2A00"), let nameData = characteristic.value, let deviceName = String(data: nameData, encoding: .utf8) { // 更新UI中的名称 if let index = peripherals.firstIndex(where: { $0.identifier == peripheral.identifier }) { if peripheralNames.count > index { peripheralNames[index] = deviceName } else { peripheralNames.append(deviceName) } } // 获取名称后断开连接 cbManager?.cancelPeripheralConnection(peripheral) } }
关键优化点
- 用设备的
identifier作为去重依据,而非直接比较CBPeripheral对象 - 优先读取广播数据中的本地名称,覆盖初始
peripheral.name为空的情况 - 通过KVO监听名称变化,利用系统后续填充的缓存名称
- 可选:主动连接设备读取官方规范的Device Name特征,确保获取到正确名称
内容的提问来源于stack exchange,提问作者Sana Naghashzadeh
相关产品推荐
相关产品推荐

