Search code examples
swiftcharacterprotocolscodable

Making Swift Class With Character Or Character-Based Property Codable


It appears that the Character class in Swift 4 does not implement the Codable protocol. I would like to understand the rationale for this, as it seems that the class would qualify as a foundational class in Swift.

Looking at the (intentionally stripped bare) recursive class below, what would then be the best way to make it Codable while retaining the use of Character as the Dictionary key?

class MyClass: Codable { <- does not compile
    var myProperty: [Character: MyClass]?
    var myOtherProperty: Int = 42
}

Thank you!


Solution

  • You can make Character conform to Codable protocol as follow:

    extension Character: Codable {
        public init(from decoder: Decoder) throws {
            var container = try decoder.unkeyedContainer()
            let string = try container.decode(String.self)
            guard !string.isEmpty else {
                throw DecodingError.dataCorruptedError(in: container, debugDescription: "Decoder expected a Character but found an empty string.")
            }
            guard string.count == 1 else {
                throw DecodingError.dataCorruptedError(in: container, debugDescription: "Decoder expected a Character but found a string: \(string)")
            }
            self = string[string.startIndex]
        }
        public func encode(to encoder: Encoder) throws {
            var container = encoder.unkeyedContainer()
            try container.encode(String(self))
        }
    }
    

    Playground testing

    let aClass = AClass()
    let bClass = AClass()
    bClass.myOtherProperty = 10
    aClass.myProperty = [:]
    aClass.myProperty?["B"] = bClass
    aClass.myOtherProperty = 20
    do {
        let jsonData = try JSONEncoder().encode(aClass)
        print(String(data: jsonData, encoding: .utf8)!)  // "{"myProperty":[["B"],{"myOtherProperty":10}],"myOtherProperty":20}\n"
        let decodedObject = try JSONDecoder().decode(AClass.self, from: jsonData)
        print(decodedObject.myProperty)      // "Optional(["B": __lldb_expr_73.AClass])\n"
        print(decodedObject.myOtherProperty) // 20
    } catch {
        print(error)
    }