1

This is not a duplicate question, please just continue reading!

I'm getting the "'RLMException', reason: 'Realm accessed from incorrect thread'" error message displayed when I use my RealmBackend singleton in a DispatchQueue.

This is my class:

class RealmBackend {

    static let shared = RealmBackend()
    var realm = try? Realm()
}

There are functions in it like addObject() which are accessing the realm.commitWrite() and the realm.beginWrite() functions.

Now I call the addObject function from a callback (UIRefreshControl) with the DispatchQueue.main.sync since I'm assuming the Realm object was created on the main Thread (I read somewhere on Github that you need the same Threads for instantiating and accessing the Realm() value).

As I stated before I always get this RLMException, has someone faced the same problem?

ph1psG
  • 568
  • 5
  • 24
  • That's because you need to create the Realm instance of the dispatch queue's background thread to use it on the dispatch queue's background thread.... It's also recommended to put it in an explicit autoreleasepool, too. – EpicPandaForce Nov 08 '16 at 20:10
  • @EpicPandaForce Could you please post a little code example in an answer? I tried that too (in a privat init of my singleton) but it gave me weird errors. – ph1psG Nov 08 '16 at 20:12
  • Simply creating the Realm instance on a background thread wouldn't be sufficient if there's also expectations the to use the instance on the main thread as well. You need ensure you're using separate Realm instances; one per thread. I've outlined how this works in my answer below. :) – TiM Nov 08 '16 at 21:57

1 Answers1

2

Realm instances are thread confined, meaning that they will only work on the thread in which they were created. If you try and pass an instance between threads, you'll get that incorrect thread exception you saw. This is done by design to guarantee Realm's ACID compliance.

There's not really much point in holding onto a hard reference to Realm instances, especially in singleton objects. Once a Realm instance for a specific thread is created, Realm will internally hold onto that reference and will simply re-supply it if you try and create a new one with the same settings down the line. As a result, there's very little overhead in calling Realm() multiple times.

If you need to use a Realm instance with a different configuration than the default Realm, Configuration objects are thread safe, so you can simply store a single reference to that, and pass it to Realm() whenever you need it.

class RealmBackend {

    static let shared = RealmBackend()

    // A Configuration for a Realm in the caches directory
    let cacheConfiguration = Realm.Configuration(
                     fileURL: URL(fileURLWithPath: /* Path to the Realm in Caches */),
                     readOnly: true)

    func addObject() {
        let cacheRealm = try! Realm(configuration: cacheConfiguration)
        // ... add object to cacheRealm
    }

    func getObject() -> Object {
        let cacheRealm = try! Realm(configuration: cacheConfiguration)
        let object = realm.objects(Object.self).first
        return object
    }
}

In this case, since Realm is being created on demand, even if the singleton methods are being called on separate threads, Realm will internally be able to provide the correct instance.

TiM
  • 15,812
  • 4
  • 51
  • 79
  • My pleasure! Glad I could help! :) – TiM Nov 08 '16 at 22:32
  • I'm getting the same error though, I'm not using this cache configuration but in every function I am initializing Realm like that `let realm = try? Realm()` – ph1psG Nov 09 '16 at 07:04
  • It's not just `Realm` instances; any objects derived from a `Realm` (i.e. `Object`, `Results` etc) need to be from the same thread. You can set an exception breakpoint in Xcode in order to determine the exact line of code causing the crash: http://stackoverflow.com/questions/17802662/exception-breakpoint-in-xcode/17802723 – TiM Nov 09 '16 at 18:39