I have a object Person with many dogs. App has separate page where it shows just dogs and other page where it shows person's dogs
My model is as follows
class Person: Object {
dynamic var id = 0
let dogs= List<Dog>()
override static func primaryKey() -> String? {
return "id"
}
}
class Dog: Object {
dynamic var id = 0
dynamic var name = ""
override static func primaryKey() -> String? {
return "id"
}
}
I have persons stored in Realm. Person has detail page where we fetch and show his dogs. If dog already exist, I update latest info for that dog and add it to person's dog list else create new dog, save it and add it to persons list. This works in coredata.
// Fetch and parse dogs
if let person = realm.objects(Person.self).filter("id =\(personID)").first {
for (_, dict): (String, JSON) in response {
// Create dog using the dict info,my custom init method
if let dog = Dog(dict: dict) {
try! realm.write {
// save it to realm
realm.create(Dog, value:dog, update: true)
// append dog to person
person.dogs.append(dog)
}
}
}
try! realm.write {
// save person
realm.create(Person.self, value: person, update: true)
}
}
On trying to update person with his dogs,realm throws exception Can't create object with existing primary key value
The problem here is that even though you're creating a completely new Realm
Dog
object, you're not actually persisting that one to the database, and so when you callappend
, you're trying to add a second copy.When you call
realm.create(Dog, value:dog, update: true)
, if an object with that ID already exists in the database, you're simply updating that existing object in with the values in thedog
instance you created, but thatdog
instance is still an independent copy; it's not theDog
object in the database. You can confirm this by checking ifdog.realm
is equal tonil
or not.So when you call
person.dogs.append(dog)
, becausedog
is not already in the database, Realm tries to create a whole new database entry, but fails because there is already a dog with that ID.If you want to append that
dog
object to aperson
, it'll be necessary to query Realm to retrieve a properdog
object that's referencing the entry in the database. Thankfully this is really easy with Realm objects backed by primary keys since you can use theRealm.object(ofType:forPrimaryKey:)
method: