NEW: Master Swift design patterns with my latest book! >>

< Previous: GCD 101: async()   Next: Easy GCD using performSelector(inBackground:) >

Back to the main thread: DispatchQueue.main

With this change, our code is both better and worse. It's better because it no longer blocks the main thread while the JSON downloads from It's worse because we're pushing work to the background thread, and any further code called in that work will also be on the background thread.

This change also introduced some confusion: the showError() call will get called regardless of what the loading does. Yes, there’s still a call to return in the code, but it now effectively does nothing – it’s returning from the closure that was being executed asynchronously, not from the whole method.

The combination of these problems means that regardless of whether the download succeeds or fails, showError() will be called on the background thread and its UIAlertController will be created and shown on the background thread. If the download succeeds, the JSON will be parsed on the background thread and the table view's reloadData() will be called on the background thread – and the error will be shown regardless.

Let’s fix those problems, starting with the user interface background work. It's OK to parse the JSON on a background thread, but it's never OK to do user interface work there.

That's so important it bears repeating twice: it's never OK to do user interface work on the background thread.

If you're on a background thread and want to execute code on the main thread, you need to call async() again. This time, however, you do it on DispatchQueue.main, which is the main thread, rather than one of the global quality of service queues.

We could modify our code to have async() before every call to showError() and parse(), but that's both ugly and inefficient. Instead, it's better to place the async() call inside showError(), wrapping up the whole UIAlertController code, and also inside parse(), but only where the table view is being reloaded. The actual JSON parsing can happily stay on the background thread.

So, inside the parse() method find this code:


…and replace it with this new code, bearing in mind again the need for [unowned self] in and self. to keep away strong reference cycles:

DispatchQueue.main.async { [unowned self] in

And now change the showError() method to this:

func showError() {
    DispatchQueue.main.async { [unowned self] in
        let ac = UIAlertController(title: "Loading error", message: "There was a problem loading the feed; please check your connection and try again.", preferredStyle: .alert)
        ac.addAction(UIAlertAction(title: "OK", style: .default))
        self.present(ac, animated: true)

The code is almost identical, it's just a matter of wrapping it up neatly to avoid strong reference cycles.

At this point, this code is in a better place: we do all the slow work off the main thread, then push work back to the main thread when we want to do user interface work. This background/foreground bounce is common, and you'll see it again in later projects.

However, the showError() call is still called no matter what happens with the loading, and to fix that I want to introduce you to something else.

Learn Server-Side Swift now!

Take your Swift code to the server and become a full-stack developer with my latest book: Server-Side Swift!

< Previous: GCD 101: async()   Next: Easy GCD using performSelector(inBackground:) >
Click here to visit the Hacking with Swift store >>