Robust Codable coding


(Howard Lovatt) #1

Hi All,

I have started to use Codable and was looking for some advice. I want to make the persisted data structure robust so that if I change the properties as the code develops the new code can deserialise an old saved file. This is what I am currently doing:

struct Project: Codable {
    var ecsVersion = 0
    var comment = ""
    
    init() {}
    
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        ecsVersion = try values.decodeIfPresent(Int.self, forKey: .ecsVersion) ?? ecsVersion
        comment = try values.decodeIfPresent(String.self, forKey: .comment) ?? comment
    }
}

The idea is that if I add fields the deserialisation doesn’t fail provided that I provide a default value. However this presumably fails when you delete a field because CodingKeys is now incorrect. (I say presumably because the documentation doesn’t say what will happen.) Though not ideal, I can leave disused properties in Projects to prevent the deserialisation error.

Any advice? Is there a better way?

Thanks in advance,

— Howard.


(Kyle Murray) #2

Hi Howard,

If I'm understanding your question correctly, you're asking: What happens if you remove, for example, the comment property in v2 of your Project structure?

The way you've set things up lets you remove all references to comment in your code, and it can still exist as a key in old data that works with the v2 code. The old key will just be ignored.

struct Project: Codable {
    var ecsVersion = 0

    init() {}

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        ecsVersion = try values.decodeIfPresent(Int.self, forKey: .ecsVersion) ?? ecsVersion
    }
}

let json = """
{
    "ecsVersion": 7,
    "comment": "A test comment."
}
""".data(using: .utf8)!

let decoded = try? JSONDecoder().decode(Project.self, from: json)
print(decoded as Any) // Prints "Optional(Project(ecsVersion: 7))"

···

---

If you still want comment to exist in your struct, but you don't want it to be sourced from an old saved file any longer, you just need to remove the decodeIfPresent line for that property. There isn't any CodingKeys exhaustiveness checking going on because you're not using the compiler-synthesized initializer.

Hope this helps,
Kyle

On Jun 23, 2017, at 5:36 PM, Howard Lovatt via swift-users <swift-users@swift.org> wrote:

Hi All,

I have started to use Codable and was looking for some advice. I want to make the persisted data structure robust so that if I change the properties as the code develops the new code can deserialise an old saved file. This is what I am currently doing:

struct Project: Codable {
    var ecsVersion = 0
    var comment = ""
    
    init() {}
    
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        ecsVersion = try values.decodeIfPresent(Int.self, forKey: .ecsVersion) ?? ecsVersion
        comment = try values.decodeIfPresent(String.self, forKey: .comment) ?? comment
    }
}

The idea is that if I add fields the deserialisation doesn’t fail provided that I provide a default value. However this presumably fails when you delete a field because CodingKeys is now incorrect. (I say presumably because the documentation doesn’t say what will happen.) Though not ideal, I can leave disused properties in Projects to prevent the deserialisation error.

Any advice? Is there a better way?

Thanks in advance,

— Howard.
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users