Realm accessed from incorrect thread - Swift 3

12,277

Solution 1

It seems like the write is happening on a different thread than the object was originally accessed from. You should be able to fix it by passing task's id and using that to fetch it from the database right before you do the write (inside the async block).

So at the top:

var taskId = 0  // Set this accordingly

and then something like

self.queue.async {
    autoreleasepool {
        let realm = try! Realm()
        let tempTask = // get task from Realm based on taskId
        realm.beginWrite()
        realm.delete(tempTask)
        do {
            try realm.commitWrite()
        } catch let error {
            self.presentError()
        }
    } 
 }

Solution 2

We need to understand the fact Realm Objects cannot be accessed from different threads. What does this means and how to workout this issue.

First, realm objects cannot be access from different thread means, one instance of Realm defined in one thread cannot be access from different thread. What we should do actually is we need to have different instance of realm instance for each thread.

For eg. let's look at following e.g. where we insert 50 records in database asynchronously in background thread upon button click and we add notification block in main thread to update the no of people in count label. Each thread (main and background ) have its own instance of realm object to access Realm Database. Because Realm Database achieves thread safety by making instances of Realm thread-confined.

class Person: Object {
    dynamic var name = ""
    convenience init(_ name: String) {
        self.init()
        self.name = name
    }
}


override func viewDidAppear(_ animated: Bool) {
    let realmMain = try!  Realm ()
    self.people = realmMain.objects(Person.self)
    self.notification = self.people?.addNotificationBlock{ [weak self] changes in
        print("UI update needed")
        guard let countLabel = self?.countLabel else {
            return
        }
        countLabel.text = "Total People: \(String(describing: self?.people?.count))"
    }
}

@IBAction func addHandler(_ sender: Any) {
    print(#function)
    let backgroundQueue = DispatchQueue(label: "com.app.queue",
                                        qos: .background,
                                        target: nil)



    backgroundQueue.async {
        print("Dispatched to background queue")
        let realm = try! Realm()
        try! realm.write {
            for i in 1..<50 {
                let name = String(format: "rajan-%d", i)
                //print(#function, name)
                realm.add(Person(name))
            }
        }

    }
}
Share:
12,277
AppreciateIt
Author by

AppreciateIt

Updated on June 13, 2022

Comments

  • AppreciateIt
    AppreciateIt almost 2 years

    At the top of my UITableViewController is the following:

    let queue = DispatchQueue(label: "background")
    

    When a task is deleted, the following executes:

    self.queue.async {
        autoreleasepool {
            let realm = try! Realm()
            realm.beginWrite()
            realm.delete(task)
            do {
                try realm.commitWrite()
            } catch let error {
                self.presentError()
            }
        } 
     }
    

    And then I receive the error

    terminating with uncaught exception of type realm::IncorrectThreadException: Realm accessed from incorrect thread.

    How could I fix this?

  • AppreciateIt
    AppreciateIt over 7 years
    Ah so I cannot read from main thread (UI thread) if I want to write? Should I be reconsidering using threading at all then?
  • Samantha
    Samantha over 7 years
    Yeah, there is something about Realm crashing when objects get passed between threads. This has happened to me a lot :)
  • AppreciateIt
    AppreciateIt over 7 years
    Do you think there would be any advantages in threading the writes if I also have to do an extra read?
  • Samantha
    Samantha over 7 years
    I think that depends on personal preference and/or your app. If it's slowing down the UI to do all the deletes on the main thread, then I could see using the async block...but if it doesn't really impact the app's responsiveness then it probably would be simpler (and few database operations) to keep all on one thread.
  • TiM
    TiM over 7 years
    Thanks for this! This is how we recommend offloading work to the background thread. You can set taskId as the primary key of the Realm object, and then retrieve a reference to it on a background thread by using Realm.object(ofType:forPrimaryKey:).
  • TiM
    TiM over 7 years
    As a general rule of thumb, we recommend offloading long-lasting Realm write transactions to the background thread as much as possible. It's possible to continue reading from Realm on other threads mid-write, but you need to be wary that if a write is in progress in the background and you try and start one on the main thread, that will block your UI until the background operation has completed.