How do I get a byte array from base64 in swift 3? - swift

I'm translating an Android application to iOS with swift 3. My problem is with the next block of Java code:
String b64 = "KJT-AAAhtAvQ";
byte[] bytesCodigo = Base64.decode(b64, Base64.URL_SAFE);
System.out.Println(bytesCodigo.length) // 9
How would it be in swift?
Thanks

One would just create a Data (NSData in Objective-C) from the base64 string. Note, though, that the standard Data(base64Encoded:) does not have “URL Safe” rendition, but you can create one that replaces “-” with “+” and “_” with “/” before trying to convert it:
extension Data {
init?(base64EncodedURLSafe string: String, options: Base64DecodingOptions = []) {
let string = string
.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")
self.init(base64Encoded: string, options: options)
}
}
Then you can do:
guard let data = Data(base64EncodedURLSafe: string) else {
// handle errors in decoding base64 string here
return
}
// use `data` here
Obviously, if it was not a “URL safe” base64 string, you could simply do:
guard let data = Data(base64Encoded: string) else {
// handle errors in decoding base64 string here
return
}
// use `data` here
You asked how one gets a “byte array”: Data effectively is that, insofar as the Data type conforms to the RandomAccessCollection protocol, just as the Array type does. So, you have many of the “array” sort of behaviors should you need them, e.g.:
for byte in data {
// do something with `byte`, a `UInt8` value
}
or
let hexString = data.map { String(format: "%02x", $0) }
.joined(separator: " ")
print(hexString) // 28 94 fe 00 00 21 b4 0b d0
So, in essence, you generally do not need a “byte array” as Data provides all the sort of access to the bytes that you might need. Besides, most Swift APIs handling binary data expect Data types, anyway.
If you literally need an [UInt8] array, you can create one:
let bytes = data.map { $0 }
But it is generally inefficient to create a separate array of bytes (especially when the binary payload is large) when the Data provides all the array behaviors that one would generally need, and more.

Related

How do you decode utf8-literals like "\xc3\xa6" in Swift 5?

I am fetching a list of WiFi SSID's from a Bluetooth characteristic. Each SSID is represented as a string, some have these UTF8-Literals like "\xc3\xa6".
I have tried multiple ways to decode this like
let s = "\\xc3\\xa6"
let dec = s.utf8
From this I expect
print(dec)
> æ
etc. but it doesn't work, it just results in
print(dec)
> \xc3\xa6
How do I decode UTF-8 literals in Strings in Swift 5?
You'll just have to parse the string, convert each hex string to a UInt8, and then decode that with String.init(byte:encoding:):
let s = "\\xc3\\xa6"
let bytes = s
.components(separatedBy: "\\x")
// components(separatedBy:) would produce an empty string as the first element
// because the string starts with "\x". We drop this
.dropFirst()
.compactMap { UInt8($0, radix: 16) }
if let decoded = String(bytes: bytes, encoding: .utf8) {
print(decoded)
} else {
print("The UTF8 sequence was invalid!")
}

Decoding strings including utf8-literals like '\xc3\xa6' in Swift?

Follow up question to my former thread about UTF-8 literals:
It was established that you can decode UTF-8 literals from string like this that exclusively includes UTF-8 literals:
let s = "\\xc3\\xa6"
let bytes = s
.components(separatedBy: "\\x")
// components(separatedBy:) would produce an empty string as the first element
// because the string starts with "\x". We drop this
.dropFirst()
.compactMap { UInt8($0, radix: 16) }
if let decoded = String(bytes: bytes, encoding: .utf8) {
print(decoded)
} else {
print("The UTF8 sequence was invalid!")
}
However this only works if the string only contains UTF-8 literals. As I am fetching a Wi-Fi list of names that has these UTF-8 literals within, how do I go about decoding the entire string?
Example:
let s = "This is a WiFi Name \\xc3\\xa6 including UTF-8 literals \\xc3\\xb8"
With the expected result:
print(s)
> This is a WiFi Name æ including UTF-8 literals ø
In Python there is a simple solution to this:
contents = source_file.read()
uni = contents.decode('unicode-escape')
enc = uni.encode('latin1')
dec = enc.decode('utf-8')
Is there a similar way to decode these strings in Swift 5?
To start with add the decoding code into a String extension as a computed property (or create a function)
extension String {
var decodeUTF8: String {
let bytes = self.components(separatedBy: "\\x")
.dropFirst()
.compactMap { UInt8($0, radix: 16) }
return String(bytes: bytes, encoding: .utf8) ?? self
}
}
Then use a regular expression and match using a while loop to replace all matching values
while let range = string.range(of: #"(\\x[a-f0-9]{2}){2}"#, options: [.regularExpression, .caseInsensitive]) {
string.replaceSubrange(range, with: String(string[range]).decodeUTF8)
}
As far as I know there's no native Swift solution to this. To make it look as compact as the Python version at the call site you can build an extension on String to hide the complexity
extension String {
func replacingUtf8Literals() -> Self {
let regex = #"(\\x[a-zAZ0-9]{2})+"#
var str = self
while let range = str.range(of: regex, options: .regularExpression) {
let literalbytes = str[range]
.components(separatedBy: "\\x")
.dropFirst()
.compactMap{UInt8($0, radix: 16)}
guard let actuals = String(bytes: literalbytes, encoding: .utf8) else {
fatalError("Regex error")
}
str.replaceSubrange(range, with: actuals)
}
return str
}
}
This lets you call
print(s.replacingUtf8Literals()).
//prints: This is a WiFi Name æ including UTF-8 literals ø
For convenience I'm trapping a failed conversion with fatalError. You may want to handle this in a better way in production code (although, unless the regex is wrong it should never occur!). There needs to be some form of break or error thrown here else you have an infinite loop.

Calling map on NSMutableData in swift

I'm having some strange behavior with NSMutableData that I can't explain. I have a method that converts a string to a null-terminated UTF-8 array of bytes. However, if I then use "data.map(...)" to print it out, the first byte is right and the rest look like random memory. What's weird is if I make a copy with "let copy = data.copy() as! Data" and then use "copy.map(...)" it works just fine. I'm converting to NSMutableData instead of Data because that's the format the API I'm using this takes it in.
Here's code to convert a string to a UTF-8 bytes array in an NSMutableData:
public func getUtf8Bytes(of str: String) -> NSMutableData {
// Convert to a null-terminated UTF-8 NSMutableData
let utf8CStringInts: [UInt8] = str.utf8CString.map { UInt8($0) }
let count = utf8CStringInts.count
let data = NSMutableData(length: count)!
data.resetBytes(in: NSRange(location: 0, length: count))
// Copy into NSMutableData
let pointer = data.mutableBytes
var index = 0
for byte in utf8CStringInts {
pointer.storeBytes(of: byte, toByteOffset: index, as: UInt8.self)
index += 1
}
return data
}
The following will correctly print "UTF-8 Bytes: 0x31 0x32 0x33 0x00":
let utf8Data = getUtf8Bytes(of: "123")
let debugString = (utf8Data.copy() as! Data).map { String(format: "0x%02x ", $0) }.joined()
print("UTF-8 Bytes: " + debugString)
However, if I take out the copy as follows it will incorrectly print "0x31 0x00 0x00 0x00":
let utf8Data = getUtf8Bytes(of: "123")
let debugString = utf8Data.map { String(format: "0x%02x ", $0) }.joined()
print("UTF-8 Bytes: " + debugString)
Can someone explain why the results are printed correctly after copying it to a Data?
Interesting... So after some sniffing around, here's what I found.
Copying the NSMutableData is not the solution, but rather, bridging it to Data. This will work as well:
let utf8Data = getUtf8Bytes(of: "123")
let debugString = (mutableData as Data).map { String(format: "0x%02x ", $0) }.joined()
print("UTF-8 Bytes: \(debugString)")
But why? The problem appears to stem from NSData's conformance to DataProtocol (& its subsequent implicit inheritance of the Collection protocol). It's through this chain of implicit inheritance that permits the (mis)use of these generic Collection methods (e.g. subscript access, map, forEach, etc.) that are all "broken".
Furthermore, we can verify the byte contents of the NSMutableData are correct:
print((0..<mutableData.length)
.map({ String(format: "0x%02x ", mutableData.bytes.load(fromByteOffset: $0, as: UInt8.self)) })
.joined())
// Prints "0x31 0x32 0x33 0x00"
Also, there's a swift-ier way to implement getUtf8Bytes(of:):
public func getUtf8Bytes(of str: String) -> NSMutableData {
// Note: You may want to handle the force unwrapping here in a safer way...
return NSMutableData(data: (str + "\0").data(using: .utf8)!)
}

Urlencode cyrillic characters in Swift

I need to convert a cyrillic string to its urlencoded version using Windows-1251 encoding. For the following example string:
Моцарт
The correct result should be:
%CC%EE%F6%E0%F0%F2
I tried addingPercentEncoding(withAllowedCharacters:) but it doesn't work.
How to achieve the desired result in Swift?
NSString has a addingPercentEscapes(using:) method which allows to specify an arbitrary
encoding:
let text = "Моцарт"
if let encoded = (text as NSString).addingPercentEscapes(using: String.Encoding.windowsCP1251.rawValue) {
print(encoded)
// %CC%EE%F6%E0%F0%F2
}
However, this is deprecated as of iOS 9/macOS 10.11. It causes compiler warnings and may not be available in newer OS versions.
What you can do instead is to convert the string do Data with
the desired encoding,
and then convert each byte to the corresponding %NN sequence (using the approach from
How to convert Data to hex string in swift):
let text = "Моцарт"
if let data = text.data(using: .windowsCP1251) {
let encoded = data.map { String(format: "%%%02hhX", $0) }.joined()
print(encoded)
// %CC%EE%F6%E0%F0%F2
}

Convert UInt8 Array to String

I have decrypted using AES (CrytoSwift) and am left with an UInt8 array. What's the best approach to covert the UInt8 array into an appripriate string? Casting the array only gives back a string that looks exactly like the array. (When done in Java, a new READABLE string is obtained when casting Byte array to String).
I'm not sure if this is new to Swift 2, but at least the following works for me:
let chars: [UInt8] = [ 49, 50, 51 ]
var str = String(bytes: chars, encoding: NSUTF8StringEncoding)
In addition, if the array is formatted as a C string (trailing 0), these work:
str = String.fromCString(UnsafePointer(chars)) // UTF-8 is implicit
// or:
str = String(CString: UnsafePointer(chars), encoding: NSUTF8StringEncoding)
I don't know anything about CryptoSwift. But I can read the README:
For your convenience CryptoSwift provides two function to easily convert array of bytes to NSData and other way around:
let data = NSData.withBytes([0x01,0x02,0x03])
let bytes:[UInt8] = data.arrayOfBytes()
So my guess would be: call NSData.withBytes to get an NSData. Now you can presumably call NSString(data:encoding:) to get a string.
SWIFT 3.1
Try this:
let decData = NSData(bytes: enc, length: Int(enc.count))
let base64String = decData.base64EncodedString(options: .lineLength64Characters)
This is string output
Extensions allow you to easily modify the framework to fit your needs, essentially building your own version of Swift (my favorite part, I love to customize). Try this one out, put at the end of your view controller and call in viewDidLoad():
func stringToUInt8Extension() {
var cache : [UInt8] = []
for byte : UInt8 in 97..<97+26 {
cache.append(byte)
print(byte)
}
print("The letters of the alphabet are \(String(cache))")
}
extension String {
init(_ bytes: [UInt8]) {
self.init()
for b in bytes {
self.append(UnicodeScalar(b))
}
}
}