How to decode/encode custom containers with different objects types in with Swift

Posted by Daniel Vela on June 17, 2019

How to decode/encode Github API event feed

Decoding automatically a custom object from json

When decoding an object like:

{
    "type": "foo",
    "payload": {
        "word": "hello"
        "push_id": 3711282988,
        "size": 1,
    }
}

With a struct implementing Codable can be parsed easyly.

struct Payload: Codable {
    var word: String
    var push_id: Int
	var size: Int
}

struct Foo: Codable {
    var type: String
	var payload: Payload	
}

let decoder = JSONDecoder()
let product = try decoder.decode(Foo.self, from: json)

But what happens when one of the inner properties of an object, includes a type that can be different.

Decoding manualy a custom object from json

[{
    "type": "foo",
    "payload": {
        "word": "hello"
        "push_id": 3711282988,
        "size": 1,
    }
},
{
    "type": "bar",
    "payload": {
        "size": 1,
		"radious": 0.5
    }
}]

In this case, the object cannot be parsed directly because the type of the payload object depends in the “type” property.

To solve this, the object must be decoded manually, this way.

struct FooPayload: Codable {
    var word: String
    var push_id: Int
	var size: Int
}

struct BarPayload: Codable {
    var size: Int
	var radious: Double
}

struct Foo {
    var type: String
    var payload: Payload?
    enum CodingKeys : CodingKey {
        case type, payload
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        type = try values.decode(String.self, forKey: .type)
        swith type {
        case "foo":
            payload = values.decode(FooPayload.self, forKey: .payload)
        case "bar":
            payload = values.decode(BarPayload.self, forKey: .payload)
        default:
            payload = nil
        }
    }
}