Search code examples
iosswiftrealm

Can we generate Realm results in background queue and use it on main thread


I'm starting using Realm recently, I'm not sure if my use case is valid:

Normally, when reading a lot of data from DB, I want to put it in a background queue so it will async get the data and later use it on main thread.

For example, I want to fetch several results based on city:

    private var results: [Results<SomeObject>?] = []
    autoreleasepool {
        DispatchQueue(label: "background").async {
            [unowned self] in
            do
            {
                let realm = try Realm()
                for i in 1...City.count
                {
                    self.results.append(realm.objects(SomeObject.self).filter("city=\(i)"))
                }
            }
            catch
            {
                NSLog("Failed to open Realm instance on background qeueue")
            }
        }
    }

And later use results to update my chart:

cell.setChartData(ChartDataFactory.createCombinedData(from: results[0]))

However if I apply this model for Realm, I'm getting error like

Terminating app due to uncaught exception 'RLMException', reason: 'Realm accessed from incorrect thread.

I understand I must use realm for each thread, and I can do this by reading realm on main thread, but I don't want the realm query block my main thread.

is there any way I can achieve my goal? e.g. reading realm in a background queue and access the results from another thread, while keeping the auto-refresh feature.

Thanks.


Solution

  • Realm has built-in functionality for running a query on a background thread and delivering the results to the main thread by using Results.observe().

    If you specifically need to perform expensive filtering logic that can't be expressed as a Realm query, you can manually pass an array of objects between threads using ThreadSafeReference.

    As of 5.0, you can now construct the query on a background thread and receive notifications on the main thread using the on: parameter to observe():

    DispatchQueue.global().async {
        let realm = try! Realm()
        let results = realm.objects(ObjectType.self).filter("property in %@", expensiveFunction(realm))
        self.token = results.observe(on: .main) { change in
            // do stuff with the results on the main thread
        }
    }