Erik Erik - 4 months ago 19
iOS Question

Realm: prevent unnecessary updates to objects

I have a setup where I fetch some json data from a server to populate a table. There is a chance that the data has changed, so I fetch all the data each time. I map that data into a Realm object and persist it to the database. A primary ID is used to prevent duplication.

I use Realm notifications to keep the tableview/collection view in sync with the datasource. When an server request completes, objects are updated or added to the database, and the views automatically reload.

The problem is that all of the cells reload because all of the objects in the database get updated, even if they aren't actually any different because I'm just blindly using

realm.add(object, update:true)
. Is there is a good way to prevent updating objects that haven't actually changed so that cell's aren't needlessly reloaded?

The solution I tried was to write an extension to Realm's Object class including a function that checks if any objects with the same primary ID exist, compare them, and add/update the Object iff they don't match. However, I have many classes of objects, and I couldn't find a way to get the objects type from the object itself without knowing its class to start with.

// All subclasses of ServerObject have id as their primaryKey
let object = database.objectForPrimaryKey(type:???, key: self.id)


I do not want to copy the same hunk of check-before-add code to every one of my classes because that's asking for trouble, so I need something that can go in a protocol or extension, or just a completely different way to go about handling the server's response.

Answer

It sounds like you want something along the lines of:

extension Object {
    public func hasChanges(realm: Realm) -> Bool {
        guard let obj = realm.objectForPrimaryKey(self.dynamicType, key: self["id"])
            else { return true }

        for property in obj.objectSchema.properties {
            let oldValue = obj[property.name]
            let newValue = self[property.name]

            if let newValue = newValue {
                if oldValue == nil || !newValue.isEqual(oldValue) {
                    return true
                }
            } else if oldValue != nil {
                return true
            }
        }
        return false
    }
}

This would be used as:

let obj = MyObjectType()
obj.id = ...;
obj.field = ...;
if obj.hasChanges(realm) {
    realm.add(obj, update: true)
}