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.2
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:
-
CBUUID
has adata
property that returns aData
object. -
Data
has a methodwithUnsafeBytes
which gives me anUnsafeRawBufferPointer
to work on in the context of a closure. I can load that data in that buffer as auuid_t
. - UUID has a constructor that takes a
uuid_t
. I can use that to construct a newUUID
which is returned from the closure.
Here’s my code:
// Swift 5.3 extension CBUUID { var UUIDValue: UUID? { get { guard self.data.count == MemoryLayout<uuid_t>.size else { return nil } return self.data.withUnsafeBytes { (pointer: UnsafeRawBufferPointer) -> UUID in let uuid = pointer.load(as: uuid_t.self) return UUID(uuid: uuid) } } } }
A few notes on what I learned from this:
- Swift sees
uuid_t
as a 16 element tuple ofUInt8
s. Because of that, my first attempt used anUnsafePointer<UInt8>
. -
NSUUID
has a constructor not available inUUID
:init(uuidBytes bytes: UnsafePointer<UInt8>!)
For no particularly good reason, I wanted a path to directly construct aUUID
. - I can manually make a tuple by indexing through an
UnsafeBufferPointer<UInt8>
, like(buffer[0], buffer[1], ...)
. That felt inelegant. - I can load a
uuid_t
because the data is layout compatible with the data provided byCBUUID
. 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 withuuid_t
. Instead of converting these to their 16 byte equivalents, I cop out and return nil.
-
I could have gone from a
CBUUID
→String
→UUID
, 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. ↩ -
The version of
withUsafeBytes
that passes anUnsafePointer
to its closure was deprecated in Swift 5. The replacement passes anUnsafeRawBufferPointer
. ↩