Observing changes on a subset of Results


#1

I have a use-case where I’m trying to display the top 5 recently-added entities in a collection, sorted by their creation date (which I’m adding manually). These entities may change while still within that view, so I need a way to observe when only those limited results change.

I’ve seen the docs on limiting results, which suggests taking slicing in order to achieve limiting since results are lazy. However, it doesn’t look like this takes into account the scenario where you only want to subscribe to a limited subset of results. In lieu of there being an actual limit() function, is there any way that I could subscribe to only a subset of the results collection?

I know that I can manually check the indices in .update, but that seems clunky and I’d rather not do that if it can be avoided :slight_smile: I also don’t think object-level notifications would help here as “recently added” could mean a new entity was added.

Any help here is appreciated!


#2

When you create a Realm filter, the resulting Results object is updated live as objects come in to or fall out of the filter criteria. As you mentioned results are lazily loaded so there’s usually no reason to limit() the data, you can simply get the top 5 like this example from the documentation.

let dogs = try! Realm().objects(Dog.self).sortedBy creationDate, inverted so newest is 'at the top'
for i in 0..<5 {
    let dog = dogs[i]
    // ...
}

If your case, if the objects are changing and coming into/out of the filter criteria, you can simple use the observer to fire the above code and then reload your collection view, which would then display the top 5 and always remain updated.


#3

Thanks for the reply! So I have seen those docs, but my issue with this approach is that in order to use the UITableView’s {begin,end}Updates methods, you’d have to manually search through the indices in order to get the top 5 most recently added. While this seems trivial at first glance (insertions.filter { $0 < 5 }), if the top-most entity gets deleted, I would then have to add the “6th” (now the 5th) item into the table. Compounding scenarios make this situation tricky. I was hoping to avoid this logic if possible, but it seems there’s no way to?

Let me know if what I’m saying is unclear, and thanks for your help!


#4

That’s a little confusing and you may be overthinking it. Let me expound on my prior response to add some clarity. Keep in mind that the sort is descending so the most current results are ‘at the top’ (index 0)

Look at the filter we have on Dog objects

.objects(Dog.self).sortedBy creationDate

This means that our dog results will be sorted by the creation date, descending so the most recently created dog will always be at the top (index 0) with the second most recently added dog following that (index 1) etc.

and then look at the loop

for i in 0..<5

The loop will print the dogs from index 0 to 4 and because we sorted descending, those will be the 5 dogs with the most current creation date.

If we have 20 dogs in our list, and create a new dog today, that list will be sorted and the dog we just created will be at the top of the list (index 0), and by adding it, an event will fire so you will know to update the UI.

So in the observe event handler, we don’t care about delete, insert or mod - we simply need to refresh our tableView when something changes.

case .update(_, _, _, _):
    //refresh our tableView
   //     assuming the tableView is using the self.dogResults as its datasource
    for i in 0..<5 { //prints the most current dogs anytime there's a change
       let dog = dogs[i]
      print(dog)
   }