Home iOS & Swift Books UIKit Apprentice

Asynchronous Networking Written by Fahim Farook

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

You’ve got your app doing network searches and it’s working well. The synchronous network calls aren’t so bad, are they?

Yes they are, and I’ll show you why! Did you notice that whenever you performed a search, the app became unresponsive? While the network request happens, you cannot scroll the table view up or down, or type anything new into the search bar. The app is completely frozen for a few seconds.

You may not have seen this if your network connection is very fast, but if you’re using your iPhone out in the wild, the network will be a lot slower than your home or office Wi-Fi, and a search can easily take ten seconds or more.

To most users, an app that does not respond is an app that has crashed. The user will probably press the home button and try again — or more likely, delete your app, give it a bad rating on the App Store, and switch to a competing app.

So, in this chapter you will learn how to use asynchronous networking to do away with the UI response issues. You’ll do the following:

  • Extreme synchronous networking: Learn how synchronous networking can affect the performance of your app by dialing up the synchronous networking to the maximum.
  • The activity indicator: Add an activity indicator to show when a search is going on so that the user knows something is happening.
  • Make it asynchronous: Change the code for web service requests to run on a background thread so that it does not lock up the app.

Extreme synchronous networking

Still not convinced of the evils of synchronous networking? Let’s slow down the network connection to pretend the app is running on an iPhone that someone may be using on a bus or in a train, not in the ideal conditions of a fast home or office network.

First off, you’ll increase the amount of data the app receives — by adding a “limit” parameter to the URL, you can set the maximum number of results that the web service will return. The default value is 50, the maximum is 200.

➤ Open SearchViewController.swift and in iTunesURL(searchText:), change the line with the web service URL to the following:

let urlString = String(format: 

You added &limit=200 to the URL. Just so you know, parameters in URLs are separated by the & sign, also known as the “and” or “ampersand” sign.

➤ If you run the app now, the search should be quite a bit slower.

The network link conditioner

Still too fast for you to see any app response issues? Then use the Network Link Conditioner. This is an additional developer tool provided by Apple that allows you to simulate different network conditions such as bad cell phone networks, in order to test your iOS apps.

The More Developer Tools menu option
Lqe Dawo Tuwubixul Rierm leso osxuug

The Downloads for Apple Developers page
Zgi Kamndiuyy cim Ormtu Yivenaronw fupe

The Network Link Conditioner preference pane
Wsi Xuzjopk Vumb Yiwwolaikuf tfosoqalgi qifo

Adding the profile for a very slow connection
Ifmozw dki yfuvace dal e qalr rvul zabmobvaub

Device conditions

The Network Link Conditioner works very well for slowing down downloads on your simulator and to simulate poor network conditions. But it doesn’t work for actual devices. Wouldn’t it be great if there was a way you could simulate network conditions on an actual device as well?

Setting network link conditions for device
Nijhoyp kuwcirm qibk nuvpepeeqj hek rezese

The activity indicator

Did you notice how the app totally doesn’t respond during a search operation? It feels like something is wrong. Did the app crash or is it still doing something? It’s impossible to tell and very confusing to your users when this happens.

The app shows that it is busy
Tpe ikh ylicj wtir ed eq mifq

The activity indicator table view cell

➤ Create a new, empty nib file. Call it LoadingCell.xib.

The design of the LoadingCell nib
Hpe bocepk in xpi WooretgZobz bim

Use the activity indicator cell

To make this special table view cell appear, you’ll follow the same steps as for the “Nothing Found” cell.

static let loadingCell = "LoadingCell"
cellNib = UINib(
  nibName: TableView.CellIdentifiers.loadingCell, 
  bundle: nil)
  forCellReuseIdentifier: TableView.CellIdentifiers.loadingCell)
var isLoading = false
func tableView(
  _ tableView: UITableView, 
  numberOfRowsInSection section: Int
) -> Int {
  if isLoading {
    return 1
  } else if !hasSearched {
    . . . 
  } else if . . . 
func tableView(
  _ tableView: UITableView, 
  cellForRowAt indexPath: IndexPath
) -> UITableViewCell {
  // New code 
  if isLoading {
    let cell = tableView.dequeueReusableCell(
      withIdentifier: TableView.CellIdentifiers.loadingCell, 
      for: indexPath)
    let spinner = cell.viewWithTag(100) as! UIActivityIndicatorView
    return cell
  } else 
  // End of new code
  if searchResults.count == 0 {
    . . .
func tableView(
  _ tableView: UITableView, 
  willSelectRowAt indexPath: IndexPath
) -> IndexPath? {
  if searchResults.count == 0 || isLoading {    // Changed
    return nil
  } else {
    return indexPath
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
  if !searchBar.text!.isEmpty {
    // New code
    isLoading = true                    
    // End of new code
    . . .
    isLoading = false                     // New code

Test the new loading cell

➤ Run the app and perform a search. While search is taking place the Loading… cell with the spinning activity indicator should appear…

func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
  if !searchBar.text!.isEmpty {
    isLoading = true
       . . . the networking code (commented out) . . . 

The main thread

The CPU (Central Processing Unit) in the oldest iPhone and iPad models had one core, which meant it can only do one thing at a time. Later models had a CPU with two cores, which allows for a whopping two computations to happen simultaneously. The latest Apple mobile CPUs have six cores.

Make it asynchronous

To prevent blocking the main thread, any operation that might take a while to complete should be asynchronous. That means the operation happens in a background thread and in the mean time, the main thread is free to process new events.

Queues have a list of closures to perform on a background thread
Qeiuav sawa e neyt iq wqorofor gu qafdasr ip o tasmqteiwh fwmoex

Put the web request in a background thread

To make the web service requests asynchronous, you’re going to put the networking part from searchBarSearchButtonClicked(_:) into a closure and then place that closure on a medium priority queue.

func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
  if !searchBar.text!.isEmpty {
    . . .
    searchResults = []
    // Replace all code after this with new code below
    // 1
    let queue = DispatchQueue.global()
    let url = self.iTunesURL(searchText: searchBar.text!)
    // 2
    queue.async {
      if let data = self.performStoreRequest(with: url) {
        self.searchResults = self.parse(data: data)
        self.searchResults.sort(by: <)
        // 3

Put UI updates on the main thread

The reason I removed all the user interface code from the closure — and moved getting the search URL outside the closure — is that UIKit has a rule that UI code should always be performed on the main thread. This is important!

DispatchQueue.main.async {
  self.isLoading = false

All kinds of queues

When working with GCD queues you will often see this pattern:

let queue = DispatchQueue.global()
queue.async {
  // code that needs to run in the background
  DispatchQueue.main.async {
    // update the user interface

The main thread checker

I mentioned previously that you should not run UI code on a background thread. However, till just a few years ago, there was no easy way to discover UI code running on background threads except by scouring the source code laboriously line-by-line trying to determine what code ran on the main thread and what ran on a background thread.

Edit scheme
Ajop vbniko

Main Thread Checker setting
Saom Vvxoik Zcodbih kulniks

let url = self.iTunesURL(searchText: searchBar.text!)
queue.async {
    let url = self.iTunesURL(searchText: searchBar.text!)
Main Thread Checker: UI API called on a background thread: -[UISearchBar text]
PID: 92439, TID: 13807148, Thread name: (none), Queue name: com.apple.root.default-qos, QoS: 0
4   StoreSearch                         0x000000010785e823 $s11StoreSearch0B14ViewControllerC09searchBarB13ButtonClickedyySo08UISearchF0CFyycfU_ + 291
5   StoreSearch                         0x000000010785eec8 $sIeg_IeyB_TR + 40
6   libdispatch.dylib                   0x0000000107a39034 _dispatch_call_block_and_release + 12
. . .
Purple icons indicating Main Thread Checker issues
Lerwxa onern attezocagd Feil Ckfaos Jjusniq alpiam

Issue navigator
Irsua rivetoqem

Commit your code

➤ I think with this important improvement, the app deserves a new version number. So commit the changes and create a tag for v0.2. You will have to do this as two separate steps — first create a commit with a suitable message, and then create a tag for your latest commit.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.

Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:

© 2021 Razeware LLC

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.