How to read from file and put in an Array - swift

This is an application of Sieve of Eratosthenes written in Swift.
I'm able to write the output on a file, but I'm only able to start by manually passing [primesList].
Instead, I would like to read the file primesList.txt and put it in [primesList].
import Foundation
// set a file called test.txt on Desktop to be used by this program
let dir = "~/Desktop"
let file = "primesList.txt"
let path = dir.stringByExpandingTildeInPath
let filePath: NSString = path.stringByAppendingPathComponent(file)
// set some needed variables and constants
var highestNumberEvaluated = 100
var primesList = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
let readFromFile = String(contentsOfFile: filePath, encoding: NSUTF8StringEncoding, error: nil)
println(readFromFile)
let inputNumber = 100
let range2Evaluate = 100
let last2Evaluate = inputNumber + range2Evaluate
println("Find primes between \(inputNumber) and \(last2Evaluate):\n")
// create a dictionary of numbers to be evaluated true or false
var numbersList = [Int: Bool]()
for i in inputNumber...last2Evaluate {
numbersList[i] = true
}
// mark as not prime (false) all not primes in numbersList
for i in primesList {
if i < Int(sqrt(Double(last2Evaluate))) {
let myMultiplier = inputNumber / i
var bottomValue = i * myMultiplier
for var j = bottomValue; j < (bottomValue + range2Evaluate + 2); j += i {
numbersList[j] = false
}
} else {break}
}
// create an array made by all primes found true, then sort
var primesFoundList = [Int]()
for (myKey, myValue) in numbersList {
if myValue == true {
primesFoundList.append(myKey)
}
}
primesFoundList.sort { $0 < $1 }
// add the primes found to the original primes list and write on the file test.txt
primesList += primesFoundList
let toBeWritten = toString(primesList)
toBeWritten.writeToFile(filePath, atomically: true, encoding: NSUTF8StringEncoding, error: nil)
on line 13-14 I can get the content of the file, how can I write this content in the array [primesList]?

The toString() function is not very well suited to produce output that is to be
read again. You could create a simple comma-separated list with
let toBeWritten = ", ".join(primesList.map { String($0)})
toBeWritten.writeToFile(filePath, atomically: true, encoding: NSUTF8StringEncoding, error: nil)
which can be read again with
if let readFromFile = String(contentsOfFile: filePath, encoding: NSUTF8StringEncoding, error: nil) {
primesList = readFromFile.componentsSeparatedByString(",").map() { ($0 as NSString).integerValue }
}
(Note: This assumes that the file has the correct format, otherwise it may crash.)
Alternatively, use the writeToFile() method from NSArray which writes the
array as a property list (XML) file:
(primesList as NSArray).writeToFile(filePath, atomically: false)
which can be read back with
if let storedList = NSArray(contentsOfFile: filePath) as? [Int] {
primesList = storedList
}

Related

What is the correct way in swift to wrap a segment of a UInt8 array as a String?

I have some raw data processing to do in an iPhone app. Strings always come out of an extremely large underlying byte array, so I want to be able to pull strings out of the array without triggering out of memory issues.
I can see a String(bytesNoCopy: ...) in the documentation, is this what I want, and how exactly is it supposed to be used?
Assuming an array of uint8 called data and index is a number which shows where the string is inside the array.
var myData:[UInt8] = [
4, // String 1 length
65,66,67,68,0, // String 1 data
4, // String 2 length
69,70,71,71,0 // String 2 data
]
var index = 0
let string1 = readString(&myData, &index)
let string2 = readString(&myData, &index)
print(string1, string2)
// Read a string located at a specific
// position in a byte array, and increment
// the pointer into the array into the next
// position
func readString(_ data:inout [UInt8], _ index:inout Int) -> String {
// Read string length out of data array
let l = Int(readUInt8(&data, &index))
// Read string out of data array without copy
let s = String(
bytesNoCopy: UnsafeMutableRawPointer(data + index), // <-- what goes here??
length: l,
encoding: .utf8,
freeWhenDone: false)
index = index + l
if s == nil {
return ""
}
return s!
}
// Read a byte as an integer from a
// data array, and increment the pointer into
// the data array to the next position.
func readUInt8(_ data:inout [UInt8], _ x:inout Int) -> UInt8 {
let v = data[x]
x = x + 1
return v
}
NOTE: This question is updated to include sample data, and renamed the variable x to index to make it clearer that the question was asking how to create a string from a segment of a byte array.
Here's how you can do try this -
import Foundation
func readString(_ data: inout [UInt8], _ x: inout Int) -> String {
let l = 4
var slice: ArraySlice<UInt8> = data[x..<x+l] // No copy, view into existing Array
x += l
return slice.withUnsafeBytes({ pointer in
// No copy, just making compiler happy (assumption that it is bound to UInt8 is correct
if let bytes = pointer.baseAddress?.assumingMemoryBound(to: UInt8.self) {
return String(
bytesNoCopy: UnsafeMutableRawPointer(mutating: bytes), // No copy
length: slice.count,
encoding: .utf8,
freeWhenDone: false
) ?? ""
} else {
return ""
}
})
}
Test
var a: [UInt8] = [
65, 66, 67, 68,
69, 70, 71, 72
]
var x = 0
let test1 = readString(&a, &x)
print("test1 : \(test1)")
// test1 : ABCD
let test2 = readString(&a, &x)
print("test2 : \(test2)")
// test2 : EFGH

Swift, how to get an image "where from" metadata field?

I'm reading a file from the file system on macOS, I tried to extract the metadata with the following code:
let imagedata = try? Data(contentsOf: fileUrl)
if imagedata == nil {
print("Could not read contents of image")
return
}
var source: CGImageSource = CGImageSourceCreateWithData((imagedata as! CFMutableData), nil)!
var metadata = CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as? [AnyHashable: Any]
print("image metadata", metadata)
And this is the output that I get:
image metadata Optional([AnyHashable("ColorModel"): RGB, AnyHashable("DPIHeight"): 72, AnyHashable("PixelHeight"): 840, AnyHashable("ProfileName"): sRGB IEC61966-2.1, AnyHashable("PixelWidth"): 840, AnyHashable("{PNG}"): {
Chromaticities = (
"0.3127",
"0.329",
"0.64",
"0.33",
"0.3",
"0.6000000000000001",
"0.15",
"0.06"
);
Gamma = "0.45455";
InterlaceType = 0;
XPixelsPerMeter = 2834;
YPixelsPerMeter = 2834;
sRGBIntent = 0;
}, AnyHashable("DPIWidth"): 72, AnyHashable("Depth"): 8])
And that extracts the usual metadata, but I actually would like to know where the image was downloaded from, when I drag n' drop an image into the desktop and then right click and then get info, I see the field:
Is there any way to retrieve this field?
Edit: I tried this:
if let attr = try? FileManager.default.attributesOfItem(atPath: fileUrl.path) {
print("attributes", attr)
}
and now I can see the kMDItemWhereFroms field, but I don't know how to reach it or parse. Maybe someone has an idea here?
I think it's always nice to show how tinker, how to find the data, so I'll show how I got it, while I didn't know at all about it.
First thought, it might not be in image Metadata, but in "File System" ones, so let's look at FileManager. It's in FileManager.attributesOfItem(atPath:)
Step 1:
let attributes = try fileManager.attributesOfItem(atPath: filePath)
This gives me:
[__C.NSFileAttributeKey(_rawValue: NSFileGroupOwnerAccountName): staff,
__C.NSFileAttributeKey(_rawValue: NSFileReferenceCount): 1,
__C.NSFileAttributeKey(_rawValue: NSFileModificationDate): 2020-05-13 15:16:20 +0000,
__C.NSFileAttributeKey(_rawValue: NSFileSystemFileNumber): 8635383780,
__C.NSFileAttributeKey(_rawValue: NSFilePosixPermissions): 420,
__C.NSFileAttributeKey(_rawValue: NSFileType): NSFileTypeRegular,
__C.NSFileAttributeKey(_rawValue: NSFileOwnerAccountID): 501,
__C.NSFileAttributeKey(_rawValue: NSFileGroupOwnerAccountID): 20,
__C.NSFileAttributeKey(_rawValue: NSFileExtensionHidden): 0,
__C.NSFileAttributeKey(_rawValue: NSFileHFSTypeCode): 0,
__C.NSFileAttributeKey(_rawValue: NSFileCreationDate): 2020-05-13 15:16:20 +0000,
__C.NSFileAttributeKey(_rawValue: NSFileOwnerAccountName): Larme,
__C.NSFileAttributeKey(_rawValue: NSFileSize): 1258299,
__C.NSFileAttributeKey(_rawValue: NSFileSystemNumber): 16777220,
__C.NSFileAttributeKey(_rawValue: NSFileExtendedAttributes): {
"com.apple.lastuseddate#PS" = {length = 16, bytes = 0x440fbc5e00000000503f091700000000};
"com.apple.macl" = {length = 72, bytes = 0x02009685 15175d5d 47089537 71b6a786 ... 00000000 00000000 };
"com.apple.metadata:kMDItemDownloadedDate" = {length = 53, bytes = 0x62706c69 73743030 a1013341 c2362362 ... 00000000 00000013 };
"com.apple.metadata:kMDItemWhereFroms" = {length = 414, bytes = 0x62706c69 73743030 a201025f 11013b68 ... 00000000 00000178 };
"com.apple.quarantine" = {length = 57, bytes = 0x30303833 3b356562 63306634 343b5361 ... 44344445 45343845 };
}, __C.NSFileAttributeKey(_rawValue: NSFileHFSCreatorCode): 0]
What to look in this info:
[...
__C.NSFileAttributeKey(_rawValue: NSFileExtendedAttributes): {
...
"com.apple.metadata:kMDItemWhereFroms" = {length = 414, bytes = 0x62706c69 73743030 a201025f 11013b68 ... 00000000 00000178 };
...
]
Ok, so let's continue to step 2, let's dig deeper:
let extendedAttributes = attributes[FileAttributeKey(rawValue: "NSFileExtendedAttributes")] as! [String: Any]
let data = extendedAttributes["com.apple.metadata:kMDItemWhereFroms"] as! Data //that's the new output for `Data` in new systems (iOS13+, etc.)
As always, I tend to do just in case to get more info: let str = String(data: data, encoding: .utf8) which returns nil. Let's remember that not all data is convertible into utf8 string like that. For instance, an image won't make it.
But I know a few tricks, let's do instead: let str = String(data: data, encoding: .ascii), this gives me:
"bplist00¢\u{01}\u{02}_\u{11}\u{01};https://doc-04-6k-docs.googleusercontent.com/docs/securesc/teo5l5s1g996hre86q7qv786r7me56c3/3nneqai4sh4lvk3bdlnpokt8793t4t5t/1589382975000/11847256646728493158/11847256646728493158/0B1U9OypmOvxpODVFb2VGZEdpckk?e=download&authuser=0&nonce=faav7fnl402re&user=11847256646728493158&hash=vl51p8m313rnnao3dleqsl348rp2vo82_\u{10}+https://drive.google.com/drive/u/0/my-drive\0\u{08}\0\u{0B}\u{01}J\0\0\0\0\0\0\u{02}\u{01}\0\0\0\0\0\0\0\u{03}\0\0\0\0\0\0\0\0\0\0\0\0\0\0\u{01}x"
I downloaded my sample from my Google Drive, info seems making sense. What do see there?: bplist, that's the most important one here.
A PropertyList? Let's decode it then:
let urls = try PropertyListDecoder().decode([String].self, from: data)
It made sense for me to be a [String], it would have been different, that would had be harder. But it's a simple one.
Final code:
static func downloadedFromURLs(for filePath: String) -> [String]? {
let fileManager = FileManager.default
guard fileManager.fileExists(atPath: filePath) else { print("File doesn't exists"); return nil }
do {
let attributes = try fileManager.attributesOfItem(atPath: filePath)
guard let extendedAttributes = attributes[FileAttributeKey(rawValue: "NSFileExtendedAttributes")] as? [String: Any] else {
print("Didn't find NSFileExtendedAttributes value")
return nil
}
guard let data = extendedAttributes["com.apple.metadata:kMDItemWhereFroms"] as? Data else {
print("Didn't find com.apple.metadata:kMDItemWhereFroms value")
return nil
}
let urls = try PropertyListDecoder().decode([String].self, from: data)
return urls
} catch {
print("Error: \(error)")
return nil
}
}
Side note:
There might be some constant instead of writing hard coded strings for "com.apple.metadata:kMDItemWhereFroms"
and NSFileExtendedAttributes, but I didn't see it here.
Some related interested discussion.
https://apple.stackexchange.com/questions/110239/where-is-the-where-from-meta-data-stored-when-downloaded-via-chrome

Convert UInt8 array of bytes to explicit values from characteristic.value

I try to convert my UInt8 array to an explicit value but I try in several ways but I can't get values that I can understand.
How have i to do to get explicit values?.
My array of UInt8 :
[216, 61, 233, 124, 240, 144, 66, 244]
My code :
guard let characteristicData = characteristic.value else { return "Error" }
let count = (characteristicData.count) / MemoryLayout<UInt8>.size
var array = [UInt8](repeating: 0, count: count)
characteristicData.copyBytes(to: &array, count:count * MemoryLayout<UInt8>.size)
let characters = array.map { Character(UnicodeScalar($0)) }
let result = String(Array(characters))
print(array)
print(result.utf8)
My output:
Ø=é|ðBô

Can't decompress H.264 frames with VideoToolbox when hardcoding PPS and SPS parameters

I wrote a H.264 decoder that works great. However for performance reasons, I'm trying to hard code in the PPS and SPS parameters (they never change).
For reference this is what they look like as a base 10 [UInt8]:
SPS [103, 66, 0, 40, 244, 5, 1, 236, 128]
PPS [104, 206, 9, 136]
What's bizarre is that when I decode the PPS and SPS parameters from frames that I receive and use those to create the format description and decompression session, it works great; but when I hard code them in, it doesn't work and I get VT error -12909 (bad data) when I try to decompress frames. As far as I can tell, the byte arrays are the exact same.
To hard code them in, I just wrote my createFormatDescription like this:
func createFormatDescription(sps: [UInt8]?, pps: [UInt8]?) -> Bool {
self.formatDesc = nil
let sps = sps ?? [103, 66, 0, 40, 244, 5, 1, 236, 128] as [UInt8]
let pps = pps ?? [104, 206, 9, 136] as [UInt8]
let pointerSPS = UnsafePointer<UInt8>(sps)
let pointerPPS = UnsafePointer<UInt8>(pps)
let dataParamArray = [pointerSPS, pointerPPS]
let parameterSetPointers = UnsafePointer<UnsafePointer<UInt8>>(dataParamArray)
let sizeParamArray = [sps.count, pps.count]
let parameterSetSizes = UnsafePointer<Int>(sizeParamArray)
let status = CMVideoFormatDescriptionCreateFromH264ParameterSets(kCFAllocatorDefault, 2, parameterSetPointers, parameterSetSizes, 4, &formatDesc)
if status == noErr {
print("===== Format description created")
return true
} else {
return false
}
}
And call this in my init() method. Other than that, I made no changes; frames are being interpreted in the exact same way.
For reference, here is what my VT callback looks like:
private var callback: VTDecompressionOutputCallback = {(
decompressionOutputRefCon: UnsafeMutableRawPointer?,
sourceFrameRefCon: UnsafeMutableRawPointer?,
status: OSStatus,
infoFlags: VTDecodeInfoFlags,
imageBuffer: CVPixelBuffer?,
presentationTimeStamp: CMTime,
duration: CMTime) in
let decoder: VideoFrameDecoder = Unmanaged<VideoFrameDecoder>.fromOpaque(decompressionOutputRefCon!).takeUnretainedValue()
if imageBuffer != nil && status == noErr {
decoder.imageDecompressed(image: imageBuffer!)
} else {
decoder.delegate?.frameDecodingFailed()
print("***** FAILED TO DECOMPRESS. VT ERROR \(status)")
}
}
Pretty basic. And just to reiterate, I'm always getting "VT ERROR -12909"

How can I create a String from UTF8 in Swift?

We know we can print each character in UTF8 code units?
Then, if we have code units of these characters, how can we create a String with them?
With Swift 5, you can choose one of the following ways in order to convert a collection of UTF-8 code units into a string.
#1. Using String's init(_:) initializer
If you have a String.UTF8View instance (i.e. a collection of UTF-8 code units) and want to convert it to a string, you can use init(_:) initializer. init(_:) has the following declaration:
init(_ utf8: String.UTF8View)
Creates a string corresponding to the given sequence of UTF-8 code units.
The Playground sample code below shows how to use init(_:):
let string = "Café 🇫🇷"
let utf8View: String.UTF8View = string.utf8
let newString = String(utf8View)
print(newString) // prints: Café 🇫🇷
#2. Using Swift's init(decoding:as:) initializer
init(decoding:as:) creates a string from the given Unicode code units collection in the specified encoding:
let string = "Café 🇫🇷"
let codeUnits: [Unicode.UTF8.CodeUnit] = Array(string.utf8)
let newString = String(decoding: codeUnits, as: UTF8.self)
print(newString) // prints: Café 🇫🇷
Note that init(decoding:as:) also works with String.UTF8View parameter:
let string = "Café 🇫🇷"
let utf8View: String.UTF8View = string.utf8
let newString = String(decoding: utf8View, as: UTF8.self)
print(newString) // prints: Café 🇫🇷
#3. Using transcode(_:from:to:stoppingOnError:into:) function
The following example transcodes the UTF-8 representation of an initial string into Unicode scalar values (UTF-32 code units) that can be used to build a new string:
let string = "Café 🇫🇷"
let bytes = Array(string.utf8)
var newString = ""
_ = transcode(bytes.makeIterator(), from: UTF8.self, to: UTF32.self, stoppingOnError: true, into: {
newString.append(String(Unicode.Scalar($0)!))
})
print(newString) // prints: Café 🇫🇷
#4. Using Array's withUnsafeBufferPointer(_:) method and String's init(cString:) initializer
init(cString:) has the following declaration:
init(cString: UnsafePointer<CChar>)
Creates a new string by copying the null-terminated UTF-8 data referenced by the given pointer.
The following example shows how to use init(cString:) with a pointer to the content of a CChar array (i.e. a well-formed UTF-8 code unit sequence) in order to create a string from it:
let bytes: [CChar] = [67, 97, 102, -61, -87, 32, -16, -97, -121, -85, -16, -97, -121, -73, 0]
let newString = bytes.withUnsafeBufferPointer({ (bufferPointer: UnsafeBufferPointer<CChar>)in
return String(cString: bufferPointer.baseAddress!)
})
print(newString) // prints: Café 🇫🇷
#5. Using Unicode.UTF8's decode(_:) method
To decode a code unit sequence, call decode(_:) repeatedly until it returns UnicodeDecodingResult.emptyInput:
let string = "Café 🇫🇷"
let codeUnits = Array(string.utf8)
var codeUnitIterator = codeUnits.makeIterator()
var utf8Decoder = Unicode.UTF8()
var newString = ""
Decode: while true {
switch utf8Decoder.decode(&codeUnitIterator) {
case .scalarValue(let value):
newString.append(Character(Unicode.Scalar(value)))
case .emptyInput:
break Decode
case .error:
print("Decoding error")
break Decode
}
}
print(newString) // prints: Café 🇫🇷
#6. Using String's init(bytes:encoding:) initializer
Foundation gives String a init(bytes:encoding:) initializer that you can use as indicated in the Playground sample code below:
import Foundation
let string = "Café 🇫🇷"
let bytes: [Unicode.UTF8.CodeUnit] = Array(string.utf8)
let newString = String(bytes: bytes, encoding: String.Encoding.utf8)
print(String(describing: newString)) // prints: Optional("Café 🇫🇷")
It's possible to convert UTF8 code points to a Swift String idiomatically using the UTF8 Swift class. Although it's much easier to convert from String to UTF8!
import Foundation
public class UTF8Encoding {
public static func encode(bytes: Array<UInt8>) -> String {
var encodedString = ""
var decoder = UTF8()
var generator = bytes.generate()
var finished: Bool = false
do {
let decodingResult = decoder.decode(&generator)
switch decodingResult {
case .Result(let char):
encodedString.append(char)
case .EmptyInput:
finished = true
/* ignore errors and unexpected values */
case .Error:
finished = true
default:
finished = true
}
} while (!finished)
return encodedString
}
public static func decode(str: String) -> Array<UInt8> {
var decodedBytes = Array<UInt8>()
for b in str.utf8 {
decodedBytes.append(b)
}
return decodedBytes
}
}
func testUTF8Encoding() {
let testString = "A UTF8 String With Special Characters: 😀🍎"
let decodedArray = UTF8Encoding.decode(testString)
let encodedString = UTF8Encoding.encode(decodedArray)
XCTAssert(encodedString == testString, "UTF8Encoding is lossless: \(encodedString) != \(testString)")
}
Of the other alternatives suggested:
Using NSString invokes the Objective-C bridge;
Using UnicodeScalar is error-prone because it converts UnicodeScalars directly to Characters, ignoring complex grapheme clusters; and
Using String.fromCString is potentially unsafe as it uses pointers.
improve on Martin R's answer
import AppKit
let utf8 : CChar[] = [65, 66, 67, 0]
let str = NSString(bytes: utf8, length: utf8.count, encoding: NSUTF8StringEncoding)
println(str) // Output: ABC
import AppKit
let utf8 : UInt8[] = [0xE2, 0x82, 0xAC, 0]
let str = NSString(bytes: utf8, length: utf8.count, encoding: NSUTF8StringEncoding)
println(str) // Output: €
What happened is Array can be automatic convert to CConstVoidPointer which can be used to create string with NSSString(bytes: CConstVoidPointer, length len: Int, encoding: Uint)
Swift 3
let s = String(bytes: arr, encoding: .utf8)
I've been looking for a comprehensive answer regarding string manipulation in Swift myself. Relying on cast to and from NSString and other unsafe pointer magic just wasn't doing it for me. Here's a safe alternative:
First, we'll want to extend UInt8. This is the primitive type behind CodeUnit.
extension UInt8 {
var character: Character {
return Character(UnicodeScalar(self))
}
}
This will allow us to do something like this:
let codeUnits: [UInt8] = [
72, 69, 76, 76, 79
]
let characters = codeUnits.map { $0.character }
let string = String(characters)
// string prints "HELLO"
Equipped with this extension, we can now being modifying strings.
let string = "ABCDEFGHIJKLMONP"
var modifiedCharacters = [Character]()
for (index, utf8unit) in string.utf8.enumerate() {
// Insert a "-" every 4 characters
if index > 0 && index % 4 == 0 {
let separator: UInt8 = 45 // "-" in ASCII
modifiedCharacters.append(separator.character)
}
modifiedCharacters.append(utf8unit.character)
}
let modifiedString = String(modifiedCharacters)
// modified string == "ABCD-EFGH-IJKL-MONP"
// Swift4
var units = [UTF8.CodeUnit]()
//
// update units
//
let str = String(decoding: units, as: UTF8.self)
I would do something like this, it may be not such elegant than working with 'pointers' but it does the job well, those are pretty much about a bunch of new += operators for String like:
#infix func += (inout lhs: String, rhs: (unit1: UInt8)) {
lhs += Character(UnicodeScalar(UInt32(rhs.unit1)))
}
#infix func += (inout lhs: String, rhs: (unit1: UInt8, unit2: UInt8)) {
lhs += Character(UnicodeScalar(UInt32(rhs.unit1) << 8 | UInt32(rhs.unit2)))
}
#infix func += (inout lhs: String, rhs: (unit1: UInt8, unit2: UInt8, unit3: UInt8, unit4: UInt8)) {
lhs += Character(UnicodeScalar(UInt32(rhs.unit1) << 24 | UInt32(rhs.unit2) << 16 | UInt32(rhs.unit3) << 8 | UInt32(rhs.unit4)))
}
NOTE: you can extend the list of the supported operators with overriding + operator as well, defining a list of the fully commutative operators for String.
and now you are able to append a String with a unicode (UTF-8, UTF-16 or UTF-32) character like e.g.:
var string: String = "signs of the Zodiac: "
string += (0x0, 0x0, 0x26, 0x4b)
string += (38)
string += (0x26, 76)
This is a possible solution (now updated for Swift 2):
let utf8 : [CChar] = [65, 66, 67, 0]
if let str = utf8.withUnsafeBufferPointer( { String.fromCString($0.baseAddress) }) {
print(str) // Output: ABC
} else {
print("Not a valid UTF-8 string")
}
Within the closure, $0 is a UnsafeBufferPointer<CChar> pointing to the array's contiguous storage. From that a Swift String can be created.
Alternatively, if you prefer the input as unsigned bytes:
let utf8 : [UInt8] = [0xE2, 0x82, 0xAC, 0]
if let str = utf8.withUnsafeBufferPointer( { String.fromCString(UnsafePointer($0.baseAddress)) }) {
print(str) // Output: €
} else {
print("Not a valid UTF-8 string")
}
If you're starting with a raw buffer, such as from the Data object returned from a file handle (in this case, taken from a Pipe object):
let data = pipe.fileHandleForReading.readDataToEndOfFile()
var unsafePointer = UnsafeMutablePointer<UInt8>.allocate(capacity: data.count)
data.copyBytes(to: unsafePointer, count: data.count)
let output = String(cString: unsafePointer)
There is Swift 3.0 version of Martin R answer
public class UTF8Encoding {
public static func encode(bytes: Array<UInt8>) -> String {
var encodedString = ""
var decoder = UTF8()
var generator = bytes.makeIterator()
var finished: Bool = false
repeat {
let decodingResult = decoder.decode(&generator)
switch decodingResult {
case .scalarValue(let char):
encodedString += "\(char)"
case .emptyInput:
finished = true
case .error:
finished = true
}
} while (!finished)
return encodedString
}
public static func decode(str: String) -> Array<UInt8> {
var decodedBytes = Array<UInt8>()
for b in str.utf8 {
decodedBytes.append(b)
}
return decodedBytes
}
}
If you want show emoji from UTF-8 string, just user convertEmojiCodesToString method below. It is working properly for strings like "U+1F52B" (emoji) or "U+1F1E6 U+1F1F1" (country flag emoji)
class EmojiConverter {
static func convertEmojiCodesToString(_ emojiCodesString: String) -> String {
let emojies = emojiCodesString.components(separatedBy: " ")
var resultString = ""
for emoji in emojies {
var formattedCode = emoji
formattedCode.slice(from: 2, to: emoji.length)
formattedCode = formattedCode.lowercased()
if let charCode = UInt32(formattedCode, radix: 16),
let unicode = UnicodeScalar(charCode) {
let str = String(unicode)
resultString += "\(str)"
}
}
return resultString
}
}