Search code examples
swiftrealmnspredicate

Realm Notification is not triggered when NSPredicate changes


I have a simple ticked off list in Realm, where I am using NotificationToken to check for updates in the datasource and hereafter updates the tableView with the following items:

class Item : Object {
    dynamic var name = ""
    dynamic var isTickedOff = false
    dynamic var timeStamp : Date?
}

The model:

var items: Results<Item> {
    get {
        let results = self.realm.objects(Item.self)
        let alphabetic = SortDescriptor(property: "name", ascending: true)
        let tickedOff = SortDescriptor(property: "isTickedOff", ascending: true)
        let timestamp = SortDescriptor(property: "timeStamp", ascending: false)
        return results.sorted(by: [tickedOff, timestamp, alphabetic]).filter("isTickedOff = %@ || isTickedOff = %@", false, self.includeAll)
    }
}

I have a switch in my tableview, where the user can change the self.includeAll property.

When inserting, deleting items or selecting them (resulting in setting them to isTickedOff or !isTickedOff) triggers the notification and updates the tableView. However, changing the self.includeAll property does not trigger the notification even though the items property is modified. I could include self.tableView.reloadData() when the user triggers the switch, but I would like the more smooth animations through the notification.

Is it me, who understands notifications wrong or is it a bug?

Thanks in advance!


Solution

  • Realm doesn't observe external properties, so it can't know when a property that is being used in a predicate query has changed, and to then subsequently generate a change notification off that.

    When you access items the next time, that will give it a sufficient event to recalculate the contents, but by that point, it won't trigger a notification.

    Obviously, like you said, the easiest solution would be to simply call tableView.reloadData() since that will force a refetch on items but there'll be no animation. Or conversely, like this SO question says, you can call tableView.reloadSections to actually enable a 're-shuffling' animation.

    Finally, it might be a rather dirty hack, but if you still want it to trigger a Realm-based change notification, you could potentially just open a Realm write notification, change the includeAll property, and then close the write transaction to try and 'trick' Realm into performing an update.

    One final point of note. I might not have enough information on this, so this might be incorrect, but if you're registering a Realm notification block off the items property, be aware that the way you've implemented that getter means that a new object is generated each time you call it. It might be more appropriate to mark that property as lazy so it's saved after the first call.