When should I do user interface work on the main thread while using Firebase?

Viewed 106

I heard that I should always do user interface work on the main thread while reading data from Firebase.

I tried to do self.tableView.reloadData on the background thread inside a Firebase observe(.value) function and the app did not crash or freeze.

databaseReference.observe(.value) { [weak self] snapshot in
        guard let self = self else { return }
        self.tableView.reloadData()
}

I tried to do the same work inside getData function instead of observe(.value) function and the app crashed.

databaseReference.getData { [weak self] error, snapshot in
        guard let self = self else { return }
        self.tableView.reloadData()
}

So why the app crashed inside getData function and it did not crash inside observe(.value) function?

2 Answers

firebaser here

While the Firebase SDK performs its network and other I/O on a background thread, it actually calls your callback on the main thread.

But it seems that getData does not do that here, which is a bug. So thanks for catching and reporting that.

I filed bug #8245 on the Github repo, so check there for progress.

UI updates (tableView.reloadData, for instance) must be done on the main thread only not dependant if Firebase or another third-party framework is used.

If you are not sure if a method of a third-party framework (as Firebase is) will execute its closure on the main thread you can check if you are on the main thread and dispatch execution onto it if needed.

For example, you can use this simple extension for Thread class to run any code that needs to be run on the main thread.

extension Thread {
    class func runOnMainThread(_ closure: @escaping () -> Void) {
        if Thread.isMainThread {
            closure()
        } else {
            DispatchQueue.main.async(execute: closure)
        }
    }
}

Then you can use it inside Firebase completion handlers as following:

Thread.runOnMainThread {
   self.tableView.reloadData()
}
Related