Migrating Codable object 
Tags: swift, migration, codable
Published: 2017-10-09 10:52:57

---
title: Migrating Codable object 
published: true
description: 
tags: swift, migration, codable
---

Original post https://github.com/onmyway133/blog/issues/83


As of swift 4 migration, we updated [Cache](https://github.com/hyperoslo/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

```swift

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

```swift
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.

```swift
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"
)
```