Observed managed objects & main thread


#1

Does it mean, when I want to observe changes into Results<> collection in main thread I MUST fetch the collection in the main thread as well? Is there any pattern to keep all database operations in background but still having the possibility to access managed objects from the main thread? As I know using dedicated dispatch queue is also not 100% secured solution while the same queue can be handled by different threads in background.

Use case:

I collect some objects in background and then pass them to the main thread for visualisation. I will NOT change anything.

Question:

Shall I iterate still in the background thread and make collection of ThreadSafeReference instances which I finally pass back to the main or I can just pass the query results keeping in mind that I use them in read-only mode?


#2

Creating a Results<T> using realm.objects(T.self).filter("…") does not eagerly fetch the objects. The filtering is deferred until the first time you retrieve an object from the collection.

If you use Results.observe(_:) to observe changes to the collection and refrain from accessing the collection prior to the initial results being delivered to the callback, all of the filtering will be performed on a background thread prior to the results being delivered to the callback on the main thread. This is the pattern I would recommend.


#3

That means, the Realm query is created in background and the method passes Results object back to consumer. The consumer calls Results.observe and hold the notification token. Once the Results.count is called for some reason, the “reading” performs in background and the observer gets notified by .initial() callback in main thread?


#4

Take a look at the documentation on collection notifications.

// All this code runs on the main thread:

let results = realm.objects(Person.self).filter("age > 5")

notificationToken = results.observe { [weak self] (changes: RealmCollectionChange) in
    guard let tableView = self?.tableView else { return }
    switch changes {
    case .initial:
        // Results are now populated and can be accessed without blocking the UI
    case .update(_, let deletions, let insertions, let modifications):
        // Query results have changed.
    case .error(let error):
        // An error occurred while opening the Realm file on the background worker thread
        fatalError("\(error)")
    }
}

The call to Results.observe(_:) results in the query immediately being evaluated on the background thread, after which the .initial notification is delivered. When any future changes are made that modify the set of objects present in results, the changes will be computed on a background thread and an .update notification will be delivered.

If you access results after the call to observe(_:) but before the .initial notification is delivered, Realm will be forced to synchronously evaluate the query on the main thread in order to return an accurate result. If you hold off on accessing results until after the .initial notification is delivered the query will only be evaluated on the background thread.


#5

I got it! Thank you for the clearance. I am very close to start loving Realm framework.


#6

Let’s say I wait for the .initial notification and only then access the results. Will now all further updates to the query also be evaluated on the main thread?

I’m asking because it seems that the results get evaluated synchronously on the main thread even when performing a transaction on a background thread. This is problematic, because the change includes the deletion of thousands of objects which also causes the order of objects to change in the results. Currently it takes minutes to complete and blocks the main thread.