Dec 18, 2017

Turning Numbers into Data in Swift

I've written articles1 on getting raw data into Swift types. This can happen when reading data from a device using Core Bluetooth. Writing data to a Bluetooth Device requires going the other direction and turning your numbers or arrays into raw Data. Luckily, going to Data is a bit easier.

The Data() constructor takes a Sequence. A type of UnsafeRawBufferPointer provides a Sequence interface to iterate over the bytes as UInt8. Creating a Data representation is a matter of getting an UnsafeRawBufferPointer.2

If you have a single scalar, there is a global function withUnsafeBytes(of: body:) that presents a closure

var i: Int32 = 500 // must be a var to pass as inout parameter
let data = withUnsafeBytes(of: &i) { (ptr) -> Data in
    // ptr is an UnsafeRawBufferPointer, which is a Collection
    return Data(ptr)
}

Arrays have an instance method to access the bytes:

let array: [UInt32] = [1, 2, 3]
let data = array.withUnsafeBytes { (ptr) -> Data in
    return Data(ptr)
}

Make sure to use the correct width (8 bit, 16 bit, 32 bit) based on what the device is expecting on the other side. Using Int will usually get you 64-bit Integers, but not always. It's best to be explicit when a specific width is necessary.

As always when dealing with network communication, you might need to switch endianness for the device on the other side.

let myArray : [UInt32] = [1, 2, 3]
let bigEndianArray = myArray.map() { $0.bigEndian }
let bigEndianData = bigEndianArray.withUnsafeBytes { (ptr) in
    ptr.map
    return Data(ptr)
}

  1. See: Turning Data Into Arrays In Swift and Turning Data Into Numbers in Swift 

  2. There are other paths to the same route, like getting an UnsafeBufferPointer<T> to pass to Data(buffer:)