I have an NSData object that is being allocated with bytes that are representing unsigned integers from 0-255. What is the simplest way to convert these bytes to integers, or, if it is easier, to a string/NSString?
For instance, if I have bytes 0x00, 0x02, and 0x0A in my NSData object, then I would like to have either an integer array of {0,2,10} or the string/NSString "0210".
It seems that there are plenty of ways to turn NSData into ASCII NSStrings,UInt8 arrays, etc, but I cannot for the life of me figure out how to do this simple conversion in Swift.
Edit: Example Code of what I want:
var data:NSData
/*allocate NSData*/
var unsignedInts = [Int]()
/*Allocate each byte of data to an individual index of unsignedInts */
print(unsignedInts)
//should see {0, 1, 10} if bytes are 0x00, 0x01, and 0x0A
It is necessary to create a new Array of Int because Int require more bytes per entry.
var nsData: NSData = NSData(bytes: [0x00, 0x02, 0x0A] as [UInt8], length: 3)
let buffer = UnsafeBufferPointer<UInt8>(start:UnsafePointer<UInt8>(nsData.bytes), count:nsData.length)
print("nsData: \(nsData)")
var intData = [Int]()
intData.reserveCapacity(nsData.length)
var stringData = String()
for i in 0..<nsData.length {
intData.append(Int(buffer[i]))
stringData += String(buffer[i])
}
print("intData: \(intData)")
print("stringData: \(stringData)")
nsData: <00020a>
intData: [0, 2, 10]
stringData: 0210
I can recommend a method like this:
import Foundation
extension NSData {
var byteBuffer : UnsafeMutableBufferPointer<UInt8> {
return UnsafeMutableBufferPointer(start: UnsafeMutablePointer(bytes), count: length)
}
var array : [UInt8] {
return Array(byteBuffer)
}
var intArray : [Int] {
return byteBuffer.map(Int.init)
}
var byteString : String {
return byteBuffer.lazy.map(String.init).joinWithSeparator("")
}
}
Usage:
let data1 = NSData(bytes: [0x48, 0xa1, 0x95, 0xe, 0x1c, 0x38, 0x82] as [UInt8], length: 7) // <48a1950e 1c3882>
print(data1.array) // "[72, 161, 149, 14, 28, 56, 130]"
print(data1.intArray) // "[72, 161, 149, 14, 28, 56, 130]"
print(data1.byteString) // "72161149142856130"
let data2 = NSData(bytes: [0x00, 0x02, 0x0A] as [UInt8], length: 3)
print(data2.array) // "[0, 2, 10]"
print(data2.intArray) // "[0, 2, 10]"
print(data2.byteString) // "0210"
It's very easy, Swift-y and efficient like this.
Note that in some cases you don't actually need an array like this, because you can just access all the values of the data in the same way without allocating a new array (recommended):
data1.byteBuffer[4] // 28
data1.byteBuffer.count // 7
data1.byteBuffer(14) // 3
EDIT: I added a property that returns an Int array, although I can't see a reason you'd need it
Related
I'm using Swift and trying to convert an Int (for example: -1333) to a byte array of 4 bytes. I was able to convert an Int to an array of 8 bytes (-1333 becomes [255, 255, 255, 255, 255, 255, 250, 203]), but I need it to be 4 bytes. I know that there are ways to do this in other languages like Java, but is there a way for Swift? Here's my code: (I used THIS answer)
func createByteArray(originalValue: Int)->[UInt8]{
var result:[UInt8]=Array()
var _number:Int = originalValue
let mask_8Bit=0xFF
var c=0
var size: Int = MemoryLayout.size(ofValue: originalValue)
for i in (0..<size).reversed(){
//at: 0 -> insert at the beginning of the array
result.insert(UInt8( _number&mask_8Bit),at:0)
_number >>= 8 //shift 8 times from left to right
}
return result
}
In Java an integer is always 32-bit, but in Swift it can be 32-bit or 64-bit, depending on the platform. Your code creates a byte array with the same size as that of the Int type, on a 64-bit platform that are 8 bytes.
If you want to restrict the conversion to 32-bit integers then use Int32 instead of Int, the result will then be an array of 4 bytes, independent of the platform.
An alternative conversion method is
let value: Int32 = -1333
let array = withUnsafeBytes(of: value.bigEndian, Array.init)
print(array) // [255, 255, 250, 203]
Or as a generic function for integer type of all sizes:
func byteArray<T>(from value: T) -> [UInt8] where T: FixedWidthInteger {
withUnsafeBytes(of: value.bigEndian, Array.init)
}
Example:
print(byteArray(from: -1333)) // [255, 255, 255, 255, 255, 255, 250, 203]
print(byteArray(from: Int32(-1333))) // [255, 255, 250, 203]
print(byteArray(from: Int16(-1333))) // [250, 203]
However, not like Java using big endian, iOS platform uses 'little endian' instead.
So if it would be
let value: Int32 = -1333
let array2 = withUnsafeBytes(of: value.littleEndian, Array.init)// [203, 250, 255, 255]
You can verify it by wrapping the value into data with below extension and check the bytes
extension FixedWidthInteger {
var data: Data {
let data = withUnsafeBytes(of: self) { Data($0) }
return data
}
}
value.data
Just a reminder for those iOS developers who are digging into memory layout.
It's hard for beginners to understand what is going on in the answers above
public extension FixedWidthInteger {
var bytes: [UInt8] {
withUnsafeBytes(of: bigEndian, Array.init)
}
}
First of all lets rewrite computed property bytes more detailed
var bytes: [UInt8] {
var copyOfSelf = self.bigEndian // this defines the order of bytes in result array
// for int '1' it can be [0, 0, 0, 0, 0, 0, 0, 1]
// or [1, 0, 0, 0, 0, 0, 0, 0]
return withUnsafeBytes(of: copyOfSelf) { urbp: UnsafeRawBufferPointer in
// Array has a constructor
// init<S>(_ s: S) where Element == S.Element, S : Sequence
// so 'UnsafeRawBufferPointer' confirms 'Sequence' protocol
// and 'urbp' can be passed as a parameter to Array constructor
return Array(urbp)
}
}
And here some tests to explain the result
final class FixedWidthIntegerTests: XCTestCase {
func testBytes() {
XCTAssertEqual([0,0,0,0,0,0,0,1], Int(1).bytes)
XCTAssertEqual([255,255,255,255,255,255,255,255], UInt.max.bytes)
XCTAssertEqual([127,255,255,255,255,255,255,255], Int.max.bytes)
XCTAssertEqual([1], Int8(1).bytes)
XCTAssertEqual([0,1], Int16(1).bytes)
XCTAssertEqual([0,0,0,1], Int32(1).bytes)
}
}
I want the hexadecimal representation of a Data value in Swift.
Eventually I'd want to use it like this:
let data = Data(base64Encoded: "aGVsbG8gd29ybGQ=")!
print(data.hexString)
A simple implementation (taken from How to hash NSString with SHA1 in Swift?, with an additional option for uppercase output) would be
extension Data {
struct HexEncodingOptions: OptionSet {
let rawValue: Int
static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
}
func hexEncodedString(options: HexEncodingOptions = []) -> String {
let format = options.contains(.upperCase) ? "%02hhX" : "%02hhx"
return self.map { String(format: format, $0) }.joined()
}
}
I chose a hexEncodedString(options:) method in the style of the existing method base64EncodedString(options:).
Data conforms to the Collection protocol, therefore one can use
map() to map each byte to the corresponding hex string.
The %02x format prints the argument in base 16, filled up to two digits
with a leading zero if necessary. The hh modifier causes the argument
(which is passed as an integer on the stack) to be treated as a one byte
quantity. One could omit the modifier here because $0 is an unsigned
number (UInt8) and no sign-extension will occur, but it does no harm leaving
it in.
The result is then joined to a single string.
Example:
let data = Data([0, 1, 127, 128, 255])
// For Swift < 4.2 use:
// let data = Data(bytes: [0, 1, 127, 128, 255])
print(data.hexEncodedString()) // 00017f80ff
print(data.hexEncodedString(options: .upperCase)) // 00017F80FF
The following implementation is faster by a factor about 50
(tested with 1000 random bytes). It is inspired to
RenniePet's solution
and Nick Moore's solution, but takes advantage of
String(unsafeUninitializedCapacity:initializingUTF8With:)
which was introduced with Swift 5.3/Xcode 12 and is available on macOS 11 and iOS 14 or newer.
This method allows to create a Swift string from UTF-8 units efficiently, without unnecessary copying or reallocations.
An alternative implementation for older macOS/iOS versions is also provided.
extension Data {
struct HexEncodingOptions: OptionSet {
let rawValue: Int
static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
}
func hexEncodedString(options: HexEncodingOptions = []) -> String {
let hexDigits = options.contains(.upperCase) ? "0123456789ABCDEF" : "0123456789abcdef"
if #available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) {
let utf8Digits = Array(hexDigits.utf8)
return String(unsafeUninitializedCapacity: 2 * self.count) { (ptr) -> Int in
var p = ptr.baseAddress!
for byte in self {
p[0] = utf8Digits[Int(byte / 16)]
p[1] = utf8Digits[Int(byte % 16)]
p += 2
}
return 2 * self.count
}
} else {
let utf16Digits = Array(hexDigits.utf16)
var chars: [unichar] = []
chars.reserveCapacity(2 * self.count)
for byte in self {
chars.append(utf16Digits[Int(byte / 16)])
chars.append(utf16Digits[Int(byte % 16)])
}
return String(utf16CodeUnits: chars, count: chars.count)
}
}
}
This code extends the Data type with a computed property. It iterates through the bytes of data and concatenates the byte's hex representation to the result:
extension Data {
var hexDescription: String {
return reduce("") {$0 + String(format: "%02x", $1)}
}
}
My version. It's about 10 times faster than the [original] accepted answer by Martin R.
public extension Data {
private static let hexAlphabet = Array("0123456789abcdef".unicodeScalars)
func hexStringEncoded() -> String {
String(reduce(into: "".unicodeScalars) { result, value in
result.append(Self.hexAlphabet[Int(value / 0x10)])
result.append(Self.hexAlphabet[Int(value % 0x10)])
})
}
}
Swift 4 - From Data to Hex String
Based upon Martin R's solution but even a tiny bit faster.
extension Data {
/// A hexadecimal string representation of the bytes.
func hexEncodedString() -> String {
let hexDigits = Array("0123456789abcdef".utf16)
var hexChars = [UTF16.CodeUnit]()
hexChars.reserveCapacity(count * 2)
for byte in self {
let (index1, index2) = Int(byte).quotientAndRemainder(dividingBy: 16)
hexChars.append(hexDigits[index1])
hexChars.append(hexDigits[index2])
}
return String(utf16CodeUnits: hexChars, count: hexChars.count)
}
}
Swift 4 - From Hex String to Data
I've also added a fast solution for converting a hex String into Data (based on a C solution).
extension String {
/// A data representation of the hexadecimal bytes in this string.
func hexDecodedData() -> Data {
// Get the UTF8 characters of this string
let chars = Array(utf8)
// Keep the bytes in an UInt8 array and later convert it to Data
var bytes = [UInt8]()
bytes.reserveCapacity(count / 2)
// It is a lot faster to use a lookup map instead of strtoul
let map: [UInt8] = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 01234567
0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 89:;<=>?
0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, // #ABCDEFG
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // HIJKLMNO
]
// Grab two characters at a time, map them and turn it into a byte
for i in stride(from: 0, to: count, by: 2) {
let index1 = Int(chars[i] & 0x1F ^ 0x10)
let index2 = Int(chars[i + 1] & 0x1F ^ 0x10)
bytes.append(map[index1] << 4 | map[index2])
}
return Data(bytes)
}
}
Note: this function does not validate the input. Make sure that it is only used for hexadecimal strings with (an even amount of) characters.
Backward compatible and fast solution:
extension Data {
/// Fast convert to hex by reserving memory (instead of mapping and join).
public func toHex(uppercase: Bool = false) -> String {
// Constants (Hex has 2 characters for each Byte).
let size = self.count * 2;
let degitToCharMap = Array((
uppercase ? "0123456789ABCDEF" : "0123456789abcdef"
).utf16);
// Reserve dynamic memory (plus one for null termination).
let buffer = UnsafeMutablePointer<unichar>.allocate(capacity: size + 1);
// Convert each byte.
var index = 0
for byte in self {
buffer[index] = degitToCharMap[Int(byte / 16)];
index += 1;
buffer[index] = degitToCharMap[Int(byte % 16)];
index += 1;
}
// Set Null termination.
buffer[index] = 0;
// Casts to string (without any copying).
return String(utf16CodeUnitsNoCopy: buffer,
count: size, freeWhenDone: true)
}
}
Note that above passes ownership of buffer to returned String object.
Also know that, because Swift's internal String data is UTF16 (but can be UTF8 since Swift 5), all solutions provided in accepted answer do full copy (and are slower), at least if NOT #available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) ;-)
As mentioned on my profile, usage under Apache 2.0 license is allowed too (without attribution need).
This doesn't really answer the OP's question since it works on a Swift byte array, not a Data object. And it's much bigger than the other answers. But it should be more efficient since it avoids using String(format: ).
Anyway, in the hopes someone finds this useful ...
public class StringMisc {
// MARK: - Constants
// This is used by the byteArrayToHexString() method
private static let CHexLookup : [Character] =
[ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" ]
// Mark: - Public methods
/// Method to convert a byte array into a string containing hex characters, without any
/// additional formatting.
public static func byteArrayToHexString(_ byteArray : [UInt8]) -> String {
var stringToReturn = ""
for oneByte in byteArray {
let asInt = Int(oneByte)
stringToReturn.append(StringMisc.CHexLookup[asInt >> 4])
stringToReturn.append(StringMisc.CHexLookup[asInt & 0x0f])
}
return stringToReturn
}
}
Test case:
// Test the byteArrayToHexString() method
let byteArray : [UInt8] = [ 0x25, 0x99, 0xf3 ]
assert(StringMisc.byteArrayToHexString(byteArray) == "2599F3")
A bit different from other answers here:
extension DataProtocol {
func hexEncodedString(uppercase: Bool = false) -> String {
return self.map {
if $0 < 16 {
return "0" + String($0, radix: 16, uppercase: uppercase)
} else {
return String($0, radix: 16, uppercase: uppercase)
}
}.joined()
}
}
However in my basic XCTest + measure setup this was fastest of the 4 I tried.
Going through a 1000 bytes of (the same) random data 100 times each:
Above: Time average: 0.028 seconds, relative standard deviation: 1.3%
MartinR: Time average: 0.037 seconds, relative standard deviation: 6.2%
Zyphrax: Time average: 0.032 seconds, relative standard deviation: 2.9%
NickMoore: Time average: 0.039 seconds, relative standard deviation: 2.0%
Repeating the test returned the same relative results. (Nick and Martins sometimes swapped)
Edit:
Nowadays I use this:
var hexEncodedString: String {
return self.reduce(into:"") { result, byte in
result.append(String(byte >> 4, radix: 16))
result.append(String(byte & 0x0f, radix: 16))
}
}
Maybe not the fastest, but data.map({ String($0, radix: 16) }).joined() does the job. As mentioned in the comments, this solution was flawed.
I want the hexadecimal representation of a Data value in Swift.
Eventually I'd want to use it like this:
let data = Data(base64Encoded: "aGVsbG8gd29ybGQ=")!
print(data.hexString)
A simple implementation (taken from How to hash NSString with SHA1 in Swift?, with an additional option for uppercase output) would be
extension Data {
struct HexEncodingOptions: OptionSet {
let rawValue: Int
static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
}
func hexEncodedString(options: HexEncodingOptions = []) -> String {
let format = options.contains(.upperCase) ? "%02hhX" : "%02hhx"
return self.map { String(format: format, $0) }.joined()
}
}
I chose a hexEncodedString(options:) method in the style of the existing method base64EncodedString(options:).
Data conforms to the Collection protocol, therefore one can use
map() to map each byte to the corresponding hex string.
The %02x format prints the argument in base 16, filled up to two digits
with a leading zero if necessary. The hh modifier causes the argument
(which is passed as an integer on the stack) to be treated as a one byte
quantity. One could omit the modifier here because $0 is an unsigned
number (UInt8) and no sign-extension will occur, but it does no harm leaving
it in.
The result is then joined to a single string.
Example:
let data = Data([0, 1, 127, 128, 255])
// For Swift < 4.2 use:
// let data = Data(bytes: [0, 1, 127, 128, 255])
print(data.hexEncodedString()) // 00017f80ff
print(data.hexEncodedString(options: .upperCase)) // 00017F80FF
The following implementation is faster by a factor about 50
(tested with 1000 random bytes). It is inspired to
RenniePet's solution
and Nick Moore's solution, but takes advantage of
String(unsafeUninitializedCapacity:initializingUTF8With:)
which was introduced with Swift 5.3/Xcode 12 and is available on macOS 11 and iOS 14 or newer.
This method allows to create a Swift string from UTF-8 units efficiently, without unnecessary copying or reallocations.
An alternative implementation for older macOS/iOS versions is also provided.
extension Data {
struct HexEncodingOptions: OptionSet {
let rawValue: Int
static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
}
func hexEncodedString(options: HexEncodingOptions = []) -> String {
let hexDigits = options.contains(.upperCase) ? "0123456789ABCDEF" : "0123456789abcdef"
if #available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) {
let utf8Digits = Array(hexDigits.utf8)
return String(unsafeUninitializedCapacity: 2 * self.count) { (ptr) -> Int in
var p = ptr.baseAddress!
for byte in self {
p[0] = utf8Digits[Int(byte / 16)]
p[1] = utf8Digits[Int(byte % 16)]
p += 2
}
return 2 * self.count
}
} else {
let utf16Digits = Array(hexDigits.utf16)
var chars: [unichar] = []
chars.reserveCapacity(2 * self.count)
for byte in self {
chars.append(utf16Digits[Int(byte / 16)])
chars.append(utf16Digits[Int(byte % 16)])
}
return String(utf16CodeUnits: chars, count: chars.count)
}
}
}
This code extends the Data type with a computed property. It iterates through the bytes of data and concatenates the byte's hex representation to the result:
extension Data {
var hexDescription: String {
return reduce("") {$0 + String(format: "%02x", $1)}
}
}
My version. It's about 10 times faster than the [original] accepted answer by Martin R.
public extension Data {
private static let hexAlphabet = Array("0123456789abcdef".unicodeScalars)
func hexStringEncoded() -> String {
String(reduce(into: "".unicodeScalars) { result, value in
result.append(Self.hexAlphabet[Int(value / 0x10)])
result.append(Self.hexAlphabet[Int(value % 0x10)])
})
}
}
Swift 4 - From Data to Hex String
Based upon Martin R's solution but even a tiny bit faster.
extension Data {
/// A hexadecimal string representation of the bytes.
func hexEncodedString() -> String {
let hexDigits = Array("0123456789abcdef".utf16)
var hexChars = [UTF16.CodeUnit]()
hexChars.reserveCapacity(count * 2)
for byte in self {
let (index1, index2) = Int(byte).quotientAndRemainder(dividingBy: 16)
hexChars.append(hexDigits[index1])
hexChars.append(hexDigits[index2])
}
return String(utf16CodeUnits: hexChars, count: hexChars.count)
}
}
Swift 4 - From Hex String to Data
I've also added a fast solution for converting a hex String into Data (based on a C solution).
extension String {
/// A data representation of the hexadecimal bytes in this string.
func hexDecodedData() -> Data {
// Get the UTF8 characters of this string
let chars = Array(utf8)
// Keep the bytes in an UInt8 array and later convert it to Data
var bytes = [UInt8]()
bytes.reserveCapacity(count / 2)
// It is a lot faster to use a lookup map instead of strtoul
let map: [UInt8] = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 01234567
0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 89:;<=>?
0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, // #ABCDEFG
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // HIJKLMNO
]
// Grab two characters at a time, map them and turn it into a byte
for i in stride(from: 0, to: count, by: 2) {
let index1 = Int(chars[i] & 0x1F ^ 0x10)
let index2 = Int(chars[i + 1] & 0x1F ^ 0x10)
bytes.append(map[index1] << 4 | map[index2])
}
return Data(bytes)
}
}
Note: this function does not validate the input. Make sure that it is only used for hexadecimal strings with (an even amount of) characters.
Backward compatible and fast solution:
extension Data {
/// Fast convert to hex by reserving memory (instead of mapping and join).
public func toHex(uppercase: Bool = false) -> String {
// Constants (Hex has 2 characters for each Byte).
let size = self.count * 2;
let degitToCharMap = Array((
uppercase ? "0123456789ABCDEF" : "0123456789abcdef"
).utf16);
// Reserve dynamic memory (plus one for null termination).
let buffer = UnsafeMutablePointer<unichar>.allocate(capacity: size + 1);
// Convert each byte.
var index = 0
for byte in self {
buffer[index] = degitToCharMap[Int(byte / 16)];
index += 1;
buffer[index] = degitToCharMap[Int(byte % 16)];
index += 1;
}
// Set Null termination.
buffer[index] = 0;
// Casts to string (without any copying).
return String(utf16CodeUnitsNoCopy: buffer,
count: size, freeWhenDone: true)
}
}
Note that above passes ownership of buffer to returned String object.
Also know that, because Swift's internal String data is UTF16 (but can be UTF8 since Swift 5), all solutions provided in accepted answer do full copy (and are slower), at least if NOT #available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) ;-)
As mentioned on my profile, usage under Apache 2.0 license is allowed too (without attribution need).
This doesn't really answer the OP's question since it works on a Swift byte array, not a Data object. And it's much bigger than the other answers. But it should be more efficient since it avoids using String(format: ).
Anyway, in the hopes someone finds this useful ...
public class StringMisc {
// MARK: - Constants
// This is used by the byteArrayToHexString() method
private static let CHexLookup : [Character] =
[ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" ]
// Mark: - Public methods
/// Method to convert a byte array into a string containing hex characters, without any
/// additional formatting.
public static func byteArrayToHexString(_ byteArray : [UInt8]) -> String {
var stringToReturn = ""
for oneByte in byteArray {
let asInt = Int(oneByte)
stringToReturn.append(StringMisc.CHexLookup[asInt >> 4])
stringToReturn.append(StringMisc.CHexLookup[asInt & 0x0f])
}
return stringToReturn
}
}
Test case:
// Test the byteArrayToHexString() method
let byteArray : [UInt8] = [ 0x25, 0x99, 0xf3 ]
assert(StringMisc.byteArrayToHexString(byteArray) == "2599F3")
A bit different from other answers here:
extension DataProtocol {
func hexEncodedString(uppercase: Bool = false) -> String {
return self.map {
if $0 < 16 {
return "0" + String($0, radix: 16, uppercase: uppercase)
} else {
return String($0, radix: 16, uppercase: uppercase)
}
}.joined()
}
}
However in my basic XCTest + measure setup this was fastest of the 4 I tried.
Going through a 1000 bytes of (the same) random data 100 times each:
Above: Time average: 0.028 seconds, relative standard deviation: 1.3%
MartinR: Time average: 0.037 seconds, relative standard deviation: 6.2%
Zyphrax: Time average: 0.032 seconds, relative standard deviation: 2.9%
NickMoore: Time average: 0.039 seconds, relative standard deviation: 2.0%
Repeating the test returned the same relative results. (Nick and Martins sometimes swapped)
Edit:
Nowadays I use this:
var hexEncodedString: String {
return self.reduce(into:"") { result, byte in
result.append(String(byte >> 4, radix: 16))
result.append(String(byte & 0x0f, radix: 16))
}
}
Maybe not the fastest, but data.map({ String($0, radix: 16) }).joined() does the job. As mentioned in the comments, this solution was flawed.
I made an app communicating with a device with Bluetooth Low Energy.
Basically, my app and this device have their own message syntax. They exchange data as bytes and each values in thoses data are reversed.
My problem is that after reversing back value, when I'm converting a 3 bytes value to an Int32, the NSData.getBytes function seems to reverse the value, so I have a wrong value. Example:
var value; // containing [ 0x01, 0xD3, 0x00 ]
value = value.reverse(); // Reverse back : [ 0x00, 0xD3, 0x01 ]
let numb = value.getUInt32(); // Numb will be 119552, instead of 54017...
I don't know if I'm clear enough on my problem, but here is my code. A function which reverse back data and then tries to convert data to int.
// Those functions are in an extension of NSData
func getRUInt32(range:NSRange) -> UInt32
{
var data = message.subdataWithRange(range); // Extract data from main message
data = data.reverse(); // Reverse back data
return data.getUInt32(); // Convert to UInt32
}
func getUInt32() -> UInt32
{
var value:Int32 = 0;
getBytes(&value, length: self.length);
return UInt32(value);
}
func reverse() -> NSData
{
let count:Int = length / sizeof(UInt8);
var array = [UInt8](count: count, repeatedValue: 0);
getBytes(&array, length: count * sizeof(UInt8));
var reversedArray = [UInt8](count: count, repeatedValue: 0);
for index in 0..<array.count
{
reversedArray[index] = array[array.count - index - 1];
}
return NSData(bytes: reversedArray, length: reversedArray.count);
}
You should have a look at the byte order utilities reference:
https://developer.apple.com/library/ios/documentation/CoreFoundation/Reference/CFByteOrderUtils/index.html#//apple_ref/c/func/CFConvertDoubleHostToSwapped
You identify the native format of the current platform using the
CFByteOrderGetCurrent function. Use functions such as
CFSwapInt32BigToHost and CFConvertFloat32HostToSwapped to convert
values between different byte order formats.
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?
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))
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)
}
}
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")
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)
Swift 3/4
let data = Data(bytes: [0x01, 0x02, 0x03])
let byteArray: [UInt8] = data.map { $0 }
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
}
}
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
}