Swift3 convert string value to hexadecimal string - ios

I am new to Swift and I want to convert a string into hexadecimal string. I have found a Objective-C function to a string into hexadecimal.
NSString * str = #"Say Hello to My Little Friend";
NSString * hexString = [NSString stringWithFormat:#"%#",
[NSData dataWithBytes:[str cStringUsingEncoding:NSUTF8StringEncoding]
length:strlen([str cStringUsingEncoding:NSUTF8StringEncoding])]];
for (NSString * toRemove in [NSArray arrayWithObjects:#"<", #">", #" ", nil])
hexString = [hexString stringByReplacingOccurrencesOfString:toRemove withString:#""];
NSLog(#"hexStr:%#", hexString);
Now I am unable to convert this function in Swift. Currently I am using Swift3.
Please someone can do this for me?

This produces the same output as the ObjC version
let str = "Say Hello to My Little Friend"
let data = Data(str.utf8)
let hexString = data.map{ String(format:"%02x", $0) }.joined()

Details
Xcode 11.2.1 (11B500), Swift 5.1
Solution
extension String {
func toHexEncodedString(uppercase: Bool = true, prefix: String = "", separator: String = "") -> String {
return unicodeScalars.map { prefix + .init($0.value, radix: 16, uppercase: uppercase) } .joined(separator: separator)
}
}
Usage
let str = "Hello, playground"
print(str.toHexEncodedString()) // 48656C6C6F2C20706C617967726F756E64
print(str.toHexEncodedString(uppercase: false, prefix: "0x", separator: " ")) // 0x48 0x65 0x6c 0x6c 0x6f 0x2c 0x20 0x70 0x6c 0x61 0x79 0x67 0x72 0x6f 0x75 0x6e 0x64

Convert your string to NSData like this.
let myString = "ABCDEF"
let data = myString.dataUsingEncoding(NSUTF8StringEncoding)
Then convert the NSData to hexadecimal string using following method -
func hexFromData(data:NSData) -> String {
let p = UnsafePointer<UInt8>(data.bytes)
let len = data.length
var str: String = String()
for i in 0...len-1 {
str += String(format: "%02.2X", p[i])
}
return str
}
Hope this helped.

Please try this,
let string = "Say Hello to My Little Friend"
let data = string.dataUsingEncoding(NSUTF8StringEncoding)
print(data)
and its output is:
Optional(<53617920 48656c6c 6f20746f 204d7920 4c697474 6c652046 7269656e 64>)

Swift 3
let string = "Hello World!"
let data = string.data(using: String.Encoding.utf8)
let hexString = data?.hex
extension Data {
var hex: String {
var string = ""
#if swift(>=3.1)
enumerateBytes { pointer, index, _ in
for i in index..<pointer.count {
string += String(format: "%02x", pointer[i])
}
}
#else
enumerateBytes { pointer, count, _ in
for i in 0..<count {
string += String(format: "%02x", pointer[i])
}
}
#endif
return string
}
}

Related

How to separate HTML string into array or dictionary by Swift3?

I got HTML string from API like this:
let a: String = "https://www.google.com.tw "
let b: String = "#Tim Hello Tim"
let c: String = "#Tim https://www.google.com.tw "
let splitedArray1: [String] = a.componentsSeparatedByString("?????") //splited string which is the best
let splitedArray2: [String] = b.componentsSeparatedByString("?????") //splited string which is the best
let splitedArray3: [String] = c.componentsSeparatedByString("?????") //splited string which is the best
I want to separate link from them and get the data like following
print(splitedArray1) //["https://www.google.com.tw","https://www.google.com.tw"]
print(splitedArray2) //["myAppName://app/user/aa3b77411825b88b318d77gg","#Tim ","Hello Tim"]
print(splitedArray3) //["myAppName://app/user/aa3b77411825b88b318d77gg","#Tim ","https://www.google.com.tw","https://www.google.com.tw "]
Possible solution: Use NSAttributedString then enumerate on the NSLinkAttributeName, if there isn't, it means there were no link tag, so you just keep the "string", else, you add the link, then the string.
Quickly written in Playground:
let a: String = "https://www.google.com.tw "
let b: String = "#Tim Hello Tim"
let c: String = "#Tim https://www.google.com.tw "
let values:[String] = [a, b, c]
for aHTMLString in values
{
let attributedString = try! NSAttributedString.init(data: aHTMLString.data(using: .utf8)!,
options: [.documentType: NSAttributedString.DocumentType.html],
documentAttributes: nil)
var retValues = [String]()
attributedString.enumerateAttribute(.link,
in: NSRange(location: 0, length: attributedString.string.count),
options: [],
using: { (attribute, range, pointerStop) in
if let attribute = attribute as? URL
{
retValues.append(attribute.absoluteString)
}
let subString = (attributedString.string as NSString).substring(with: range)
retValues.append(subString)
})
print("*** retValues: \(retValues)")
}
let targetResult1 = ["https://www.google.com.tw","https://www.google.com.tw"]
let targetResult2 = ["myAppName://app/user/aa3b77411825b88b318d77gg","#Tim ","Hello Tim"]
let targetResult3 = ["myAppName://app/user/aa3b77411825b88b318d77gg","#Tim ","https://www.google.com.tw","https://www.google.com.tw "]
print("targetResult1: \(targetResult1)")
print("targetResult2: \(targetResult2)")
print("targetResult3: \(targetResult3)")
Output:
*** retValues: ["https://www.google.com.tw/", "https://www.google.com.tw "]
*** retValues: ["myappname://app/user/aa3b77411825b88b318d77gg", "#Tim ", "Hello Tim"]
*** retValues: ["myappname://app/user/aa3b77411825b88b318d77gg", "#Tim ", "https://www.google.com.tw/", "https://www.google.com.tw "]
targetResult1: ["https://www.google.com.tw", "https://www.google.com.tw"]
targetResult2: ["myAppName://app/user/aa3b77411825b88b318d77gg", "#Tim ", "Hello Tim"]
targetResult3: ["myAppName://app/user/aa3b77411825b88b318d77gg", "#Tim ", "https://www.google.com.tw", "https://www.google.com.tw "]
There are small differences, I copied your "target" (splitArray), and it's missing a space in the last one, and my code tend to add a final "/" on links.
I've created this extension to get url.
extension String {
func getUrl() -> String? {
let rss = self.split { (char) -> Bool in
return char == ">"
}
if let final = rss.last?.split(separator: "<"), let first = final.first {
return String(first)
}
return nil
}
var hrefUrl: String {
let matchString = "=\""
let arrComponents = self.components(separatedBy: matchString)
if let first = arrComponents.last, let str = first.split(separator: "\"").first {
return String(str)
}
return ""
}
}
Usage:
let a: String = "https://www.google.com.tw "
a.getUrl() //output: https://www.google.com.tw
//or
a.hrefUrl //output: https://www.google.com.tw
Simple solution without libraries - just use String.replaceOccurences(of:... to replace odd strings like a href, a into split parameter (like "|") and then use componentsSeparatedByString("|") to get your components.
Use the regular expression for extracting the URL. Below I have written the code snippet.
let text = "<a href=\"https://www.google.com\">"
let regex = try! NSRegularExpression(pattern: "<a[^>]+href=\"(.*?)\"[^>]*>")
let range = NSMakeRange(0, text.characters.count)
let matches = regex.matches(in: text, range: range)
for match in matches {
let strURL = (text as NSString).substring(with: match.rangeAt(1))
print(strURL)
}

How to replace Email id text before # with * in swift 3.0

Hello I have not get perfect solution for how to replace Email Id text before # with * like
suppose my email id is XXXXXXX#gmail.com then my output I want like *******#gmail.com
I try like this
let delimiter = "#"
let newstr = "Himmoradiya#gmail.com"
var token = newstr.components(separatedBy: delimiter)
let newQuery = (token[0] as NSString).replacingCharacters(
in: NSMakeRange(0,token[0].characters.count), with: "*")
Any suggestion is accepted.
Thank you.
Use init(repeating:count:)
let newstr = "Himmoradiya#gmail.com"
var token = newstr.components(separatedBy: "#")
let newQuery = String(repeating: "*", count: token[0].characters.count) //check array length before using index
print(newQuery + "#" + token[1] ) // *******#gmail.com
Write this code:
let aString = "This is my string"
let newString = aString.replacingOccurrences(of: "#", with: "*")
Use this function .
func getStarred(_ email : String ) -> String
{
var didFoundATRO = false
var tempString = ""
for char in email.characters
{
if char == "#"
{
didFoundATRO = true
}
if !didFoundATRO
{
tempString += "*"
}
else
{
tempString.append(char)
}
}
return tempString
}
You can do like,
let delimiter = "#"
let newstr = "Himmoradiya#gmail.com"
let arr = newstr.components(separatedBy: delimiter)
let resultStr = "xxxxxx" + "#" + arr[1]
print(resultStr)
let delimiter = "#"
var newstr = "Himmoradiya#gmail.com"
let arr = newstr.components(separatedBy: delimiter)
let strMail = arr.first!
let strReplaceStr = String(repeating: "*", count: strMail.characters.count)
newstr = newstr.replacingOccurrences(of: strMail, with: strReplaceStr)
and output (newstr) will be : ***********#gmail.com
I have created a string extension like...
extension String {
var hideEmailPrefix: String {
if self.contains("#") {
var part = self.components(separatedBy: "#")
let newText = String(repeating: "*", count: part[0].count)
return newText + "#" + part[1]
}
return self
}
}
Then in my ViewController:
override func viewDidLoad() {
super.viewDidLoad()
let email = "abc#gmail.com"
print(email.hideEmailPrefix) // ***#gmail.com
}
var maskEmail: String {
let email = self
let components = email.components(separatedBy: "#")
var maskEmail = ""
if let first = components.first {
maskEmail = String(first.enumerated().map { index, char in
return [0, 1, first.count - 1, first.count - 2].contains(index) ?
char : "*"
})
}
if let last = components.last {
maskEmail = maskEmail + "#" + last
}
return maskEmail
}
var maskPhoneNumber: String {
return String(self.enumerated().map { index, char in
return [0, 3, self.count - 1, self.count - 2].contains(index) ?
char : "*"
})
}

Replace numeric of different length in a string with different text length

Problem: Numeric is of different length could be 1, 200, 1000, 39 99995 etc. And need to replace with a text eg. "Apple" which is of different lenth comapring to the numeric values.
let str: String = "hello i have 1313 object of 10 string class with 1 object, similar to 9999 errors"
Expected Result = "hello i have Apple object of Apple string class with Apple object, similar to Apple errors"
I have tried with below code:
var originalString: String = "hello i have 1313 object of 10 string class with 1 object, similar to 9999 errors"
let strippedString: NSMutableString = NSMutableString(capacity: originalString.characters.count)
var numArray: [String] = []
var locArray: [NSNumber] = []
var scanner: NSScanner = NSScanner(string:originalString)
let numbers: NSCharacterSet = NSCharacterSet(charactersInString: "0123456789")
while scanner.atEnd == false {
var buffer: NSString?
if scanner.scanCharactersFromSet(numbers, intoString: &buffer) {
strippedString.appendString(buffer! as String)
numArray.append(buffer! as String)
locArray.append(scanner.scanLocation)
}
else {
scanner.scanLocation = (scanner.scanLocation + 1)
}
}
for (index, _) in numArray.enumerate() {
var loc : Int = Int(locArray[index] ) - (String(numArray[index]).characters.count)
let len = String(numArray[index]).characters.count
let dupStr = "Apple"
if(index != 0 && len != dupStr.characters.count)
{
loc = loc + (dupStr.characters.count - len) + 1
}
originalString.replaceRange(originalString.startIndex.advancedBy(loc)..<originalString.startIndex.advancedBy(loc + len), with: dupStr)
}
print(originalString)
Swift 2
NSScanner is great, but if you want a simpler solution for this task, you could use componentsSeparatedByString, map, Int() and joinWithSeparator, like this:
let originalString = "hello i have 1313 object of 10 string class with 1 object, similar to 9999 errors"
let tokens = originalString.componentsSeparatedByString(" ")
let newTokens = tokens.map { (token) -> String in
if let _ = Int(token) {
return "Apple"
}
return token
}
let result = newTokens.joinWithSeparator(" ")
print(result)
Prints:
hello i have Apple object of Apple string class with Apple object, similar to Apple errors
There's also a short version for the mapping:
let newTokens = tokens.map { Int($0) != nil ? "Apple" : $0 }
Swift 3
componentsSeparatedByString(_:) is now components(separatedBy:), and joinWithSeparator(_:) is now joined(separator:).
let tokens = originalString.components(separatedBy: " ")
let newTokens = tokens.map { Int($0) != nil ? "Apple" : $0 }
let result = newTokens.joined(separator: " ")
var str: String = "hello i have 1313 object of 10 string class with 1 object, similar to 9999 errors"
var comp: [AnyObject] = [AnyObject](array: str.componentsSeparatedByString(" "))
for var i = 0; i < comp.count; i++ {
var numberRegex: NSRegularExpression = NSRegularExpression.regularExpressionWithPattern("^[0-9]", options: NSRegularExpressionCaseInsensitive, error: nil)
var regexMatch: Int = numberRegex.numberOfMatchesInString(comp[i], options: 0, range: NSMakeRange(0, (String(comp[i])).length))
if regexMatch != 0 {
comp[i] = "Apple"
}
}
var result: String = comp.componentsJoinedByString(" ")
print(result)

string replace phone number iOS swift

I import a phone-number from "Contacts" and save in NSString.
this string contains white-space and I try to delete them using the method:
numero = numero.stringByReplacingOccurrencesOfString(" ", withString: "")
this method doesn't work.
func sostituisci( stringa: NSString! ) -> NSString
{
var numero: NSString = ""
NSLog(stringa)
numero = ((stringa as String).stringByReplacingOccurrencesOfString(" ", withString: "") as NSString)
NSLog(numero)
return numero
}
the output unchanged
log
2014-11-05 17:54:50.734 HappyRicarica[33438:3119446] (327) 124-3503
2014-11-05 17:54:50.737 HappyRicarica[33438:3119446] (327) 124-3503
I suspect that the space character in your string is not really a space. Try adding this after NSLog(string) to see what the unicode scalar values are for the characters in your string:
for uni in (stringa as String).unicodeScalars {
println("\(uni) = \(uni.value)")
}
The expected output for "(327) 124-3503" is:
( = 40
3 = 51
2 = 50
7 = 55
) = 41
= 32
1 = 49
2 = 50
4 = 52
- = 45
3 = 51
5 = 53
0 = 48
3 = 51
From your comment, your space has value 160 instead of 32. You could remove that with:
numero = stringa.stringByReplacingOccurrencesOfString(String(Character(UnicodeScalar(160))), withString: "")
update: Xcode 7.2 • Swift 2.1.1
extension String {
var numbersOnly: String {
return componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: "1234567890")
.invertedSet)
.joinWithSeparator("")
}
var numbersExempt: String {
return componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: "1234567890"))
.joinWithSeparator("")
}
var charactersOnly: String {
return componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ").invertedSet).joinWithSeparator("")
}
var charactersExempt: String {
return componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")).joinWithSeparator("")
}
func keep(keepIt: String) -> String {
return componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: keepIt).invertedSet).joinWithSeparator("")
}
func exclude(excludeIt: String) -> String {
return componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: excludeIt)).joinWithSeparator("")
}
}
let phoneNumber = "+1 (555) 555 - 5555".numbersOnly
print(phoneNumber) // "15555555555"
let excludePlusMinus = "+1 (555) 555-5555".exclude("+-") // "1 (555) 5555555"
let keepWhatever = "+1 (555) 555-5555".keep("()-+") // "+()-"
you can also use your function to subtract only spaces with some adjustments. Try like this:
func sostituisci(stringa: String) -> String {
return stringa.stringByReplacingOccurrencesOfString(" ", withString: "")
}
sostituisci("1 234 567 8901") // "12345678901"
or like an extension:
extension String {
var sostituisci: String {
return stringByReplacingOccurrencesOfString(" ", withString: "")
}
}
let phoneNumber2 = "1 234 567 8901".sostituisci
NSString is not String, so you should use:
numero = ((numero as String).stringByReplacingOccurrencesOfString(" ", withString: "") as NSString)
did you try :
let str = "XX XX XX XX XX"
let separated = str.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
let finalStr = "".join(separated)

Converting Hex String to NSData in Swift

I got the code to convert String to HEX-String in Objective-C:
- (NSString *) CreateDataWithHexString:(NSString*)inputString {
NSUInteger inLength = [inputString length];
unichar *inCharacters = alloca(sizeof(unichar) * inLength);
[inputString getCharacters:inCharacters range:NSMakeRange(0, inLength)];
UInt8 *outBytes = malloc(sizeof(UInt8) * ((inLength / 2) + 1));
NSInteger i, o = 0;
UInt8 outByte = 0;
for (i = 0; i < inLength; i++) {
UInt8 c = inCharacters[i];
SInt8 value = -1;
if (c >= '0' && c <= '9') value = (c - '0');
else if (c >= 'A' && c <= 'F') value = 10 + (c - 'A');
else if (c >= 'a' && c <= 'f') value = 10 + (c - 'a');
if (value >= 0) {
if (i % 2 == 1) {
outBytes[o++] = (outByte << 4) | value;
outByte = 0;
} else {
outByte = value;
}
} else {
if (o != 0) break;
}
}
NSData *a = [[NSData alloc] initWithBytesNoCopy:outBytes length:o freeWhenDone:YES];
NSString* newStr = [NSString stringWithUTF8String:[a bytes]];
return newStr;
}
I want the same in Swift. Can anybody translate this code in Swift, or is there any easy way to do this 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 and string:
Swift1
func dataWithHexString(hex: String) -> NSData {
var hex = hex
let data = NSMutableData()
while(countElements(hex) > 0) {
var c: String = hex.substringToIndex(advance(hex.startIndex, 2))
hex = hex.substringFromIndex(advance(hex.startIndex, 2))
var ch: UInt32 = 0
NSScanner(string: c).scanHexInt(&ch)
data.appendBytes(&ch, length: 1)
}
return data
}
use:
let data = dataWithHexString("68656c6c6f2c20776f726c64") // <68656c6c 6f2c2077 6f726c64>
if let string = NSString(data: data, encoding: 1) {
print(string) // hello, world
}
Swift2
func dataWithHexString(hex: String) -> NSData {
var hex = hex
let data = NSMutableData()
while(hex.characters.count > 0) {
let c: String = hex.substringToIndex(hex.startIndex.advancedBy(2))
hex = hex.substringFromIndex(hex.startIndex.advancedBy(2))
var ch: UInt32 = 0
NSScanner(string: c).scanHexInt(&ch)
data.appendBytes(&ch, length: 1)
}
return data
}
use:
let data = dataWithHexString("68656c6c6f2c20776f726c64") // <68656c6c 6f2c2077 6f726c64>
if let string = String(data: data, encoding: NSUTF8StringEncoding) {
print(string) //"hello, world"
}
Swift3
func dataWithHexString(hex: String) -> Data {
var hex = hex
var data = Data()
while(hex.characters.count > 0) {
let c: String = hex.substring(to: hex.index(hex.startIndex, offsetBy: 2))
hex = hex.substring(from: hex.index(hex.startIndex, offsetBy: 2))
var ch: UInt32 = 0
Scanner(string: c).scanHexInt32(&ch)
var char = UInt8(ch)
data.append(&char, count: 1)
}
return data
}
use:
let data = dataWithHexString(hex: "68656c6c6f2c20776f726c64") // <68656c6c 6f2c2077 6f726c64>
let string = String(data: data, encoding: .utf8) // "hello, world"
Swift4
func dataWithHexString(hex: String) -> Data {
var hex = hex
var data = Data()
while(hex.count > 0) {
let subIndex = hex.index(hex.startIndex, offsetBy: 2)
let c = String(hex[..<subIndex])
hex = String(hex[subIndex...])
var ch: UInt32 = 0
Scanner(string: c).scanHexInt32(&ch)
var char = UInt8(ch)
data.append(&char, count: 1)
}
return data
}
use:
let data = dataWithHexString(hex: "68656c6c6f2c20776f726c64") // <68656c6c 6f2c2077 6f726c64>
let string = String(data: data, encoding: .utf8) // "hello, world"
Swift 4 & Swift 5 implementation:
init?(hexString: String) {
let len = hexString.count / 2
var data = Data(capacity: len)
var i = hexString.startIndex
for _ in 0..<len {
let j = hexString.index(i, offsetBy: 2)
let bytes = hexString[i..<j]
if var num = UInt8(bytes, radix: 16) {
data.append(&num, count: 1)
} else {
return nil
}
i = j
}
self = data
}
Usage:
let data = Data(hexString: "0a1b3c4d")
Swift 5
extension Data {
init?(hex: String) {
guard hex.count.isMultiple(of: 2) else {
return nil
}
let chars = hex.map { $0 }
let bytes = stride(from: 0, to: chars.count, by: 2)
.map { String(chars[$0]) + String(chars[$0 + 1]) }
.compactMap { UInt8($0, radix: 16) }
guard hex.count / bytes.count == 2 else { return nil }
self.init(bytes)
}
}
Here is my Swift 5 way to do it:
does take care of "0x" prefixes
use subscript instead of allocated Array(), no C style [i+1] too
add .hexadecimal to String.data(using encoding:) -> Data?
.
String Extension:
extension String {
enum ExtendedEncoding {
case hexadecimal
}
func data(using encoding:ExtendedEncoding) -> Data? {
let hexStr = self.dropFirst(self.hasPrefix("0x") ? 2 : 0)
guard hexStr.count % 2 == 0 else { return nil }
var newData = Data(capacity: hexStr.count/2)
var indexIsEven = true
for i in hexStr.indices {
if indexIsEven {
let byteRange = i...hexStr.index(after: i)
guard let byte = UInt8(hexStr[byteRange], radix: 16) else { return nil }
newData.append(byte)
}
indexIsEven.toggle()
}
return newData
}
}
Usage:
"5413".data(using: .hexadecimal)
"0x1234FF".data(using: .hexadecimal)
Tests:
extension Data {
var bytes:[UInt8] { // fancy pretty call: myData.bytes -> [UInt8]
return [UInt8](self)
}
// Could make a more optimized one~
func hexa(prefixed isPrefixed:Bool = true) -> String {
return self.bytes.reduce(isPrefixed ? "0x" : "") { $0 + String(format: "%02X", $1) }
}
}
print("000204ff5400".data(using: .hexadecimal)?.hexa() ?? "failed") // OK
print("0x000204ff5400".data(using: .hexadecimal)?.hexa() ?? "failed") // OK
print("541".data(using: .hexadecimal)?.hexa() ?? "failed") // fails
print("5413".data(using: .hexadecimal)?.hexa() ?? "failed") // OK
Here's a simple solution I settled on:
extension NSData {
public convenience init(hexString: String) {
var index = hexString.startIndex
var bytes: [UInt8] = []
repeat {
bytes.append(hexString[index...index.advancedBy(1)].withCString {
return UInt8(strtoul($0, nil, 16))
})
index = index.advancedBy(2)
} while index.distanceTo(hexString.endIndex) != 0
self.init(bytes: &bytes, length: bytes.count)
}
}
Usage:
let data = NSData(hexString: "b8dfb080bc33fb564249e34252bf143d88fc018f")
Output:
print(data)
>>> <b8dfb080 bc33fb56 4249e342 52bf143d 88fc018f>
Update 6/29/2016
I updated the initializer to handle malformed data (i.e., invalid characters or odd number of characters).
public convenience init?(hexString: String, force: Bool) {
let characterSet = NSCharacterSet(charactersInString: "0123456789abcdefABCDEF")
for scalar in hexString.unicodeScalars {
if characterSet.characterIsMember(UInt16(scalar.value)) {
hexString.append(scalar)
}
else if !force {
return nil
}
}
if hexString.characters.count % 2 == 1 {
if force {
hexString = "0" + hexString
}
else {
return nil
}
}
var index = hexString.startIndex
var bytes: [UInt8] = []
repeat {
bytes.append(hexString[index...index.advancedBy(1)].withCString {
return UInt8(strtoul($0, nil, 16))
})
index = index.advancedBy(2)
} while index.distanceTo(hexString.endIndex) != 0
self.init(bytes: &bytes, length: bytes.count)
}
Here is my take on converting hexadecimal string to Data using Swift 4:
extension Data {
private static let hexRegex = try! NSRegularExpression(pattern: "^([a-fA-F0-9][a-fA-F0-9])*$", options: [])
init?(hexString: String) {
if Data.hexRegex.matches(in: hexString, range: NSMakeRange(0, hexString.count)).isEmpty {
return nil // does not look like a hexadecimal string
}
let chars = Array(hexString)
let bytes: [UInt8] =
stride(from: 0, to: chars.count, by: 2)
.map {UInt8(String([chars[$0], chars[$0+1]]), radix: 16)}
.compactMap{$0}
self = Data(bytes)
}
var hexString: String {
return map { String(format: "%02hhx", $0) }.joined()
}
}
(I threw in a small feature for converting back to hex string I found in this answer)
And here is how you would use it:
let data = Data(hexString: "cafecafe")
print(data?.hexString) // will print Optional("cafecafe")
One more solution that is simple to follow and leverages swifts built-in hex parsing
func convertHexToBytes(_ str: String) -> Data? {
let values = str.compactMap { $0.hexDigitValue } // map char to value of 0-15 or nil
if values.count == str.count && values.count % 2 == 0 {
var data = Data()
for x in stride(from: 0, to: values.count, by: 2) {
let byte = (values[x] << 4) + values[x+1] // concat high and low bits
data.append(UInt8(byte))
}
return data
}
return nil
}
let good = "e01AFd"
let bad = "e0671"
let ugly = "GT40"
print("\(convertHexToBytes(good))") // Optional(3 bytes)
print("\(convertHexToBytes(bad))") // nil
print("\(convertHexToBytes(ugly))") // nil
The code worked for me in Swift 3.0.2.
extension String {
/// Expanded encoding
///
/// - bytesHexLiteral: Hex string of bytes
/// - base64: Base64 string
enum ExpandedEncoding {
/// Hex string of bytes
case bytesHexLiteral
/// Base64 string
case base64
}
/// Convert to `Data` with expanded encoding
///
/// - Parameter encoding: Expanded encoding
/// - Returns: data
func data(using encoding: ExpandedEncoding) -> Data? {
switch encoding {
case .bytesHexLiteral:
guard self.characters.count % 2 == 0 else { return nil }
var data = Data()
var byteLiteral = ""
for (index, character) in self.characters.enumerated() {
if index % 2 == 0 {
byteLiteral = String(character)
} else {
byteLiteral.append(character)
guard let byte = UInt8(byteLiteral, radix: 16) else { return nil }
data.append(byte)
}
}
return data
case .base64:
return Data(base64Encoded: self)
}
}
}
Swift 5
With support iOS 13 and iOS2...iOS12.
extension String {
var hex: Data? {
var value = self
var data = Data()
while value.count > 0 {
let subIndex = value.index(value.startIndex, offsetBy: 2)
let c = String(value[..<subIndex])
value = String(value[subIndex...])
var char: UInt8
if #available(iOS 13.0, *) {
guard let int = Scanner(string: c).scanInt32(representation: .hexadecimal) else { return nil }
char = UInt8(int)
} else {
var int: UInt32 = 0
Scanner(string: c).scanHexInt32(&int)
char = UInt8(int)
}
data.append(&char, count: 1)
}
return data
}
}
Swift 5
There is a compact implementation of initialize Data instance from hex string using a regular expression. It searches hex numbers inside a string and combine them to a result data so that it can support different formats of hex representations:
extension Data {
private static let regex = try! NSRegularExpression(pattern: "([0-9a-fA-F]{2})", options: [])
/// Create instance from string with hex numbers.
init(from: String) {
let range = NSRange(location: 0, length: from.utf16.count)
let bytes = Self.regex.matches(in: from, options: [], range: range)
.compactMap { Range($0.range(at: 1), in: from) }
.compactMap { UInt8(from[$0], radix: 16) }
self.init(bytes)
}
/// Hex string representation of data.
var hex: String {
map { String($0, radix: 16) }.joined()
}
}
Examples:
let data = Data(from: "0x11223344aabbccdd")
print(data.hex) // Prints "11223344aabbccdd"
let data2 = Data(from: "11223344aabbccdd")
print(data2.hex) // Prints "11223344aabbccdd"
let data3 = Data(from: "11223344 aabbccdd")
print(data3.hex) // Prints "11223344aabbccdd"
let data4 = Data(from: "11223344 AABBCCDD")
print(data4.hex) // Prints "11223344aabbccdd"
let data5 = Data(from: "Hex: 0x11223344AABBCCDD")
print(data5.hex) // Prints "11223344aabbccdd"
let data6 = Data(from: "word[0]=11223344 word[1]=AABBCCDD")
print(data6.hex) // Prints "11223344aabbccdd"
let data7 = Data(from: "No hex")
print(data7.hex) // Prints ""
Handles prefixes
Ignores invalid characters and incomplete bytes
Uses Swift built in hex character parsing
Doesn't use subscripts
extension Data {
init(hexString: String) {
self = hexString
.dropFirst(hexString.hasPrefix("0x") ? 2 : 0)
.compactMap { $0.hexDigitValue.map { UInt8($0) } }
.reduce(into: (data: Data(capacity: hexString.count / 2), byte: nil as UInt8?)) { partialResult, nibble in
if let p = partialResult.byte {
partialResult.data.append(p + nibble)
partialResult.byte = nil
} else {
partialResult.byte = nibble << 4
}
}.data
}
}
Supposing your string is even size, you can use this to convert to hexadecimal and save it to Data:
Swift 5.2
func hex(from string: String) -> Data {
.init(stride(from: 0, to: string.count, by: 2).map {
string[string.index(string.startIndex, offsetBy: $0) ... string.index(string.startIndex, offsetBy: $0 + 1)]
}.map {
UInt8($0, radix: 16)!
})
}

Resources