Mar 13, 2017

Converting a Core Bluetooth CBUUID to a Foundation UUID

Here's a short and sweet extension that converts a CoreBluetooth CBUUID to a Foundation UUID. It's interesting mainly because of the time it took me to figure out how to use the raw data.1 I had to get familiar with pointer functions in Swift 3.

UUID can be initialzed with a uuid_t. I just had to find a way to convert a CBUUID to a uuid_t. In the end it turned out to be pretty short and simple. Here are the steps:

  1. CBUUID has a data property that returns a Data object.
  2. Data has a method withUnsafeBytes which gives me an UnsafePointer<T> to work on in the context of a closure. I can designate it to be UnsafePointer<uuid_t>.
  3. UUID has a constructor that takes a uuid_t. I can use that to construct a new UUID which is returned from the closure.

Here's my code:

extension CBUUID {
    var UUIDValue: UUID? {
        get {
            if self.data.count != MemoryLayout<uuid_t>.size { return nil }
            return self.data.withUnsafeBytes {
                            (pointer: UnsafePointer<uuid_t>) -> UUID in
                return UUID(uuid:pointer.pointee)
            }
        }
    }
}

Update: Swift 5

The Data method withUnsafeBytes no longer passes an UnsafePointer<T> to the closure. Now the closure receives an UnsafeRawBufferPointer.

extension CBUUID {
    var UUIDValue: UUID? {
        get {
            if self.data.count != MemoryLayout<uuid_t>.size { return nil }
            return self.data.withUnsafeBytes {
                            (buffer: UnsafeRawBufferPointer) -> UUID in
                let pointer = buffer.bindMemory(to: uuid_t.self)
                return UUID(uuid:pointer.baseAddress!.pointee)
            }
        }
    }
}

A few notes on what I learned from this:

  • Swift sees uuid_t as a 16 element tuple of UInt8s. Because of that, my first attempt used an UnsafePointer<UInt8>.
  • NSUUID has a constructor not available in UUID: init(uuidBytes bytes: UnsafePointer<UInt8>!) For no particularly good reason, I wanted a path to directly construct a UUID.
  • I can manually make a tuple by indexing through an UnsafeBufferPointer<UInt8>, like (buffer[0], buffer[1], ...). That felt inelegant.
  • I can point directly to a uuid_t because the data is layout compatible with the data provided by CBUUID. That is, it has the same elements, the same length, in the same order.
  • CBUUID supports some UUIDs which are shorter than 16 bytes. Usually these are defined in the Bluetooth standard. These are not layout compatible with uuid_t. Instead of converting these to their 16 byte equivalents, I cop out and return nil.

  1. I could have gone from a CBUUIDStringUUID, but that seems unnecessary. A uuid is essentially an array of 16 bytes, and I should be able to change the representation of those bytes without a back-and-forth converstion to String.