Realm migrations in Swift
Asked Answered
M

3

22

I have a Realm Object modeled as so

class WorkoutSet: Object {
     // Schema 0
     dynamic var exerciseName: String = ""
     dynamic var reps: Int = 0
     // Schema 0 + 1
     dynamic var setCount: Int = 0
}

I am trying to perform a migration.

Within my AppDelegate I have imported RealmSwift.

Within the function didFinishLaunchWithOptions I call

Migrations().checkSchema()

Migrations is a class declared in another file.

Within that file there is a struct declared as so.

func checkSchema() {
    Realm.Configuration(
        // Set the new schema version. This must be greater than the previously used
        // version (if you've never set a schema version before, the version is 0).
        schemaVersion: 1,

        // Set the block which will be called automatically when opening a Realm with
        // a schema version lower than the one set above
        migrationBlock: { migration, oldSchemaVersion in
            // We haven’t migrated anything yet, so oldSchemaVersion == 0
            switch oldSchemaVersion {
            case 1:
                break
            default:
                // Nothing to do!
                // Realm will automatically detect new properties and removed properties
                // And will update the schema on disk automatically
                self.zeroToOne(migration)
            }
    })
}

func zeroToOne(migration: Migration) {
    migration.enumerate(WorkoutSet.className()) {
        oldObject, newObject in
        let setCount = 1
        newObject!["setCount"] = setCount
    }
}

I am getting an error for adding setCount to the model

Mendymene answered 20/1, 2016 at 4:41 Comment(2)
What error do you get? Can you share that?Glim
Property 'setCount' has been added to latest modelMendymene
G
39

You will need to invoke the migration. Merely creating a configuration, won't invoke it. There are two ways of doing this:

  1. Set your configuration with migration as Realm's default configuration-

    let config = Realm.Configuration(
      // Set the new schema version. This must be greater than the previously used
      // version (if you've never set a schema version before, the version is 0).
      schemaVersion: 1,
    
      // Set the block which will be called automatically when opening a Realm with
      // a schema version lower than the one set above
      migrationBlock: { migration, oldSchemaVersion in
    
        if oldSchemaVersion < 1 {
          migration.enumerate(WorkoutSet.className()) { oldObject, newObject in
            newObject?["setCount"] = setCount
          }    
        }
      }
    ) 
    Realm.Configuration.defaultConfiguration = config   
    

OR

  1. Migrate manually using migrateRealm :

migrateRealm(config)

Now your migration should work properly.

Glim answered 20/1, 2016 at 5:19 Comment(1)
Is there a way to just do this automatically? I think this is pretty straight forward if the value could be nil... any thoughts?Nit
S
6

Because you just create Realm.Configuration. Migration block is invoked by Realm if needed. You cannot invoke migration directly.

So to be invoke migration block, you should set configuration object to Realm, or set as default configuration. Then create Realm instance.

So You need to do the following:

let config = Realm.Configuration(
    // Set the new schema version. This must be greater than the previously used
    // version (if you've never set a schema version before, the version is 0).
    schemaVersion: 1,

    // Set the block which will be called automatically when opening a Realm with
    // a schema version lower than the one set above
    migrationBlock: { migration, oldSchemaVersion in
        // We haven’t migrated anything yet, so oldSchemaVersion == 0
        switch oldSchemaVersion {
        case 1:
            break
        default:
            // Nothing to do!
            // Realm will automatically detect new properties and removed properties
            // And will update the schema on disk automatically
            self.zeroToOne(migration)
        }
})

let realm = try! Realm(configuration: config) // Invoke migration block if needed
Shurwood answered 20/1, 2016 at 6:33 Comment(2)
dont forget to call import RealmSwiftAntibaryon
Do not call let realm = try! Realm(configuration: config) with force. It's marked as throwing for a reason (for example there is no space on device to perform migration). Instead you should call it in a do-catch block.Ardyth
A
0

Update answer @kishikawa katsumi for Xcode 14.3:

func zeroToOne(migration: Migration) {
    migration.enumerateObjects(ofType: <Your Realm Object>.className()) { _, newObject in
        newObject![<Your new properties>] = nil
        // Ex: newObject!["release_date"] = nil
    }
}

let config = Realm.Configuration(
    // Set the new schema version. This must be greater than the previously used
    // version (if you've never set a schema version before, the version is 0).
    schemaVersion: 1,

    // Set the block which will be called automatically when opening a Realm with
    // a schema version lower than the one set above
    migrationBlock: { migration, oldSchemaVersion in

        // We haven’t migrated anything yet, so oldSchemaVersion == 0
        switch oldSchemaVersion {
        case 1:
            break
        default:
            // Nothing to do!
            // Realm will automatically detect new properties and removed properties
            // And will update the schema on disk automatically
            zeroToOne(migration: migration)
        }
    }
)

let realm = try! Realm(configuration: config) // Invoke migration block if needed
Alb answered 6/4, 2023 at 15:44 Comment(2)
This doesn't update the referenced answer at all. You've copied that answer's code without any change. It seems you've only changed the original question's zeroToOne function.Quinnquinol
Yes. It changed from "enumerate" to "enumerateObjects(ofType:)"Alb

© 2022 - 2024 — McMap. All rights reserved.