I’ve written previous posts on how to interpret Data
as a simple scalar, or as an array of scalars. Now I want to refine that with generics.
Here’s an example of the code I want to use.
let data:Data = characteristic.value //get a data object from the CBCharacteristic let temperature: Int16 = data.decode() //Custom method to decode into Int16
My previous solution just called a method that required specifying the pointer type for the closure parameter. In order to make this a method on Data
that might return any type, it has to be generic. Here’s an implementation for “trivial” types.
extension Data { func decode<T>() -> T? { guard self.count == MemoryLayout<T>.size else { return nil } return self.withUnsafeBytes { (ptr: UnsafePointer<T>) -> T in return ptr.pointee } } }
As long as the size of the Data
matches the size of type T
, it will interpret it as T
. I could contraint T
to be a FixedWidthInteger
or some known trivial type.
What about arrays of integers? We can use a generic constraint on T
to make sure we are only dealing with an array of FixedWidthIntegers
, which covers all our common cases.
extension Data { func decode<T: FixedWidthInteger>() -> [T]? { let byteCount = self.count let stride = MemoryLayout<T>.stride guard byteCount % stride == 0 else { return nil } return self.withUnsafeBytes({ (ptr: UnsafePointer<T>) -> [T] in let count = byteCount / stride let buffer = UnsafeBufferPointer(start: ptr, count: count) return Array<T>(buffer) }) } }
This time I check to make sure the bytes can be evenly divided by elements of size T
. Here are some uses. Note that you need to provide type annotations to properly interpret the generic type.
let fourBytes = Data([0x01, 0x02, 0x03, 0x04]) let eightBytes = Data([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 ]) let fourByteInt8Array: [Int8]? = fourBytes.decode() // [1, 2, 3, 4] let fourByteInt32: Int32? = fourBytes.decode() // 67305985 let eightByteInt8Array: [Int8]? = eightBytes.decode() // [1, 2, 3, 4, 5, 6, 7, 8] let eightByteInt32Array: [Int32]? = eightBytes.decode() // [67305985, 134678021]