Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Realm notifications registration while in write transaction

Tags:

swift

realm

I understand that you can not register a Realm .observe block on an object or collection if the Realm is in a write transaction.

This is easier to manage if everything is happening on the main thread however I run into this exception often because I prefer to hand my JSON parsing off to a background thread. This works great because I don't have to bog down the main thread and with Realm's beautiful notification system I can get notified of all modifications if I have already registered to listen for those changes.

Right now, if I am about to add an observation block I check to make sure my Realm is not in a write transaction like this:

    guard let realm = try? Realm(), !realm.isInWriteTransaction else {
        return
    }

    self.myToken = myRealmObject.observe({ [weak self] (change) in
        //Do what ever
    }

This successfully guards against this exception. However I never get a chance to re - register this token unless I get a little creative.

Does the Realm team have any code examples/ suggestions on a better pattern to avoid this exception? Any tricks I'm missing to successfully register the token?

like image 952
Jon Vogel Avatar asked Nov 15 '17 17:11

Jon Vogel


1 Answers

In addition to the standard function, I do use an extension for Results to avoid this in general. This issue popped up, when our data load grew bigger and bigger.

While we do now rewrite our observe functions logic, this extension is an interims solution to avoid the crashes at a first place. Idea is simple: when currently in a write transaction, try it again.

import Foundation
import RealmSwift

extension Results {
    public func safeObserve(on queue: DispatchQueue? = nil,
                            _ block: @escaping (RealmSwift.RealmCollectionChange<RealmSwift.Results<Element>>) -> Void)
    -> RealmSwift.NotificationToken {
        // If in Write transaction, call it again
        if self.realm?.isInWriteTransaction ?? false {
            DispatchQueue.global().sync {
                Thread.sleep(forTimeInterval: 0.1) // Better to have some delay than a crash, hm?
            }
            return safeObserve(on: queue, block)
        }
        // Aight, we can proceed to call Realms Observe function
        else {
            return self.observe(on: queue, block)
        }
    }
}

Then call it like

realmResult.safeObserve({ [weak self] (_: RealmCollectionChange<Results<AbaPOI>>) in
    // Do anything
})
like image 98
Lepidopteron Avatar answered Oct 18 '22 13:10

Lepidopteron