Realm swift notification stops firing

Viewed 403

I have set an observer for a live result object.

let token = realm.objects(RealmObject.self).observe(on: realmQueue) { changeset in
   print(changeset)
}

It works as expected for the most part, it triggers when I add/delete or edit RealmObject type objects in/to the realm db. My issue happens when I try to replace the whole array of objects with a new array. Basically what I do is I get all objects of type RealmObjects delete them and then I add the new array of RealmObjects. Now the issues is if I do the delete and add in one write transaction the notification mechanism seems to break, it doesn't trigger for this operation. On the other hand if I separate the delete and add into different write transactions then everything works with notifications (except I get two notifications as expected in this case, but it's not what I want). Am I missing something and doing something wrong ?

// triggers notifications
let objectsToDelete = realm.objects(RealmObject.self)
try realm.write {
    if !objectsToDelete.isEmpty {
        realm.delete(objectsToDelete)
    }
}
try realm.write {
    realm.add(objects)
}
// doesn't trigger notification
let objectsToDelete = realm.objects(RealmObject.self)
try realm.write {
    if !objectsToDelete.isEmpty {
        realm.delete(objectsToDelete)
    }
    realm.add(objects)
}

edit: After further investigation it seems that this only happens in a specific case. When replacing the items with the same number of items and same primary key (even though some other properties ore different).

class RealmObject: Object {
    @objc dynamic var primary: String!
    @objc dynamic var summary: String!
    
    override class func primaryKey() -> String? {
        return "primary"
    }
}

extension RealmObject {
    convenience init(summary: Int, uuid: String = UUID().uuidString) {
        self.init()
        self.primary = uuid
        self.summary = "Object nr. \(summary)"
    }
}
let sharedKeys = [UUID().uuidString, UUID().uuidString, UUID().uuidString]
let initialObjects = [RealmObject(summary: 0, uuid: sharedKeys[0]),RealmObject(summary: 1, uuid: sharedKeys[1]),RealmObject(summary: 2, uuid: sharedKeys[2])]
let replaceObjects = [RealmObject(summary: 3, uuid: sharedKeys[0]),RealmObject(summary: 4, uuid: sharedKeys[1]),RealmObject(summary: 5, uuid: sharedKeys[2])]
1 Answers

This rings bells (I worked on the Realm C# team 2015-2017).

I'm fairly certain it is because of optimisations around primary keys and their use within a transaction, combined with how the notification structure is built. Strictly speaking, it is a bug. You might expect these would be coming out as Delete+Add or as a Change record in the notification.

You are fighting a deep core assumption about how primary keys are used and optimisations that are intended to keep them working incredibly fast. I doubt strongly it will get fixed.

Switching to non-primary keys but indexed fields would probably get around this but complicate the rest of your code, sorry.

It's also possible you don't need these to be indexed at all, depending on your data volumes. Realm does some incredibly fast searching so I always recommend people try a non-indexed search first to see if that's enough.

Related