Realm-cocoa: Primary key property has duplicate values after migration

Created on 15 Jul 2017  路  5Comments  路  Source: realm/realm-cocoa

happened on migration

my model version 1

class AAAA: Object {
    dynamic var code: Int = 10
}

and version 2

class AAAA: Object {
    dynamic var id: Int = 0
    dynamic var code: Int = 10
    override static func primaryKey() -> String? {
        return "id"
    }
}

migration code

func migrationVersion() {
        let config = Realm.Configuration(
            schemaVersion : 1 ,
            migrationBlock : { migration, oldSchemaVersion in
                if oldSchemaVersion < 1 { }
            }
        )
        Realm.Configuration.defaultConfiguration = config
}

then I try! Realm(), error was happen

fatal error: 'try!' expression unexpectedly raised an error: Error Domain=io.realm Code=1 "Primary key property 'AAAA.id' has duplicate values after migration." UserInfo={NSLocalizedDescription=Primary key property 'AAAA.id' has duplicate values after migration., Error Code=1}: file /Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-802.0.53/src/swift/stdlib/public/core/ErrorType.swift, line 182
(lldb)
T-Help

Most helpful comment

Primary keys are required to be unique across all objects of a given type. In your first schema version, you have no primary key, and no property named "id". When you migrate to schema version 1 (your second schema), Realm will populate all the missing properties for existing objects (in this case, id) with the default value you specified (in this case, 0, since you have var id: Int = 0 in your model). This means that if you have X number of AAAA objects in your Realm when you attempt the migration, you'll have X number of duplicate AAAA.id property values.

In your migration code, you'll need to make sure you're setting unique values for all those existing objects:

func migrationVersion() {
    let config = Realm.Configuration(
        schemaVersion : 1 ,
        migrationBlock : { migration, oldSchemaVersion in
            if oldSchemaVersion < 1 {
                var nextID = 0
                migration.enumerateObjects(ofType: AAAA.className()) { oldObject, newObject in
                    newObject!["id"] = nextID
                    nextID += 1
                }
            }
        }
    )
    Realm.Configuration.defaultConfiguration = config
}

All 5 comments

Primary keys are required to be unique across all objects of a given type. In your first schema version, you have no primary key, and no property named "id". When you migrate to schema version 1 (your second schema), Realm will populate all the missing properties for existing objects (in this case, id) with the default value you specified (in this case, 0, since you have var id: Int = 0 in your model). This means that if you have X number of AAAA objects in your Realm when you attempt the migration, you'll have X number of duplicate AAAA.id property values.

In your migration code, you'll need to make sure you're setting unique values for all those existing objects:

func migrationVersion() {
    let config = Realm.Configuration(
        schemaVersion : 1 ,
        migrationBlock : { migration, oldSchemaVersion in
            if oldSchemaVersion < 1 {
                var nextID = 0
                migration.enumerateObjects(ofType: AAAA.className()) { oldObject, newObject in
                    newObject!["id"] = nextID
                    nextID += 1
                }
            }
        }
    )
    Realm.Configuration.defaultConfiguration = config
}

oh i see!! because i have many AAAA in database, thank you very much

Happy to help.

Hello, I have same issue, but in my first scheme id is present and need to be update as primaryKey().
But I have id started not zero, how I can migrate to new scheme with primaryKey ?

This is how I resolved it.

func migrationVersion() {
    let config = Realm.Configuration(
        schemaVersion : 1 ,
        migrationBlock : { migration, oldSchemaVersion in
            if oldSchemaVersion < 1 {
                   migration.enumerateObjects(ofType: QAppendices.className()) { oldObject, newObject in
      if let id  = oldObject?["id"] as? NSNumber{

                        newObject!["id"] = id.intValue
                    }
                }
            }
        }
    )
    Realm.Configuration.defaultConfiguration = config
}
Was this page helpful?
0 / 5 - 0 ratings