Realm Migrations in Swift

Realm migrations in Swift

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.

Realm Migration: Migrating objects to another

You don't need to do anything with LinkingObjects, realm calculates those automatically when you query them.

All you'll need to do in your migration is set media to be a new Media object with the values you already have.

Other notes:

  • The second enumerateObjects isn't needed.
  • You can remove image, coverImage, and video from Item since you're moving those value to Media

Edit: This is what you would need to have in your migration.

let media = Media()
media.fullImage = oldItem?["image"] as! String
media.thumbnailImage = oldItem?["coverImage"] as! String
media.video = oldItem?["video"] as! String

newItem?["media"] = media

Swift Realm migration create reference from old type to new one

After long testing and trying different possibilities I managed to migrate the data.
Here is what I did to accomplish this.

I used this as a base:

class RealmMigrationObject {
let migration: () -> ()

init(migration: @escaping () -> ()) {
self.migration = migration
}
}

and derived classes from that. Something like:

class MigrationObjectToThree: RealmMigrationObject {

init() {
super.init(migration: MigrationObjectToThree.migration)
}

private static func migration() {
print("Migration to three | migration")

var imageInfos: [ImageInfo] = []

let config = Realm.Configuration(schemaVersion: 3, migrationBlock: { migration, oldSchemaVersion in

print("Migration to three | migrationBlock")
print("RealmMigration: Applying migration from \(oldSchemaVersion) to 3")

migration.deleteData(forType: "ExploreSectionObjectRealm")

migration.enumerateObjects(ofType: "ImageInfoRealm") { oldInfo, newObject in
guard let oldInfo = oldInfo else {
return
}

guard let id = oldInfo["id"] as? String,
let url = oldInfo["url"] as? String,
let url500 = oldInfo["url500"] as? String,
let url400 = oldInfo["url400"] as? String,
let url300 = oldInfo["url300"] as? String,
let url200 = oldInfo["url200"] as? String,
let url100 = oldInfo["url100"] as? String,
let colorString = oldInfo["color"] as? String,
let color = UIColor(hexString: colorString) else {
return
}
imageInfos.append(ImageInfo(id: id,
url: url,
url500: url500,
url400: url400,
url300: url300,
url200: url200,
url100: url100,
color: color))
}

})

Realm.Configuration.defaultConfiguration = config
do {
let realm = try Realm(configuration: config)
print("Realm is located at: \(realm.configuration.fileURL?.description ?? "")")
print(realm.configuration.fileURL?.description ?? "") // Printing here on purpose as it's easier to copy
} catch {
print("Realm Error: \(error), trying to rebuild realm from scratch")

let deleteMigrationConfig = Realm.Configuration(schemaVersion: RealmHelper.schemaVersion,
deleteRealmIfMigrationNeeded: true)
do {
_ = try Realm(configuration: deleteMigrationConfig)
} catch {
print("Failed to instantiate: \(error.localizedDescription)")
}
}

RealmHelper.removeRealmFiles()
Realm.Configuration.defaultConfiguration = Realm.Configuration(schemaVersion: 3)

imageInfos.forEach({ $0.save() })
}
}

From that I just created all migration for the difference between the current schema version and target schema version on looped over all migrations simply executing the migration function of that given object.

Understanding migrations in Realm - Swift

No, in this case you don't need to perform a migration. You only need to perform a migration in case existing objects need to be changed (adding/removing properties of existing Realm model classes).

If you add a new class to Realm, you don't need to perform a migration.

In most cases, Realm can automatically perform the migration and all you need to do is increase your schema version by 1. However, if you are for example combining two existing properties into a new one, you need to handle that manually in your migration block.

Realm migration introducing primary key

Did you just try to increment schema version and add property deleteRealmIfMigrationNeeded to true ?

Realm.Configuration.defaultConfiguration = Realm.Configuration(
schemaVersion: 2,
migrationBlock: { migration, oldSchemaVersion in },
deleteRealmIfMigrationNeeded: true
)

EDIT:

Sorry, it's not true because you need to keep only oldest people so try use delete method of realm migration object:

let config = Realm.Configuration(
schemaVersion: 1,
migrationBlock: { migration, oldSchemaVersion in
switch oldSchemaVersion {
case 0:
var objects: [String: Any] = []
migration.enumerateObjects(ofType: MyData.className()) {
(oldObject, newObject) in
if let age = oldObject["age"] as? Int, age > objects[oldObject["name"]] {
migration.delete(oldObject["name"])
objects[oldObject["name"]] = oldObject
} else {
migration.delete(oldObject)
}

}
default:
break
}
})

https://realm.io/docs/swift/latest/api/Classes/Migration.html#/s:FC10RealmSwift9Migration6deleteFCS_13DynamicObjectT_

Unable to do a Realm migration in Swift

This exception will be thrown when stored data doesn’t match the model you have in code.

You shouldn’t need to do anything in the migration block, however you will need to trigger a migration by updating the value of Realm.Configuration.schemaVersion, e.g.:

schemaVersion: 3,

How can I avoid migration in RealmSwift

There are two ways to skip migration error regardless schema changes.

  1. Use deleteRealmIfMigrationNeeded property. If it is true, recreate the Realm file with the provided schema if a migration is required.

    let config = Realm.Configuration(deleteRealmIfMigrationNeeded: true)
    Realm.Configuration.defaultConfiguration = config

    let realm = try! Realm()
    ...

      

  2. Increment schema version every launch. Realm has auto migration feature. If you don't need to migrate existing data, you can just increment schema version. Schema will be changed by Realm automatically.

    let config = Realm.Configuration(schemaVersion: try! schemaVersionAtURL(Realm.Configuration.defaultConfiguration.fileURL!) + 1)
    Realm.Configuration.defaultConfiguration = config

    let realm = try! Realm()
    ...

Access list property in Realm migration

I don't know what the full scope is of what your trying to do but a specific answer to the question would be:

let test = [ old!["bar"] ]

EDIT

The OP added some additional information. The goal is to be able to access the values within the bar List property. Here's some quickie code that iterates over that list and outputs the index and the object (as a string) to the console.

migration.enumerateObjects(ofType: Foo.className()) { oldItem, newItem in
let dynamicBarList = oldItem?.dynamicList("stringList")
if let list = dynamicBarList {
let myArray = list._rlmArray
let lastIndex = myArray.count - 1

for index in 0...lastIndex {
let object = myArray.object(at: index)
let value = "\(object)" //make the NSTaggedPointerString a String
print(index, value)
}
}
}

Realm migration causing intermittent crashes for some users

I may be mis-reading the question so please add a comment if I miss the mark and I will update the answer.

It appears you have a Realm object with no defined primary key

class Stuff2: Object {
@objc dynamic var primaryKeyTag = ""
@objc dynamic var localID = ""
}

and then at some point a primary key was needed and you want to make the localID property that primary key, so you added the primaryKey function to the object (that will trigger a migration)

class Stuff2: Object {
@objc dynamic var primaryKeyTag = ""
@objc dynamic var localID = ""

override static func primaryKey() -> String? {
return "localID"
}
}

So the only thing needed in that case is to take the old localID value and assign it to the new localID property (which is now the primary key)

let vers = UInt64(1)
let config = Realm.Configuration( schemaVersion: vers, migrationBlock: { migration, oldSchemaVersion in
if (oldSchemaVersion < vers) {
migration.enumerateObjects(ofType: TestClass.className()) { oldItem, newItem in

let oldVar = oldItem!["localID"] as! String
newItem!["localID"] = oldVar
}
}
})

There are two getcha's; primary keys cannot be nil and must be unique so there should be some error checking to ensure that's enforced.

Well, technically one objects primary key could be nil, but again, to enforce uniqueness it would be the only one.



Related Topics



Leave a reply



Submit