3

I have an array of UInt32 values. I would like to convert this array to a String.

This doesn't work:

let myUInt32Array: [UInt32] = [72, 101, 108, 108, 111, 128049]
let myString = String(myUInt32Array) // error
let myString = String(stringInterpolationSegment: myUInt32Array) // [72, 101, 108, 108, 111, 128049] (not what I want)

These SO posts show UTF8 and UTF16:

Community
  • 1
  • 1
Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393

2 Answers2

4

UnicodeScalar is a type alias for UInt32. So cast your UInt32 values to UnicodeScalar and then append them to a String.

let myUInt32Array: [UInt32] = [72, 101, 108, 108, 111, 128049]

var myString: String = ""

for value in myUInt32Array {
    if let scalar = UnicodeScalar(value) {
        myString.append(Character(scalar))
    }
}

print(myString) // Hello
Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
  • @jvarela, updated. Thanks for the notice. In addition to converting to `Character` first, the `UInt32` value must also be checked for `nil`. – Suragch Jan 02 '17 at 05:30
3

(The answer has been updated for Swift 4 and later.)

Using the Swift type Data and String this can be done as

let myUInt32Array: [UInt32] = [72, 101, 108, 108, 111, 128049, 127465, 127466]
let data = Data(bytes: myUInt32Array, count: myUInt32Array.count * MemoryLayout<UInt32>.stride)
let myString = String(data: data, encoding: .utf32LittleEndian)!
print(myString) // Hello

A forced unwrap is used here because a conversion from UTF-32 code points to a string cannot fail.

You can define a String extension for your convenience

extension String {
    init(utf32chars:[UInt32]) {
        let data = Data(bytes: utf32chars, count: utf32chars.count * MemoryLayout<UInt32>.stride)
        self = String(data: data, encoding: .utf32LittleEndian)!
    }
}

and use it as

let myUInt32Array: [UInt32] = [72, 101, 108, 108, 111, 128049, 127465, 127466]
let myString = String(utf32chars: myUInt32Array)
print(myString) // Hello

And just for completeness, the generic converter from https://stackoverflow.com/a/24757284/1187415

extension String {
    init?<C : UnicodeCodec>(codeUnits:[C.CodeUnit], codec : C) {
        var codec = codec
        var str = ""
        var generator = codeUnits.makeIterator()
        var done = false
        while !done {
            let r = codec.decode(&generator)
            switch (r) {
            case .emptyInput:
                done = true
            case .scalarValue(let val):
                str.unicodeScalars.append(val)
            case .error:
                return nil
            }
        }
        self = str
    }
}

can be used with UTF-8, UTF-16 and UTF-32 input. In your case it would be

let myUInt32Array: [UInt32] = [72, 101, 108, 108, 111, 128049, 127465, 127466]
let myString = String(codeUnits: myUInt32Array, codec : UTF32())!
print(myString) // Hello
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Thank you for your answer. I have a few followup questions. (1) When should I choose to use the Foundation classes as opposed to the answer I gave? (2) I've read and understand a little about [big endian and little endian byte order](http://betterexplained.com/articles/understanding-big-and-little-endian-byte-order/). But what is the reasoning behind using `NSUTF32LittleEndianStringEncoding` in your answer? Do I need to change anything when saving strings to a file or sending them over the network? If I just use the Swift classes can I avoid worrying about big/little endian encoding? – Suragch Jul 15 '15 at 15:47
  • 2
    @Suragch: 1) I don't think there is a clear advantage of one over the other. I have shown the NSData/NSString method as an alternative because it is a little less code, but your method works as well. (That's also why I find your bounty question difficult to answer: All those methods work, so you can choose what you feel most familiar with.) – Martin R Jul 15 '15 at 16:32
  • 2
    @Suragch: 2) All current OS X and iOS devices use little-endian for storing integer numbers in memory, therefore NSUTF32LittleEndianStringEncoding. Of course, if you sent data over the network to some different device with a possibly different byte order then you have to take care of that. But that is valid generally, and unrelated to your current question. – Martin R Jul 15 '15 at 16:34
  • @l--marcl: I have updated the code for the current Swift. – Martin R Jul 01 '19 at 17:42