Hello URLSession, My Old Friend.
Talking to Servers
The Client-Server model dictates that the server component is to be designed so as to facilitate communication with one or more clients. This is a widely prevalent architecture that we employ in designing products and services and it’s not alien to iOS clients that we build today. On the iOS platform, we have a variety of frameworks assisting in accomplishing various things without having to rely on third-party frameworks. This includes communicating with the server whenever necessary without adding any overhead to your app or affecting performance.
I would like to discuss how the platform provides ways to talk to the server in this post. If you simply want to look at the code, I have a playground with all what we discuss in this post, feel free to check that out.
Meet the URL Loading System
Resources on the internet are identified using a URL, which simply is an abbreviation for Uniform Resource Locator. A URL is constructed with various components which include the protocol used for communication, the hostname, the TLD, the file path etc.
The URL Loading System built into iOS is responsible for interacting and communicating with servers over the internet using the standard Internet Protocols like https, ftp etc. The loading performed by this system is asynchronous to make sure the app is responsive and errors are handled accordingly.
The URL Loading System uses URLSession instances to manage the communication between the app and the server on a broad level, which further leverages URLSessionTask instances to perform tasks that download data from the server, be it images, files or anything practically using the URLSessionDataTask instance or upload data to the server using URLSessionUploadTask instance.
A URLSession can manage multiple URLSessionTasks:
The URLSession API is used to manage data transfers within the application and is essentially the object we employ to talk to the servers. It encompasses many URLSessionTasks which can either download or upload data from a server. This API provides various delegate methods which can be leveraged for functions including background downloads and authentication support.
A URLSession instance is typically instantiated with a URLSessionConfiguration object which specifies the connection behaviour like whether to allow transfer over a cellular network, etc. The
shared instance is a singleton on the URLSession class which is an instance without a session configuration object and can be used for very basic transfers when the requirements are minimal. Otherwise, there are three kinds of configurations we could use to instantiate a URLSession:
default session configuration is very much the basic setup which allows configuration of the session and allows data to be obtained using delegates
ephemeral session configuration restricts data writes to disk like caches, cookies etc, which can simply be regarded as a private session
background session configuration is used to upload and download data in the background when the app isn’t running and it’s one of the powerful features we’ll discuss shortly
A URLSession uses tasks to manage transfers like we’ve learnt sometime before.
URLSessionTask is an abstract class which describes a task object, and the different kinds of tasks that the session could manage are:
- Data Tasks (
- Download Tasks (
- Upload Tasks (
These tasks are used for purposes which are implied quite directly from their names.
Data Tasks are used for data transfers using the
Data objects and represents a stream of data.
Upload Tasks and Download Tasks are used for data transfers in the form of file objects, and both of them support background sessions which could be used to download and upload data in the background when the app isn’t active.
All kinds of tasks can be suspended, cancelled and resumed. In addition to this
URLSessionDownloadTask instances can be paused to allow for the ability to be resumed at a later point.
GETting and POSTing data
Let’s briefly go through how to put
URLSession to use to download an image from a URL using a Data Task.
Building a Configuration Object
Let’s use the default configuration to instantiate a session in this example.
Instantiating Session with Delegate
A Completion Handler is a swift block which is passed as an argument when creating a data task, this block is called once the task completes and is an appropriate place to use the data we’ve retrieved using our task.
Building a Data Task and Starting it
dataTask(with:completionHandler:) is used to create a task that downloads the image and in this case, we also pass a completion handler which is called after the task is completed. In most cases, this approach is sufficient to keep things simple. In times where we could use more control over the download/upload process, there is a delegate we could employ for this purpose: URLSessionDataDelegate which lets us handle task-level events specific to the tasks.
In a download task, we get a reference to the location of the file in the filesystem after the task has executed successfully. This can be an effective way to download resources locally and reference elsewhere.
Building a Download Task and Starting it
From the completion handler, we see that once the download task is executed the URL of the downloaded file and the type of resource downloaded will be printed. A Completion Handler is a simple way to handle such a use case. But often times we would like more control over the download task such as to monitor the download progress. In such cases, we can make use of URLSessionDownloadDelegate methods to implement progress tracking and such. But, please keep in mind you can only either use completion handler OR delegate methods, but not both.
Let’s use a separate session to implement this since we have to assign a delegate when we initialize a session and cannot do it after.
Notice we’re using a
lazy variable here to make sure we’re assigning the
self delegate after the current ViewController is initialised.
The delegate method where we could track progress is
We can implement it this way:
URLSession gives us extensive control over the download process. Now that we’ve seen how to download things, let’s try to upload some data which is important because hey — who enjoys one-way communication, let’s talk back to the server.
Upload Tasks are similar to the tasks that we’ve seen thus far, except they POST data to a server. The method for creating and executing an Upload Task stays the same as other tasks. There are two ways to deal with completion after Upload Tasks, one is using a completion handler when a session is created or the session delegate tasks similar to the data tasks we discussed before. The Upload Task calls the respective data task delegates and behaves like a data task after the upload phase of the request finishes.
In case we want to track the progress of the upload we could do so in the
method of the URLSessionDelegate similar to how we implemented it for the Download Task.
We’re simply printing the data back from the server, in case we like to parse this data it can be done easily by casting it to JSON or however the response of the server is.
Building an Upload Task
In this example, we simply build a dictionary (1) and convert it into a
Data object (2). Once we have a
data object we could create an upload task (3) from the data and resume the task. In case we would like more control over the task events, we should avoid passing in the completion handler, and implement
uploadTask delegate methods (detailed here).
Background Sessions are extremely useful in cases where we like to minimize the use of device resources and download things without keeping the user actively engaged. A background
URLSession can be created simply by using an identifier:
This identifier needs to be stored and could be used again to reassociate this to a session when the app is resumed, terminated or crashed. The App Delegate method where this could be handled is
This method is implemented in the app’s
AppDelegate.swift and completion can be handled within here, but note that the completion handler provided which is a part of UIKit needs to be called on the main thread.
I highly encourage you to use
URLSession for your networking on iOS, it’s easy to use and you could very well avoid the overhead of a networking library once you understand how to work with this API. Feel free to leave any feedback.