You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 04:27:34