Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom Dictionary Encoder & Decoder for the Codable protocol in Swift 4

If I have a struct that conforms to the Codable protocol like so:

enum AnimalType: String, Codable {
    case dog
    case cat
    case bird
    case hamster
}

struct Pet: Codable {
    var name: String
    var animalType: AnimalType
    var age: Int
    var ownerName: String
    var pastOwnerName: String?
}

How can I create an encoder & a decoder that encodes / decodes it to / from an instance of type Dictionary<String, Any?> like so?

let petDictionary: [String : Any?] = [
    "name": "Fido",
    "animalType": "dog",
    "age": 5,
    "ownerName": "Bob",
    "pastOwnerName": nil
]
let decoder = DictionaryDecoder()
let pet = try! decoder.decode(Pet.self, for: petDictionary)

NB: I'm aware that it's possible to use the JSONEncoder and JSONDecoder classes before casting the result to a dictionary object, but I don't want that for efficiency reasons.

The Swift standard library comes with the JSONEncoder and JSONDecoder as well as the PListEncoder and PListDecoder classes right out the box which conform to the Encoder and Decoder protocols respectively.

My problem is that I have no clue how to implement these protocols for my custom encoder and decoder classes:

class DictionaryEncoder: Encoder {

    var codingPath: [CodingKey]

    var userInfo: [CodingUserInfoKey : Any]

    func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key : CodingKey {

    }

    func unkeyedContainer() -> UnkeyedEncodingContainer {

    }

    func singleValueContainer() -> SingleValueEncodingContainer {

    }
}
class DictionaryDecoder: Decoder {

    var codingPath: [CodingKey]

    var userInfo: [CodingUserInfoKey : Any]

    func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key : CodingKey {

    }

    func unkeyedContainer() throws -> UnkeyedDecodingContainer {

    }

    func singleValueContainer() throws -> SingleValueDecodingContainer {

    }
}

Given that Swift is open sourced, it's possible to view the source code of the JSONEncoder and PListEncoder classes in the standard library, but the source files are huge and difficult to understand due to the lack of documentation apart from a few comments.

like image 921
Toni Sučić Avatar asked Oct 19 '17 13:10

Toni Sučić


1 Answers

If you look at the implementation of JSONDecoder (here), you'll see that it's a two-step process: 1. use JSONSerialization to convert the Data into an json dictionary, then 2. create an instance of an internal class _JSONDecoder to do the dictionary -> Codable object conversion.

There is some discussion on the Swift forums on possibly exposing the internal types, and the Swift team may do something about it in the future. Someone also offered up a 3rd party framework for doing what you want.

like image 112
Steven Grosmark Avatar answered Oct 02 '22 23:10

Steven Grosmark