Search code examples
iosswiftbluetooth-lowenergycore-bluetooth

CoreBluetooth - get BLE Genertic Attribute Profile raw bytes


I have a view controller that scans for BLE beacons that are advertising only and do now allow incoming connections. I can see the bytes that I want by inspecting the service data section (CBAdvertisementDataServiceDataKey) of the advertisement, but I can't actually access the raw bytes of the data as variable.

Here is the CM callback:

func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
    var advBytes: String!
    
    if let dictionary = advertisementData[CBAdvertisementDataServiceDataKey] as? [String: Any] {
        if let gatt = dictionary["Generic Attribute Profile"] as? [String: String] {
            advBytes = gatt["bytes"]?[2...]
        } else {
            print("Can't convert byte array to string")
        }
    }
    else {
        print("Couldn't decode advertisement \(advertisementData[CBAdvertisementDataServiceDataKey])")
    }

When the code is run, the console outputs:

Couldn't decode advertisement Optional({
    "Generic Attribute Profile" = {length = 7, bytes = 0x6413000a000000};
})

How can I access this bytes part of the GATT as a variable?


Solution

  • According to the doc of CBAdvertisementDataServiceDataKey, the value is [CBUUID: Data].

    So it should be:

    if let dictionary = advertisementData[CBAdvertisementDataServiceDataKey] as? [CBUUID: Data] {
    
    }
    

    Now, if I remember correctly, the Service ID you are looking for GATT, is 1801. It's just that Apple translate it automatically into a human readable "Generic Attribute Profile". You can test it with this:

    let service = CBUUID(string: "1801")
    print("Service UUID: \(service.uuidString) - \(service)")
    

    They must have override description of CBUUID to print "words" when it's a known UUID.

    Now, you could do this:

    let genericAttributeProfileData = dictionary[CBUUID(string: "1801")]
    let subData = genericAttributeProfileData[0...0]
    // etc.