Migration with Realm (RealmSwift Part 6)

https://realm.io/docs/swift/latest/

In this part we will cover following topics

  1. Why schema migration
  2. Schema Versioning in Realm
  3. Automatic Realm Migration
  4. Custom Realm Migration with examples

This is the continuation of the previous part . If you know the basics operation in Realm you can continue this .Download the starter project with title “MigrationRealm” and as shown in Figure 1 we created User model and saved it into the realm fair enough. Add this code in viewDidLoad and run the application

Figure 1

As shown in Figure 2 we did a couple of things

  1. Added do catch block for proper error handling
  2. Added title as new property in User model

Now run the application again and you will get the error “Migration is required” since Realm file failed to open and you can’t perform realm operation because every time you try to open the realm file with the data saved with the previous schema and you updated schema in your next run/update case your Realm() will always throw an error. If you force try realm your application will crash

let realm = try! Realm() . //In this case application will crash

Figure 2

Why Schema Migration

Imagine you have a User app installed on the user’s iPhone. The app creates a Realm file in the Documents folder and uses it to persist objects. At this point if you had saved any data with the previous model version, there will be a mismatch between what Realm sees defined in code and the data Realm sees on disk. When this occurs, an exception will be thrown when you try to open the existing file unless you run a migration. For our previous case new code expect title field but previously saved objects don’t have this field so there is a mismatch

Solution 1 (Delete old file)

Delete the application and run again with the code shown in Figure 3 and you will see we configured realm with deleteRealmIfMigrationNeeded = true which means when there is a mismatch delete the Realm file and create again. In this case you will delete the user’s data every time there is a migration required which is not a good solution

Figure 3

As shown in Figure 4 we updated schema by adding title property in User model and realm saw mismatch it deleted old data and created fresh file and you can see we lost User whose id = 20

Note: You use this property mostly in development phase. Don’t shipped with production code unless you will get low rating

Figure 4

Solution 2 (Schema Versioning Automatic Migration)

Delete the application and run again with the code shown in Figure 5 and you will see we configured realm with schemaVersion = 1 which means we are telling realm it’s our first version of the schama and if we increment this value do automatic migration.

Figure 5

As shown in Figure 6 we updated model schema since we need to do migration we changed schemaVersion = 2, realm introspect difference in schemaVersion it will do migration itself and you can see we opened realm successfully without deleting our old data. One thing to note User with Id = 21 was previously stored it get empty title field

As documented “Note that default property values aren’t applied to new objects or new properties on existing objects during migrations. We consider this to be a bug, and are tracking it as #1793.”

Figure 6

Solution 2 (Schema Versioning Custom Migration)

What if you want your old schema who don’t have title property when upgraded to new version will have default value. Let’s do it, before start first delete the application and write this code as shown in Figure 7. We saved user object into the realm.

Figure 7

Now application evolved and they decided to add title property in User model because server is sending this field which we need to show in our latest UI. As you can see we performed number of tasks

  1. Incremented schemaVersion to tell realm perform automatic migration (schema migration)
  2. Specified migration block contains code that Realm runs once during the schema migration, and it provides access to both the old and new schemas. When realm see difference in schema version it will run this block
  3. migrationBlock provides two closure arguments: firstmigration is an instance of the Migration helper class, which gives you access to the old and new object schemas and provides you with methods to access the data in the old and new Realm files. second → oldVersion is the schema version of the existing file, e.g., the one you’re migrating from and in our case it’s value is 1 since we are migrating from 1 to 2
  4. Iterate over all objects in the database stored with previous version and one thing to note both old and new parameter represents the same data stored but old represent the data with older scheme and new with newest schema and as shown in Figure 8 since we added title field on new schema and we are accessing this with the new model and assigning value to it

Note: Migration only done to the data stored with the previous schema and if put it in our previous case old represents the UserId = 20 with previous schema which don’t have title field and new represents the UserId = 20 with newer schema which have title field but point to note both represents the same user

You can access the existing data by enumerating over all of the objects of a certain type. The API is similar to Realm.objects(_), with the main difference being that it provides you with the objects from both the old and new Realms simultaneously.

Figure 8

More Custom Migration Examples

Delete the application first and as shown in Figure 9 we created User with our first schema version and saved into the realm, so far so good

Figure 9

As shown in Figure 10 we performed two custom migrations

  1. In previous stored data we saved User title with nil, now in our next release if we want to give some value to it we can do it , Note both new and old have title field since both version of the schema has title property
  2. Secondly Migration.renameProperty(onType:from:to:). When you rename a property in your code, Realm has no way to know that a property represents the same thing, given the fact it has a different name.
    For example, when you rename currentUser to isCurrentUser, the only information Realm has is that currentUser was deleted and isCurrentUser was added. Therefore, it deletes all data stored in currentUser and adds a new empty property named isCurrentUser to the class. To preserve the existing data stored on disk, you need to help Realm by adding the following to your migration block:
Figure 10

Now application evolved and User now have a 1:1 relationship with the Passport model and as shown in Figure 11 we added Passport model as well and created a relationship with the User model

Figure 11

As shown in Figure 12 let’s imagine User is migrating from schema version 2 to 3 for the simple case and we don’t cater 1 to 3 migration. You can see we performed number of tasks

  1. Incremented the schemaVersion from 2 to 3
  2. If User are migrating from 2 to 3 we created Passport model and assign it to the existing users data stored with previous schema in which there is no passport property
  3. As shown in console “migration passport Number” is printed on the existing data as well which means migration went well

Migration.create(_:value:) creates a new MigrationObject. You provide a class name matching a local Realm object (as a String) and a dictionary to initialize the object with.

Figure 12

Rule of Thumb :

Avoid Custom migration as much as possible because when you do you need to think many flows

Useful Links

https://store.raywenderlich.com/products/realm-building-modern-swift-apps-with-realm-database

Senior iOS Engineer | HungerStation | Delivery Hero