-
-
Save elfenlaid/14b9297993a51e40e5a722165fc800fe to your computer and use it in GitHub Desktop.
Example demonstrating how to use versioning for Codable structs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This gist demonstrates how you can implement versioning for a Codable struct to support loading | |
// old serialized data after changing the structure. Notable features of this solution: | |
// | |
// * No need to make new properties optional, or to perform post-processing on the struct after | |
// loading in ordeer to populate missing values | |
// * No need to change the call site - from the outside this struct behaves just the same | |
// as if we had implemented codable directly in the normal way. | |
// * Versioning can be applied individually to parents or leaves in a larger tree of | |
// structs without affecting the other elements | |
// * This approach will work even if the original struct was not designed with versioning in mind | |
// and was directly serialized without using an intermediate struct. | |
// | |
// Note: In this trivial case you could (and probably would) solve the problem in a simpler way | |
// without having multiple versions of the serialization struct by just making the `id` property | |
// optional, but the intention is to demonstrate how to handle an arbitrarily complex change. | |
import Foundation | |
struct User { | |
var name: String | |
var id: String | |
} | |
extension User: Codable { | |
// V1 used email instead of opaque id | |
private struct UserV1: Decodable { | |
let name: String | |
let email: String | |
} | |
// V2 matches current implementation | |
private struct UserV2: Decodable { | |
let name: String | |
let id: String | |
} | |
init(from decoder: Decoder) throws { | |
do { | |
let userV2 = try UserV2(from: decoder) | |
self.init(name: userV2.name, id: userV2.id) | |
} catch { | |
let userV1 = try UserV1(from: decoder) | |
self.init(name: userV1.name, id: userV1.email) | |
} | |
} | |
} | |
let oldJSON = """ | |
{ | |
"name": "Foo Bar", | |
"email": "[email protected]" | |
} | |
""" | |
let data = oldJSON.data(using: .utf8)! | |
let user = try! JSONDecoder().decode(User.self, from: data) | |
print(user) // User(name: "Foo Bar", id: "[email protected]") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment