Issue #83
As of swift 4 migration, we updated Cache to fully take advantage of Codable. It works for most cases, as we should usually declare our entity as typed safe object instead of array or json dictionary. And by conforming to Codable
, it is easily encoded and decoded to and from json data. And persisting them to Cache
is as easy as eating cookie.
The other day, I saw someone asking on how to migrate if the model changes https://github.com/hyperoslo/Cache/issues/153, and he likes the way Realm
does https://realm.io/docs/swift/latest/#migrations
Realm.Configuration.defaultConfiguration = Realm.Configuration(
schemaVersion: 1,
migrationBlock: { migration, oldSchemaVersion in
if (oldSchemaVersion < 1) {
// The enumerateObjects(ofType:_:) method iterates
// over every Person object stored in the Realm file
migration.enumerateObjects(ofType: Person.className()) { oldObject, newObject in
// combine name fields into a single field
let firstName = oldObject!["firstName"] as! String
let lastName = oldObject!["lastName"] as! String
newObject!["fullName"] = "\(firstName) \(lastName)"
}
}
})
I think we can rely on Codable
to the migration. FYI, here is the PR https://github.com/hyperoslo/Cache/pull/154
Class name change
I see Codable
is based on json, and the importance of json is its data structure, not the class name. So if you change the class name, it still works.
First, we save model of type Person
, later we load model of type Alien
. It works because the structure stays the same
struct Person: Codable {
let firstName: String
let lastName: String
}
struct Alien: Codable {
let firstName: String
let lastName: String
}
let person = Person(firstName: "John", lastName: "Snow")
try! storage.setObject(person, forKey: "person")
// As long as it has same properties, it works too
let cachedObject = try! storage.object(ofType: Alien.self, forKey: "person")
XCTAssertEqual(cachedObject.firstName, "John")
Property change
If the property changes, then you need to do a little work of migration.
First, we save model of type Person1
, it has just fullName
. Later we change the model to Person2
with some new properties. To do the migration, we need to load model with old Person1
first, then construct a new model Person2
based on this Person1
. Finally, save that to Cache
with the same key.
struct Person1: Codable {
let fullName: String
}
struct Person2: Codable {
let firstName: String
let lastName: String
}
// Firstly, save object of type Person1
let person = Person1(fullName: "John Snow")
try! storage.setObject(person, forKey: "person")
XCTAssertNil(try? storage.object(ofType: Person2.self, forKey: "person"))
// Later, convert to Person2, do the migration, then overwrite
let tempPerson = try! storage.object(ofType: Person1.self, forKey: "person")
let parts = tempPerson.fullName.split(separator: " ")
let migratedPerson = Person2(firstName: String(parts[0]), lastName: String(parts[1]))
try! storage.setObject(migratedPerson, forKey: "person")
XCTAssertEqual(
try! storage.object(ofType: Person2.self, forKey: "person").firstName,
"John"
)