How do I deal with the String.Encoding initializer never failing?

Viewed 66
if let encoding = String.Encoding(rawValue: 999) {
  // ...
}

Produces a compiler error saying "Initializer for conditional binding must have Optional type, not 'String.Encoding'" because despite the docs saying the String.Encoding initializer is failable, it is not and will happily create non-existent encodings.

How do I check if the encoding returned by initializer is an actual encoding?

The two ideas I have are

  1. Check the String.Encoding description is not empty. This assumes that supported encodings must have a description
  2. Encode something - e.g. "abc".data(using: encoding) == nil - which assumes that the string "abc" can be encoded by all supported encodings
2 Answers

The documentation is misleading, String.Encoding has a non-failable

 public init(rawValue: UInt)

initializer. A list of all valid string encodings is String.availableStringEncodings, so you can use that to check the validity:

let encoding = String.Encoding(rawValue: 999)
print(String.availableStringEncodings.contains(encoding)) // false

You can use CoreFoundation's CFStringIsEncodingAvailable() function to check if the encoding is valid, and also if it can be used on that particular device.

However, as MartiR pointed out, CFStringIsEncodingAvailable needs a CFStringEncoding to work with, so the String.Encoding needs to be converted first to a CF one.

let encoding = String.Encoding(rawValue: 999)
let cfEncoding = CFStringConvertNSStringEncodingToEncoding(encoding.rawValue)

if CFStringIsEncodingAvailable(cfEncoding) {
  // ...
}

, or, as MartinR nicely suggested, the result of CFStringConvertNSStringEncodingToEncoding can also be used:

if CFStringConvertNSStringEncodingToEncoding(encoding.rawValue) != kCFStringEncodingInvalidId {
  // ...
}
Related