在 OSX 和 iOS (GAP/GATT) 上发现外设时,CBPeripheral 广告数据不同

Vap*_*olf 5 bluetooth core-bluetooth bluetooth-lowenergy cbperipheral cbcentralmanager

我希望将我的一些 CoreBluetooth 代码从 iOS 移植到 OS X。我已经设置了一组共享的 CoreBluetooth 包装器,iOS 应用程序和 OS X 应用程序使用相同的 BLE 设备以完全相同的方式使用这些包装器.

外围设备扫描:

override init() {
    super.init()
    let queue = DispatchQueue.global(qos: .background)
    centralManager = CBCentralManager(delegate: self, queue: queue)
}

func startScanning() {
    let options: [String: Any] = [CBCentralManagerScanOptionAllowDuplicatesKey: true]
    let deviceUUID = CBUUID(string: Project.Service.Device)
    let recoveryUUID = CBUUID(string: Project.Service.DFURecovery)
    centralManager?.scanForPeripherals(withServices: [deviceUUID, recoveryUUID], options: options)
}

func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber){
    // Inspect advertisementData here to decipher what kind of device
}
Run Code Online (Sandbox Code Playgroud)

在我的 iOS 应用中,didDiscoverPeripheral 被触发。然后当我检查广告数据时,我得到了我期望的所有键/值:

{
    kCBAdvDataIsConnectable = 1;
    kCBAdvDataLocalName = "My Device";
    kCBAdvDataManufacturerData = <34045254 5877f283 43fdd12d ff530978 45000000 000050c2 6500>;
    kCBAdvDataServiceData =     {
        Battery = <64>;
    };
    kCBAdvDataServiceUUIDs =     (
        "My Inforamtion"
    );
}
Run Code Online (Sandbox Code Playgroud)

但是,当从 OS X 应用程序运行相同的代码(扫描相同的设备)时,广告数据会丢失某些字段。

{
    kCBAdvDataIsConnectable = 1;
    kCBAdvDataManufacturerData = <34045254 5877f36e 43fdd12d ff530978 45000000 000050c2 6500>;
}
Run Code Online (Sandbox Code Playgroud)

AdvertedData 中缺少以下键/值对。

kCBAdvDataLocalName
kCBAdvDataServiceData
kCBAdvDataServiceUUIDs
Run Code Online (Sandbox Code Playgroud)

我已经尝试将这些键添加到 scanForPeripherals 调用中,如下所示:

    let options: [String: Any] = [CBCentralManagerScanOptionAllowDuplicatesKey: true,
                                  CBAdvertisementDataLocalNameKey: true,
                                  CBAdvertisementDataServiceDataKey: true,
                                  CBAdvertisementDataServiceUUIDsKey: true]
    let deviceUUID = CBUUID(string: Nightlight.Service.Device)
    let recoveryUUID = CBUUID(string: Nightlight.Service.DFURecovery)
    centralManager?.scanForPeripherals(withServices: [deviceUUID, recoveryUUID], options: options)
Run Code Online (Sandbox Code Playgroud)

没有效果。

Vap*_*olf 6

OSX 可能会在每个设备上多次调用 didDiscoverPeripheral,每次调用都使用不同的 adsData。我想出的解决方案是写一个缓存。

import Foundation
import CoreBluetooth
    
class AdvertisementDataCache {
    /// A dictionary of advertised data to peripheral.uuid
    private var cache: [UUID: [String: Any]] = [:]
    
    /// Appends advertisementData to our cache
    /// for each unique `peripheral.uuid`.
    func append(
        advertisementData: [String: Any],
        to peripheral: CBPeripheral
    ) -> [String: Any] {
        // Join our cached adverts (our `cache` ivar)
        // with new adverts (`advertisementData`)
        let joined = advertisementData.reduce(
            cache[peripheral.identifier] ?? [:]
        ) {
            var all = $0
            all[$1.key] = $1.value
            return all
        }
        
        // write back to our private iVar
        cache[peripheral.identifier] = joined
        
        // Return all cached averts for this peripheral
        return joined
    }
    
    /// Purges all cached avertisements for all peripherals
    func clear() {
        cache.removeAll()
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在 didDiscoverPeripheral 中:

private var advertisementDataCache = AdvertisementDataCache()

public func centralManager(
    _ central: CBCentralManager,
    didDiscover peripheral: CBPeripheral,
    advertisementData adPart: [String: Any],
    rssi RSSI: NSNumber
) {
    let advertisementData = advertisementDataCache.append(
        advertisementData: adPart,
        to: peripheral
    )
    
    // advertisementData now contains all data contained in multiple callbacks
}
Run Code Online (Sandbox Code Playgroud)