FREE: Follow my new 100 Days of Swift challenge! >>

< 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. And 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 [weak self] in and self?. to keep away any risk of strong reference cycles:

DispatchQueue.main.async { [weak self] in

To stop showError() being called regardless of the result of our fetch call, we need to move it inside the call to in viewDidLoad(), like this: .userInitiated).async { [weak self in
    if let url = URL(string: urlString) {
        if let data = try? Data(contentsOf: url) {
            self?.parse(json: data)


Remember, we need to add self?. to the showError() call because it’s inside a closure now.

But this has created a second problem: showError() creates and shows a UIAlertController – we now have user interface work happening on a background thread, which is always a bad idea.

So, we need to modify showError() to push that work back to the main thread, like this:

func showError() {
    DispatchQueue.main.async { [weak 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 any strong reference cycles. This code is easy enough to read and say for sure that there aren’t any reference cycles, but there’s no harm using weak self anyway!

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.

< Previous: GCD 101: async()   Next: Easy GCD using performSelector(inBackground:) >
Buy Testing Swift Buy Practical iOS 12 Buy Pro Swift Buy Swift Design Patterns Buy Swift Coding Challenges Buy Server-Side Swift (Vapor Edition) Buy Server-Side Swift (Kitura Edition) Buy Hacking with macOS Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with Swift Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let me know!

Average rating: 3.6/5

Click here to visit the Hacking with Swift store >>