Converting Hex String to NSData in Swift
This is my hex string to Data
routine:
extension String {
/// Create `Data` from hexadecimal string representation
///
/// This creates a `Data` object from hex string. Note, if the string has any spaces or non-hex characters (e.g. starts with '<' and with a '>'), those are ignored and only hex characters are processed.
///
/// - returns: Data represented by this hexadecimal string.
var hexadecimal: Data? {
var data = Data(capacity: count / 2)
let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)
regex.enumerateMatches(in: self, range: NSRange(startIndex..., in: self)) { match, _, _ in
let byteString = (self as NSString).substring(with: match!.range)
let num = UInt8(byteString, radix: 16)!
data.append(num)
}
guard data.count > 0 else { return nil }
return data
}
}
And for the sake of completeness, this is my Data
to hex string routine:
extension Data {
/// Hexadecimal string representation of `Data` object.
var hexadecimal: String {
return map { String(format: "%02x", $0) }
.joined()
}
}
Note, as shown in the above, I generally only convert between hexadecimal representations and NSData
instances (because if the information could have been represented as a string you probably wouldn't have created a hexadecimal representation in the first place). But your original question wanted to convert between hexadecimal representations and String
objects, and that might look like so:
extension String {
/// Create `String` representation of `Data` created from hexadecimal string representation
///
/// This takes a hexadecimal representation and creates a String object from that. Note, if the string has any spaces, those are removed. Also if the string started with a `<` or ended with a `>`, those are removed, too.
///
/// For example,
///
/// String(hexadecimal: "<666f6f>")
///
/// is
///
/// Optional("foo")
///
/// - returns: `String` represented by this hexadecimal string.
init?(hexadecimal string: String, encoding: String.Encoding = .utf8) {
guard let data = string.hexadecimal() else {
return nil
}
self.init(data: data, encoding: encoding)
}
/// Create hexadecimal string representation of `String` object.
///
/// For example,
///
/// "foo".hexadecimalString()
///
/// is
///
/// Optional("666f6f")
///
/// - parameter encoding: The `String.Encoding` that indicates how the string should be converted to `Data` before performing the hexadecimal conversion.
///
/// - returns: `String` representation of this String object.
func hexadecimalString(encoding: String.Encoding = .utf8) -> String? {
return data(using: encoding)?
.hexadecimal
}
}
You could then use the above like so:
let hexString = "68656c6c 6f2c2077 6f726c64"
print(String(hexadecimal: hexString))
Or,
let originalString = "hello, world"
print(originalString.hexadecimalString())
For permutations of the above for earlier Swift versions, see the revision history of this question.
convert hex string to Data on swift
You can avoid converting your data to string and back to data manually encoding your bytes. Try like this:
extension RangeReplaceableCollection where Element == UInt8 {
var hexaEncoded: Self {
reduce(.init()) {
let a = $1 / 16
let b = $1 % 16
return $0 + [a + (a < 10 ? 48 : 87),
b + (b < 10 ? 48 : 87)]
}
}
}
extension Sequence where Element == UInt8 {
var string: String? { String(bytes: self, encoding: .utf8) }
}
let data = Data([0,127,255]) // "007fff"
let hexaEncodedData = data.hexaEncoded // 6 bytes [48, 48, 55, 102, 102, 102]
let hexaEncodedString = hexaEncodedData.string // "007fff"
Converting HEX NSString To NSData
NSString *command = @"72ff63cea198b3edba8f7e0c23acc345050187a0cde5a9872cbab091ab73e553";
command = [command stringByReplacingOccurrencesOfString:@" " withString:@""];
NSMutableData *commandToSend= [[NSMutableData alloc] init];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i;
for (i=0; i < [command length]/2; i++) {
byte_chars[0] = [command characterAtIndex:i*2];
byte_chars[1] = [command characterAtIndex:i*2+1];
whole_byte = strtol(byte_chars, NULL, 16);
[commandToSend appendBytes:&whole_byte length:1];
}
NSLog(@"%@", commandToSend);
How to convert an NSData into an NSString Hex string?
Keep in mind that any String(format: ...)
solution will be terribly slow (for large data)
NSData *data = ...;
NSUInteger capacity = data.length * 2;
NSMutableString *sbuf = [NSMutableString stringWithCapacity:capacity];
const unsigned char *buf = data.bytes;
NSInteger i;
for (i=0; i [sbuf appendFormat:@"%02X", (NSUInteger)buf[i]];
}
If you need something more performant try this:
static inline char itoh(int i) {
if (i > 9) return 'A' + (i - 10);
return '0' + i;
}
NSString * NSDataToHex(NSData *data) {
NSUInteger i, len;
unsigned char *buf, *bytes;
len = data.length;
bytes = (unsigned char*)data.bytes;
buf = malloc(len*2);
for (i=0; i buf[i*2] = itoh((bytes[i] >> 4) & 0xF);
buf[i*2+1] = itoh(bytes[i] & 0xF);
}
return [[NSString alloc] initWithBytesNoCopy:buf
length:len*2
encoding:NSASCIIStringEncoding
freeWhenDone:YES];
}
Swift version
private extension Data {
var hexadecimalString: String {
let charA: UInt8 = 0x61
let char0: UInt8 = 0x30
func byteToChar(_ b: UInt8) -> Character {
Character(UnicodeScalar(b > 9 ? charA + b - 10 : char0 + b))
}
let hexChars = flatMap {[
byteToChar(($0 >> 4) & 0xF),
byteToChar($0 & 0xF)
]}
return String(hexChars)
}
}
How to convert hexadecimal string to an array of UInt8 bytes in Swift?
You can convert your hexa String
back to array of [UInt8]
iterating every two hexa characters and initialize an UInt8
using its string radix initializer. The following implementation assumes the hexa string is well formed:
Edit/update: Xcode 11 • Swift 5.1
extension StringProtocol {
var hexaData: Data { .init(hexa) }
var hexaBytes: [UInt8] { .init(hexa) }
private var hexa: UnfoldSequence {
sequence(state: startIndex) { startIndex in
guard startIndex < self.endIndex else { return nil }
let endIndex = self.index(startIndex, offsetBy: 2, limitedBy: self.endIndex) ?? self.endIndex
defer { startIndex = endIndex }
return UInt8(self[startIndex.. }
}
}
let string = "e0696349774606f1b5602ffa6c2d953f"
let data = string.hexaData // 16 bytes
let bytes = string.hexaBytes // [224, 105, 99, 73, 119, 70, 6, 241, 181, 96, 47, 250, 108, 45, 149, 63]
If you would like to handle malformed hexa strings as well you can make it a throwing method:
extension String {
enum DecodingError: Error {
case invalidHexaCharacter(Character), oddNumberOfCharacters
}
}
extension Collection {
func unfoldSubSequences(limitedTo maxLength: Int) -> UnfoldSequence {
sequence(state: startIndex) { lowerBound in
guard lowerBound < endIndex else { return nil }
let upperBound = index(lowerBound,
offsetBy: maxLength,
limitedBy: endIndex
) ?? endIndex
defer { lowerBound = upperBound }
return self[lowerBound.. }
}
}
extension StringProtocol {
func hexa() throws -> D where D: DataProtocol & RangeReplaceableCollection {
try .init(self)
}
}
extension DataProtocol where Self: RangeReplaceableCollection {
init(_ hexa: S) throws {
guard hexa.count.isMultiple(of: 2) else {
throw String.DecodingError.oddNumberOfCharacters
}
self = .init()
reserveCapacity(hexa.utf8.count/2)
for pair in hexa.unfoldSubSequences(limitedTo: 2) {
guard let byte = UInt8(pair, radix: 16) else {
for character in pair where !character.isHexDigit {
throw String.DecodingError.invalidHexaCharacter(character)
}
continue
}
append(byte)
}
}
}
Usage:
let hexaString = "e0696349774606f1b5602ffa6c2d953f"
do {
let bytes: [UInt8] = try hexaString.hexa()
print(bytes)
let data: Data = try hexaString.hexa()
print(data)
} catch {
print(error)
}
This will print
[224, 105, 99, 73, 119, 70, 6, 241, 181, 96, 47, 250, 108, 45, 149, 63]
16 bytes
NSData from hex String?
With the arrival of Swift3 and the new Foundation Data type, I finally circled back to this:
extension UnicodeScalar {
var hexNibble:UInt8 {
let value = self.value
if 48 <= value && value <= 57 {
return UInt8(value - 48)
}
else if 65 <= value && value <= 70 {
return UInt8(value - 55)
}
else if 97 <= value && value <= 102 {
return UInt8(value - 87)
}
fatalError("\(self) not a legal hex nibble")
}
}
extension Data {
init(hex:String) {
let scalars = hex.unicodeScalars
var bytes = Array(repeating: 0, count: (scalars.count + 1) >> 1)
for (index, scalar) in scalars.enumerated() {
var nibble = scalar.hexNibble
if index & 1 == 0 {
nibble <<= 4
}
bytes[index >> 1] |= nibble
}
self = Data(bytes: bytes)
}
}
Now I can construct Data objects in a fashion similar to their printed form:
Data(hex: "600dBeef")
How do I convert an NSData object with hex data to ASCII in Swift?
Sorry for answering my own question, but I just (accidentally) found an amazing solution to my problem and hopefully this will help someone.
If you have an NSData object with a hex representation of an ASCII string, then all you have to do is write String(data: theNSDataObject, encoding: NSUTF8StringEncoding)
and that is the ASCII string.
Hope this helps someone!
Best way to serialize an NSData into a hexadeximal string
This is a category applied to NSData that I wrote. It returns a hexadecimal NSString representing the NSData, where the data can be any length. Returns an empty string if NSData is empty.
NSData+Conversion.h
#import
@interface NSData (NSData_Conversion)
#pragma mark - String Conversion
- (NSString *)hexadecimalString;
@end
NSData+Conversion.m
#import "NSData+Conversion.h"
@implementation NSData (NSData_Conversion)
#pragma mark - String Conversion
- (NSString *)hexadecimalString {
/* Returns hexadecimal string of NSData. Empty string if data is empty. */
const unsigned char *dataBuffer = (const unsigned char *)[self bytes];
if (!dataBuffer)
return [NSString string];
NSUInteger dataLength = [self length];
NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)];
for (int i = 0; i < dataLength; ++i)
[hexString appendString:[NSString stringWithFormat:@"%02lx", (unsigned long)dataBuffer[i]]];
return [NSString stringWithString:hexString];
}
@end
Usage:
NSData *someData = ...;
NSString *someDataHexadecimalString = [someData hexadecimalString];
This is "probably" better than calling [someData description]
and then stripping the spaces, <'s, and >'s. Stripping characters just feels too "hacky". Plus you never know if Apple will change the formatting of NSData's -description
in the future.
NOTE: I have had people reach out to me about licensing for the code in this answer. I hereby dedicate my copyright in the code I posted in this answer to the public domain.
How to convert Data to hex string in swift
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 usemap()
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 ofString(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)
}
}
}
Related Topics
In Ios13 the Status Bar Background Colour Is Different from the Navigation Bar in Large Text Mode
Replacement For Deprecated -Sizewithfont:Constrainedtosize:Linebreakmode: in iOS 7
Trying to Handle "Back" Navigation Button Action in Ios
Can Afnetworking Return Data Synchronously (Inside a Block)
Anyobject Not Working in Xcode8 Beta6
How to Compare Two Uiimage Objects
Event Handling For iOS - How Hittest:Withevent: and Pointinside:Withevent: Are Related
Swift - Stored Values Order Is Completely Changed in Dictionary
Xcode 4: Create Ipa File Instead of .Xcarchive
How to Use Uivisualeffectview to Blur Image
Looping a Video With Avfoundation Avplayer
Move a View Up Only When the Keyboard Covers an Input Field
Using Ssl in an Iphone App - Export Compliance
How to Scale Down a Uiimage and Make It Crispy/Sharp At the Same Time Instead of Blurry
Get a List of All Contacts on Ios
Making Gcm Work for iOS Device in the Background
Cannot Install Alamofire in New Xcode Project. "No Such Module Alamofire"
Viewdidappear Is Not Called When Opening App from Background