Fallible decoder for invalid type


Since The Codable intruduced back then, I'm struggling with decoder default value.

Imagine this type:

struct Animal: Codable {
    var tag: Int?
    var name: String?
    var type: String?
    var birthday: Date?

So all (or many) keys are optional and handled when they are nil from the caller perspective.

Then we have this JSON at the time of swift code written:

    "tag": 12,
    "name": "Dog",
    "type": "TYPE1"

So everything is ok. But sometime later, the json creator decides to change the json to something like this:

    "tag": "ANIMAL",
    "name": "Dog",
    "type": 1

as you can see, the json itself is not incorrect logically, but it doesn't match the swift code anymore and when we try to decode it, it will fail with incorrect data type error.

Yes, we can get it done with a custom initializer like this:

init (from decoder: Decoder) throws {
     let container = try decoder.container(keyedBy: CodingKeys. self )

     tag = ( try ? container.decodeIfPresent(Int.self , forKey: .tag)) ?? nil
     name = ( try ? container.decodeIfPresent(String.self , forKey: .name)) ?? nil
     type = ( try ? container.decodeIfPresent(String.self , forKey: .type)) ?? nil
     birthday = ( try ? container.decodeIfPresent(Date.self , forKey: .birthday)) ?? nil

So we bypassed the type validation and any other per key validation errors successfully. cheers :tada:

But what if we have hundreds of optional variables? We should write a line of code for each and all of them are almost the same!

Wasn't it nice to have something like this?

for key in container.allKeys {
   self.<#corresponding_property#> = (try? container.decodeIfPresent(<#corresponding_type#>.self, forKey: key)) ?? nil

Or something nicer like this:

struct Animal: Codable {
    @fallible var tag: Int?
    @fallible var name: String?  = "Update to see"
    @fallible var type: String?
    @fallible var birthday: Date?

I must mentioned in this case, imagine we have no access to server code and it is not following versioning and it is not backward compatible at all. the actual case is this happens because server and app are developing at the same time and sometimes api changes and need to update the code without getting crashed a lot. So it would be nice if we can put our custom initializer in #if DEBUG block.

So I would like to know how can I achieve something like this to set a default value for all properties at once or each by each with the prettier style. With using property wrapper? or Dynamic member lookup? or etc.

Thanks for your time.

Terms of Service

Privacy Policy

Cookie Policy