Search code examples
iosswiftrealm

Swift Realm Migrations fails (duplicate values for primary key)


I need to migrate my Realm DB in Swift and getting errors due to duplicate values in regards to primary key.

I have reviewed both models and the migration block several times and cannot find my error. That's why I'd be thankful for any hints that might help.

Old data model:

class LabelObject: Object, Codable {
    
    @objc dynamic var identifier: String = UUID().uuidString
    
    @objc dynamic var geo_latitude: Double = 0
    @objc dynamic var geo_longitude: Double = 0
    @objc dynamic var geo_radius: Double = 300
    
    @objc dynamic var info_text: String = ""
    @objc dynamic var info_icon: String = ""
    @objc dynamic var info_placemark: String = ""
    
    @objc dynamic var color_red: Float = Float(UIColor.systemBlue.cgColor.components?[0] ?? 0)
    @objc dynamic var color_green: Float = Float(UIColor.systemBlue.cgColor.components?[1] ?? 0)
    @objc dynamic var color_blue: Float = Float(UIColor.systemBlue.cgColor.components?[2] ?? 0)
    @objc dynamic var color_alpha: Float = 1
    
    override class func primaryKey() -> String? {
        return "identifier"
    }

}

New data model:

class LabelObject: Object {
    
    @Persisted var identifier: String = UUID().uuidString
    
    @Persisted var geo_latitude: Double = 0
    @Persisted var geo_longitude: Double = 0
    @Persisted var geo_radius: Double = 150
    
    @Persisted var info_icon: String = "tag"
    @Persisted var info_text: String = ""
    @Persisted var info_placemark: String = ""
    
    @Persisted var color_red: Double = Double(UIColor.systemBlue.cgColor.components?[0] ?? 0)
    @Persisted var color_green: Double = Double(UIColor.systemBlue.cgColor.components?[1] ?? 0)
    @Persisted var color_blue: Double = Double(UIColor.systemBlue.cgColor.components?[2] ?? 0)
    @Persisted var color_alpha: Double = Double(UIColor.systemBlue.cgColor.components?[3] ?? 1)
    
    override class func primaryKey() -> String? {
        return "identifier"
    }
    
}

Migration block:

migration.enumerateObjects(ofType: "LabelObject") { oldObject, _ in
                                
guard let oldObject = oldObject else { return }
                                
let newLabelObject: MigrationObject = migration.create("LabelObject")
                                
newLabelObject["identifier"] = oldObject["identifier"] as? String ?? UUID().uuidString
                                
newLabelObject["geo_latitude"] = oldObject["geo_latitude"] as? Double ?? 0
newLabelObject["geo_longitude"] = oldObject["geo_longitude"] as? Double ?? 0
newLabelObject["geo_radius"] = oldObject["geo_radius"] as? Double ?? georyCurrentConfiguration.preferenceLabelRadius
                                
newLabelObject["info_text"] = oldObject["info_text"] as? String ?? ""
newLabelObject["info_icon"] = oldObject["info_icon"] as? String ?? ""
newLabelObject["info_placemark"] = oldObject["info_placemark"] as? String ?? ""
                                
newLabelObject["color_red"] = newLabelObject["color_red"] as? Float ?? UIColor.systemBlue.cgColor.components?[0] ?? 0
newLabelObject["color_green"] = newLabelObject["color_green"] as? Float ?? UIColor.systemBlue.cgColor.components?[1] ?? 0
newLabelObject["color_blue"] = newLabelObject["color_blue"] as? Float ?? UIColor.systemBlue.cgColor.components?[2] ?? 0
newLabelObject["color_alpha"] = newLabelObject["color_alpha"] as? Float ?? UIColor.systemBlue.cgColor.components?[3] ?? 0
                                    
}

But I keep getting

Primary key property 'class_LabelObject.identifier' has duplicate values after migration.

Thanks for any hints!


Solution

  • You're telling your new object to use the same primary key as the old object, which isn't allowed

    Let the new object populate the primary key on its own, which will copy the properties but assign it a unique ID.

    It also appears the only difference is the new model is Codable and if that's the case it can be removed without a migration block as it's unrelated to Realm

    Note that you only need local migrations for destructive changes like changing a property name or deleting a property.