pls
pls

Reputation: 1532

Realm - Realm Accessed from incorrect thread Swift

I am new to Realm and getting a 'Realm accessed from incorrect thread' error. I have read that one must access data on the same realm it was retrieved from. However I am getting this error after successfully being able to access the object. Here is some code:

func retrieveApplicationData(completionBlock: (Results<Application>, NSError?) -> Void) {
        let realm = try! Realm()
        let applications = realm.objects(Application)

        if applications.count > 0 {
            completionBlock(applications, nil)
        }   
}

This method calls a method which create an array of applications by using: let array = Array(results)

I am then passing this to a method which sets an array:

func setApplicationItems(items: [Application]) {
    applications = items

    print(applications)
}

In the above method I am printing out the array and that works fine. However later in the cycle the tableViewDatasource method cellForRowAtIndexPath gets called. In here I am trying to use the applications array but the application crashes with the 'incorrect thread' error. Here is the method:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    print("\(applications)")

    let cell = ApplicationTableViewCell(style: .Subtitle, reuseIdentifier: nil)
    let application = applications[indexPath.row]
    cell.configureCell(application)

    return cell
}

As you can see, I have added a print statement in the above method. When I try to print the applications array out here I get the crash.

How come I can print it out in the method that is setting it, but here it's crashing it? Is it because cellForRowAtIndexPath is called on the main thread? And if so how can I update my tableView in this instance? Cheers.

========

Edit:

When I do the following in cellForRowAtIndexPath I get back an empty result:

let realm = try! Realm()
let applicationsB = realm.objects(Application)

Edit 2:

I handle the saving in a closure. I tried passing it back over into main thread using dispatch_async when the data returned and it was still crashing. However I moved this dispatch_async to just before I call self.tableView.reloadData(). It now doesn't crash which is good but the data is not always available. If I slow it down by using breakpoints I get the data back. However the data is not there if I just let it run. Is there a way to know when the data saved on a background thread is available on the main thread?

Here is the code I am using:

 dispatch_async(dispatch_get_main_queue()) {               
            let realm = try! Realm()
            realm.refresh()
            let applicationsB = realm.objects(Application)
            let array = Array(applicationsB)
            self.tableViewDataSource.setApplicationItems(array as! [Application])
            self.tableView.reloadData()
        }

Data is not always there.

Edit 3:

I have now wrapped the save method in a dispath_async(dispath_get_main_queue()) and it works fine:

    dispatch_async(dispatch_get_main_queue()) {

            let realm = try! Realm()
            try! realm.write() {

            let applications = data.map({ (object) -> Application in
                return Application(applicationID: object.applicationID, contact: "", state: "", jobBoard: object.jobBoard, salary: object.salary, title: object.title, location: "")
            })

            for application in applications {
                print(application)
                realm.add(application)
            }

            //try! realm.commitWrite()

            completionBlock(true, nil)

        }

Upvotes: 2

Views: 4460

Answers (1)

pls
pls

Reputation: 1532

Edit:

Please read comment from bdash below. Although this works, it's actually only by chance. Leaving answer here so others don't try the same thing.

Following code worked but is not correct!!!:

So I have managed to get it working. First I stopped the app and added code into view will appear which fetched the data. This worked fine. So I knew that the data was in the database. However I wanted to save the data and then immediately use it.

To achieve this, I needed to add the following try! realm.commitWrite() inside my realm.write() { } block. Full method below:

try realm.write() {

       let applications = data.map({ (object) -> Application in
                return Application(applicationID: object.applicationID, contact: "", state: "", jobBoard: object.jobBoard, salary: object.salary, title: object.title, location: "")
        })

        for application in applications {
            print(application)
            realm.add(application)
        }

        try! realm.commitWrite() // *NOTE* Here is the code I added.

        completionBlock(true, nil)

    }

Once I added the commitWrite() I was able to do my fetch later on the main thread like so:

 dispatch_async(dispatch_get_main_queue()) {

            let realm = try! Realm()
            realm.refresh()
            let applicationsB = realm.objects(Application)
            let array = Array(applicationsB)

            self.tableViewDataSource.setApplicationItems(array as! [Application])


            self.tableView.reloadData()
        }

Upvotes: 0

Related Questions