Sign in with GitHub to watch this episode for free.

Networking In Swift

Meet the URLSession Family

1 Meet the URLSession Family 06:01

AFNetworking has always been one of my favorite libraries and Alamofire is just as easy to like. Since the introduction of URLSession in iOS 7 and macOS Mavericks, I've been more reluctant to include either libraries in my projects.

The reason is simple. The URLSession API offers an easy-to-use, modern API for networking. It offers flexibility and several features many developers have been asking for. If a project can do without another dependency, then that's something worth considering.

URLSession is the successor of NSURLConnection. For many years, NSURLConnection has been the workhorse for networking on iOS and macOS. Most developers used or created a wrapper around NSURLConnection to hide the less enjoyable aspects of the API.

In addition to being a class, URLSession is a technology that provides the infrastructure for networking, exposed through a modern and elegant API. In this series, I introduce you to the URLSession stack. You learn how easy it is to get started with URLSession and you discover that URLSession exposes a flexible API that should meet anyone's networking needs.

Meet the Family

The URLSession family includes a number of core classes. The URLSession class is the key component of the URLSession stack. It's used to create and configure network requests.

Another important class is URLSessionConfiguration. A session configuration object is used to configure a URLSession instance. The session is in charge of managing requests. The session configuration defines the behavior of the session. This is especially interesting for uploading and downloading data.

The workhorses of URLSession are URLSessionTask and its concrete subclasses. The subclasses you interact with most are:

  • URLSessionDataTask for fetching data
  • URLSessionUploadTask for uploading data
  • URLSessionDownloadTask for downloading data

URLSessionDataTask and URLSessionDownloadTask directly inherit from URLSessionTask. URLSessionUploadTask is a subclass of URLSessionDataTask.

A URLSessionTask object is always associated with a session. You can create a URLSessionTask object by asking the session for one. This is illustrated by the following example. We discuss the details a bit later in this episode.

import Foundation

if let url = URL(string: "https://cocoacasts.com") {
    // Reference to Shared Session
    let session = URLSession.shared

    // Create Request
    let request = URLRequest(url: url)

    // Create Data Task
    let dataTask = session.dataTask(with: request)
}

Fetching Data

Now that we have met the family, it's time to create a request to see how the various pieces fit together. Create a new project in Xcode based on the Single View App template. Set Product Name to Networking and Language to Swift.

Setting Up the Project

Setting Up the Project

To illustrate how the URLSession stack works, we fetch an image from the web and display it in an image view. This simple example shows you how easy it is to create a request, schedule it, and handle its response.

Open Main.storyboard and add an image view to the View Controller Scene. Add the required constraints to pin the image view to the edges of its superview. Open the Attributes Inspector and set Mode to Aspect Fit.

Creating the User Interface

Open ViewController.swift and create an outlet for the image view. Revisit Main.storyboard and connect the outlet to the image view.

import UIKit

class ViewController: UIViewController {

    // MARK: - Properties

    @IBOutlet var imageView: UIImageView!

    // MARK: - View Life Cycle

    override func viewDidLoad() {
        super.viewDidLoad()
    }

}

Open ViewController.swift and locate the viewDidLoad() method.

// MARK: - View Life Cycle

override func viewDidLoad() {
    super.viewDidLoad()
}

We obtain a reference to a URLSession instance by invoking shared on the URLSession class. This returns a shared session object, a singleton, that uses the global cache, cookie storage, and credential storage. There are other options to create a session, but the shared session is fine for this example.

// MARK: - View Life Cycle

override func viewDidLoad() {
    super.viewDidLoad()

    // Obtain Reference to Shared Session
    let sharedSession = URLSession.shared
}

We create a URL instance and use it to create a request, an instance of the URLRequest class.

// MARK: - View Life Cycle

override func viewDidLoad() {
    super.viewDidLoad()

    // Obtain Reference to Shared Session
    let sharedSession = URLSession.shared

    if let url = URL(string: "https://goo.gl/wV9G4I") {
        // Create Request
        let request = URLRequest(url: url)
    }

}

To make a request, we ask the session for a data task by invoking dataTask(with:completionHandler:). This method accepts two arguments, the request and a closure. The closure accepts three arguments:

  • an optional Data instance
  • an optional URLResponse instance
  • and an optional Error instance

The closure is executed when the request completes, successfully or unsuccessfully.

// MARK: - View Life Cycle

override func viewDidLoad() {
    super.viewDidLoad()

    // Obtain Reference to Shared Session
    let sharedSession = URLSession.shared

    if let url = URL(string: "https://goo.gl/wV9G4I") {
        // Create Request
        let request = URLRequest(url: url)

        // Create Data Task
        let dataTask = sharedSession.dataTask(with: request, completionHandler: { (data, response, error) -> Void in

        })

    }

}

We use optional binding to safely unwrap the data that is returned to us and we use it to instantiate a UIImage object. We update the image view with the resulting image.

// MARK: - View Life Cycle

override func viewDidLoad() {
    super.viewDidLoad()

    // Obtain Reference to Shared Session
    let sharedSession = URLSession.shared

    if let url = URL(string: "https://goo.gl/wV9G4I") {
        // Create Request
        let request = URLRequest(url: url)

        // Create Data Task
        let dataTask = sharedSession.dataTask(with: request, completionHandler: { (data, response, error) -> Void in
            if let data = data, let image = UIImage(data: data) {
                DispatchQueue.main.async {
                    self.imageView.image = image
                }
            }
        })

    }

}

It's important to point out that we explicitly update the image view on the main thread. The closure we pass to dataTask(with:completionHandler:) is invoked on a background thread and, therefore, we are required to dispatch updating the image view to the main queue.

Last but not least, we schedule the data task by invoking resume() on the data task. This is often overlooked by developers that are new to URLSession.

// MARK: - View Life Cycle

override func viewDidLoad() {
    super.viewDidLoad()

    // Obtain Reference to Shared Session
    let sharedSession = URLSession.shared

    if let url = URL(string: "https://goo.gl/wV9G4I") {
        // Create Request
        let request = URLRequest(url: url)

        // Create Data Task
        let dataTask = sharedSession.dataTask(with: request, completionHandler: { (data, response, error) -> Void in
            if let data = data, let image = UIImage(data: data) {
                DispatchQueue.main.async {
                    self.imageView.image = image
                }
            }
        })

        dataTask.resume()
    }

}

Run the application in the simulator or on a physical device to see the result.

Downloading and Displaying an Image

What's Next?

Using URLSession is much easier than NSURLConnection. It's clear Apple decided it was time to rethink networking on its platforms when they created URLSession. In this episode, you learned about the very basics of URLSession. In the next episodes, we take a look at the more advanced options of the URLSession API.