I couldn't find a solution to this problem in Swift (all of them are Objective-C, and they deal with pointers which I don't think exist in Swift in the same form). Is there any way to convert a NSData
object into an array of bytes in the form of [Uint8]
in Swift?
7 Answers
You can avoid first initialising the array to placeholder values, if you go through pointers in a slightly convoluted manner, or via the new Array
constructor introduced in Swift 3:
Swift 3
let data = "foo".data(using: .utf8)!
// new constructor:
let array = [UInt8](data)
// …or old style through pointers:
let array = data.withUnsafeBytes {
[UInt8](UnsafeBufferPointer(start: $0, count: data.count))
}
Swift 2
Array(UnsafeBufferPointer(start: UnsafePointer<UInt8>(data.bytes), count: data.length))

- 41,011
- 10
- 62
- 84
-
Though clean and convenient, this uses an NSArray and may cause errors down the road... – califrench May 24 '16 at 03:07
-
2@califrench I'm sorry, I didn't follow. 1) There is no `NSArray` that I can spot, the `bytes` accessor does not return it. 2) What errors would it cause down the road? If you're referring to possible unavailability of `NS*` classes, recall that the question specifies `NSData`. – Arkku May 24 '16 at 21:35
-
1I'm sorry @Arkku, I got confused, I was thinking for a minute that `Array()` was relying on `NSArray` but I don't think it is... The issues down the road would have been trying to access an element using a subscript since `NSArray`s don't know what kind of elements they contain whereas Swift `Array`s do. – califrench May 25 '16 at 22:19
-
This fails with a compiler error. `Cannot invoke initializer for type 'UnsafePointer
' with an argument list of type '(UnsafePointer – Dan Loewenherz Jun 24 '16 at 13:01)'` -
1@Dan It works just fine for me both in Swift 2.2 and Swift 3. In Swift 3 the preceding line is `let data : NSData! = "foo".data(using: String.Encoding.utf8)` – Arkku Jun 26 '16 at 11:11
-
1@Arkku it doesn't compile with latest Swift, I had to change it to: array = data.withUnsafeBytes { bytes in Array( UnsafeBufferPointer(start: bytes, count: data.count) ) } – Zmey Jun 30 '16 at 00:07
-
@Zmey: True, it stopped working at some point during the XCode 8 betas. I've updated the answer with that solution, thanks! – Arkku Aug 25 '16 at 15:43
-
Data conforms to MutableCollection in Swift 3 so you can initialize an Array with it to get an array of UInt8. try `Array(data)` – Leo Dabus Dec 13 '16 at 17:34
-
1@LeoDabus That's basically a different way of saying `[UInt8](data)`, which is already in the answer since the latest edit. (And IMO the clearer syntax with explicit type.) – Arkku Dec 13 '16 at 20:21
-
how to get signed bytes here? – Naga Mallesh Maddali Feb 19 '18 at 12:55
Swift 5 Solution
Data to [bytes]
extension Data {
var bytes: [UInt8] {
return [UInt8](self)
}
}
[bytes] to Data
extension Array where Element == UInt8 {
var data: Data {
return Data(self)
}
}
-
1
-
1
-
Apologies if this is a silly question, but this would produce a copy, right? – Tobi Schweiger Jun 12 '20 at 16:20
-
@TobiSchweiger yes because arrays are always value types in Swift, so arrays always copy – whitneyland Jul 27 '23 at 20:06
It's funny but exist more simple solution. Works in Swift 3. Surely. I've used this today.
data: Data // as function parameter
let byteArray = [UInt8](data)
That's all! :) NSData easily bridged to Data.
UPDATE: (due to Andrew Koster comment)
Swift 4.1, Xcode 9.3.1
Just has been rechecked - all works as expected.
if let nsData = NSData(base64Encoded: "VGVzdFN0cmluZw==", options: .ignoreUnknownCharacters) {
let bytes = [UInt8](nsData as Data)
print(bytes, String(bytes: bytes, encoding: .utf8))
Output: [84, 101, 115, 116, 83, 116, 114, 105, 110, 103] Optional("TestString")

- 1,043
- 12
- 12
-
1
-
This most likely makes a copy of your data. While small, not a big deal. If large, can cause a lot of issues. – Luke Nov 14 '16 at 21:24
-
-
Looks like it works now. I don't know if the compiler changed or if your answer changed, but it compiles. – Andrew Koster Jun 01 '18 at 22:59
-
1@AndrewKoster Yes, you do know: check the edit history of the answer. The original answer has not changed, only the update has been added. (And in case of my answer, where you made the same comment: it has not been edited between 2016 and now.) And of course you should know whether you updated your compiler or not. However, given that the answer has been working in official releases for a couple of years now, I think it is more likely that something else was broken when you previously tried it. In any case, answers cannot predict the future, so I question downvoting for "doesn't work anymore". – Arkku Jun 06 '18 at 15:55
-
Maybe I screwed up pasting this into my code? Maybe I changed something afterward without realizing? I didn't keep a record of every detail of this failed test. I tried a different way, which I posted here, and it worked. I voted this back up, since everyone says that it works now/still. – Andrew Koster Jun 13 '18 at 01:01
You can use the getBytes
function of NSData
to get the byte array equivalent.
As you did not provide any source code, I will use a Swift String contents that has been converted to NSData.
var string = "Hello World"
let data : NSData! = string.dataUsingEncoding(NSUTF8StringEncoding)
let count = data.length / sizeof(UInt8)
// create an array of Uint8
var array = [UInt8](count: count, repeatedValue: 0)
// copy bytes into array
data.getBytes(&array, length:count * sizeof(UInt8))
println(array)
Swift 3/4
let count = data.length / MemoryLayout<UInt8>.size
// create an array of Uint8
var byteArray = [UInt8](repeating: 0, count: count)
// copy bytes into array
data.getBytes(&byteArray, length:count)

- 5,747
- 2
- 30
- 42
Swift 3/4
let data = Data(bytes: [0x01, 0x02, 0x03])
let byteArray: [UInt8] = data.map { $0 }

- 532
- 6
- 8
swift 4 and image data to a byte array.
func getArrayOfBytesFromImage(imageData:Data) ->[UInt8]{
let count = imageData.count / MemoryLayout<UInt8>.size
var byteArray = [UInt8](repeating: 0, count: count)
imageData.copyBytes(to: &byteArray, count:count)
return byteArray
}

- 726
- 12
- 24
You can try
extension Data {
func toByteArray() -> [UInt8]? {
var byteData = [UInt8](repeating:0, count: self.count)
self.copyBytes(to: &byteData, count: self.count)
return byteData
}
}

- 356
- 2
- 9