1

There are many questions asking about converting Data to an \[UInt8\]. I would like to do the opposite. Convert from [UInt8] to Data.

I looked at the initializers, and these seem to be the most promising:

  1. init(elements: Sequence)
  2. init(bytes: UnsafeRawPointer, count: Int)
  3. init(repeating: UInt8, count: Int)

The problem with #1, is that it takes in any sequence, not just [UInt8]. Therefore, it doesn't give me much confidence that it'll encode my data exactly as I want.

The problem with #2, is that I'm not sure how to convert from [UInt8] to UnsafeRawPointer. Also the unsafe part makes me think this is not the correct approach.

The problem with #3, is that it only allows me to repeat the same exact byte, multiple times. My data contains different bytes.

How do I convert from [UInt8] to Data?

Senseful
  • 86,719
  • 67
  • 308
  • 465
  • Nothing has changed here since this question has been answered many, many times before. – matt Jun 19 '20 at 16:54
  • It's hard to search for these due to the from/to not being taken in to consideration in the title. Thanks for the link to the dupe. [This](https://stackoverflow.com/a/61225449/35690) is the closest answer to what I was looking for. – Senseful Jun 19 '20 at 16:58
  • I had to perform the same search, and I'm an idiot, so how hard can it really be? – matt Jun 19 '20 at 17:00
  • You are the opposite of an idiot. I was trying to avoid `NSData` results and focus on the newer APIs, apparently to my detriment, since that question you linked to both has the newer `Data` API, and explains why it's using `Sequence` instead `[UInt8]`. Apologies for the dupe, and thanks for finding that nugget. – Senseful Jun 19 '20 at 17:12

1 Answers1

0

Use init(elements: Sequence), and treat it as though it was init(elements: [UInt8]).

You'll see this in the Data struct which explains why you can do so:

@inlinable public init<S>(_ elements: S) where S : Sequence, S.Element == UInt8

Original:

Even though the data type is Sequence it looks like the real data type is actually closer to [UInt8]. If you attempt to pass something that isn't UInt8, the compiler will complain.

To verify that this all works as expected, I tried using the repeating overload and verifying that I get the same results:

let data = Data(repeating: 65, count: 2)
let data2 = Data([UInt8(65), UInt8(65)])
let data3 = Data([65, 65])

String(data: data, encoding: String.Encoding.ascii) // Output: "AA"
String(data: data2, encoding: String.Encoding.ascii) // Output: "AA"
String(data: data3, encoding: String.Encoding.ascii) // Output: "AA"

//let data4 = Data([16705]) // Does not compile: Integer literal '16705' overflows when stored into 'UInt8'

//let i: Int = { 16705 }()
//let data5 = Data([i]) // Does not compile: Cannot convert value of type 'Int' to expected element type 'UInt8'

This gives me confidence that it will encode the data correctly.

I'm not sure why the method shows elements as type Sequence instead of [UInt8] especially since doing the latter would make it much more obvious that this is how it behaves (and prevent the need for a question like this in the first place).

Senseful
  • 86,719
  • 67
  • 308
  • 465
  • How would you expect to fit 16705 into a byte 0-255? Type `Data.init([])` and option click the init. It will show the declaration `init(_ elements: S) where S : Sequence, S.Element == UInt8`. Btw Swift is a type inferred language `String(data: data, encoding: .ascii)` – Leo Dabus Jun 19 '20 at 16:51
  • @LeoDabus: Since `[Int]` conforms to `Sequence`, one might assume that the function is "smart" and you can pass an array of Int. For example, maybe it would notice that the value is too big and it would automatically split it up into its corresponding bytes. -- So in this case, I was testing if it would see that `16705` is too big for one byte, and convert it to the bytes `[65, 65]`. This is why I think it would be less confusing if the data type was `[UInt8]` instead of `Sequence` as then one can't make the assumption that the function is "smart." – Senseful Jun 19 '20 at 16:55
  • There is no Data Sequence initializer without the UInt8 Element constrain. Btw An `Int` 64bit requires 8 bytes not 2. With 2 bytes you have an Int16 or an UInt16 – Leo Dabus Jun 19 '20 at 16:58
  • @LeoDabus: Ah excellent point. Do you have any tricks for easily figuring this out on one's own? Command clicking the `Data.init` method doesn't take me to the extension where this is defined. – Senseful Jun 19 '20 at 17:01
  • I can show you how to convert from Numeric types to Data https://stackoverflow.com/a/43244973/2303865 – Leo Dabus Jun 19 '20 at 17:02