Multithreading Realm Practices

I’m refactoring my DataProvider code to better support synced realms, and all of the example code I find opens and persists a realm DB on the main thread. Then either passes that realm between views or holds it in a Singleton class.

Which is fine. Except for the fact that this now locks you into having every DB operation on that main thread.

If you obviously try to do a write on a background thread then, you’ll immediately be confronted with:

'RLMException', reason: 'Realm accessed from incorrect thread.'

The examples also contradict the documentation that clearly states:

:roll_eyes:
To help alleviate this and better support multithreading, I’ve written a wrapper to always (re)open a realm partition for that sole operation.

struct DALconfig {
    static let partitionKey = "_partition"
}


extension DataProvider where Self: Object {

    static func openRealm(partition: String) -> Realm? {
        if partition.isEmpty {
            DDLogError("Database error: empty partition value")
            fatalError("Database error: empty partition value")
        }

        let config = realmApp.currentUser!.configuration(partitionValue: partition)
        let realmConfiguration = setup(config: config) // update with other params.

        do {
            return try Realm(configuration: realmConfiguration)
        } catch let error {
            DDLogError("Database error: \(error)")
            fatalError("Database error: \(error)")
        }
    }


    static func createOrUpdateAll(with objects: [Self], update: Bool = true) {
        if objects.isEmpty {
            DDLogError("⚠️ 0 objects")
            return
        }

        let objectPartitionValue = objects.first?.value(forKeyPath: DALconfig.partitionKey) as! String

        autoreleasepool {
            do {
                let database = self.openRealm(partition: objectPartitionValue)
                try database?.write {
                    database?.add(objects, update: update ? .all : .error)
                }
            } catch let error {
                DDLogError("Database error: \(error)")
                fatalError("Database error: \(error)")
            }
        }
    }

...


let car = RLMCar(partitionKey: Partition.user)
car.brand = "Datsun"
car.colour = "Neon Green"

RLMCar.createOrUpdateAll(with: [car])

This way, you can perform a write operation regardless of what thread you might be on, because the realm DB will specifically be opened for your transaction on that same thread.

One important detail though with this method, is that because the realm DB is opened and destroyed (closed) within the same function scope, the sync engine doesn’t get time to trigger automatically.

To solve this, as part of my app init(), I have to open the same realm DB on a main thread and persist it in global memory, so that its sync engine can stay active and be triggered by either the iOS event loop or calling .userRealm?.syncSession?.resume() to refresh a sync operation in the background.

This “works”. But… is there perhaps a more elegant solution to this kind of work?

Hi @Jay ,

Regarding your own question around threading off main, re:Writing Sync Data on main thread, did you find a clean and elegant pattern to use a syncing realm within a multithreaded environment?