realm-swift icon indicating copy to clipboard operation
realm-swift copied to clipboard

Enhancement to Results<T> Queries and Collection Update notifications

Open jeffreybergier opened this issue 8 years ago • 6 comments

Enhancement Request

Goals

Search is a critical part of most apps that contain a lot of data. On top of that, modern user interfaces have created the standard of "live" search and users expect this feature. Live search generally includes the following features:

  • As the underlying data changes, either on their current device, or from other devices, the search is automatically updated.
    • Preferably with a meaningful animation as data is added to and removed from the results
  • As the user types, the data updates automatically to match their query
    • Preferably with a meaningful animation as data is added to and removed from the results

Its this last item which is not easy to implement when using Realm.

From an implementation perspective when data is shown in an iOS app using Realm, a Results<Element> object is generated with a predicate query. Then an object subscribes to the collection notifications and populates a view with the data when initial notification is fired. Note that this usually doesn't include an animation because its assumed this set of data has no relation to any other data that was on the screen. Animation is intended in the update notification. There its assumed the data was mostly the same as before and allows the developer to easily animate changes in the view for the user.

Expected Results

Currently, the easiest way to make a live search that supports all the features mentioned above is to take advantage of predicates and Realm collection notifications. When the query from the user changes, a new Results object is generated with the new predicate and then the view can be updated in the initial notification. However, the initial update doesn't allow the view to updated in an animated way, or at least, not easily.

It would be fantastic if there was a way to have the conceptual equivalent of an NSMutablePredicate. When the developer creates a Results object from an NSMutablePredicate based query it has only 1 extra feature. The feature is that whenever the predicate is changed, the update notification is fired rather than the initial notification. This would allow the developer to easily update the UI in an animated way.

I don't want to imply an implementation. I just used the made-up concept of NSMutablePredicate as a way to explain the enhancement. Another way of explaining it might be: A way of being able to update the underlying query of a Results object without having to reset the collection notifications - which causes the initial notification to be fired instead of the update notification.

Actual Results

When the user changes their query, the developer has to unsubscribe from notifications changes, create a new Results object with a new predicate and then subscribe to notifications again. When the initial notification is made, the developer can update the view in a non-animated way.

Version of Realm and Tooling

Realm version when this enhancement was made: 2.4.2

jeffreybergier avatar Feb 16 '17 22:02 jeffreybergier

I think the main difficulty here is just figuring out the API, since this isn't something that can be expressed well with our current abstractions. Implementation-wise handling changing the query isn't too bad since there's no big difference between calculating the changes between two versions of the data with one query and two different queries (although maybe things get weird if you change the query while a notification is already in flight?).


An awful thing that you could (but probably shouldn't) do with the current API is the following:

class Search: Object {
  let objects = List<MyModel>()

  var token: NotificationToken? = nil
  var query: Results? = nil {
    didSet {
      realm.write {
        objects.clear()
        if let query = query {
          objects.add(query)
          token = query.addNotificationBlock { changes in
            realm.write {
              objects.clear()
              objects.add(query)
            }
          }
        }
      }
    }
  }

  class func ignoredProperties() -> [String] {
    return ["token", "query"]
  }
}

func setUp() {
  let realm = try! Realm()
  let search = Search()
  try! realm.write { realm.add(search) }

  let token = search.objects.filter("TRUEPREDICATE").addNotificationBlock { change in
    // ... do whatever stuff
  }
  search.query = realm.objects(MyModel.self).filter("initial search")
}

The basic idea is that you store the results of the query in a List and then update your UI based on the changes to the List (with the filter("TRUEPREDICATE") making it so that the changes aren't just "hey you cleared it and then added a bunch of new items").

tgoyne avatar Feb 17 '17 00:02 tgoyne

@tgoyne thats a really really interesting workaround. I'm going to play around with something like that.

I imagine that if I didn't want to muck up my user's realm or be syncing these Search objects (which are more of a means to an end rather than something I want to keep) I could use a temporary realm on disk on this line

try! realm.write { realm.add(search) }

and use my normal user logged in syncing realm on this line

search.query = realm.objects(MyModel.self).filter("initial search")

*Edit - The performance penalty of copying from one realm to another for every search may not be worth it. I'm guessing that syncing a new object that has links to a bunch of existing objects is not that much overhead.

Thanks again for the idea. I'm going to play with this.

jeffreybergier avatar Feb 17 '17 00:02 jeffreybergier

+1 Ability to get change-sets after updating/filtering existing results would be supremely useful.

kunalsood avatar Feb 23 '17 10:02 kunalsood

+1

kajensen avatar Aug 21 '17 20:08 kajensen

+1

iandundas avatar Feb 10 '18 15:02 iandundas

+1

Any progress on this here? Since filtering any fetched result from your data store is a widespread feature I think this should be reminded very soon. Otherwise the notifications-feature "as is" is not very useful for many developers.

orschaef avatar Jul 22 '18 16:07 orschaef