Tracking Download Progress With Swift
An Introduction To URLSessionDownloadDelegate
Brief Overview
Recently I’ve been interested in building something that streams media. So last week, I started an example project, which streams ma4 music files from Apple’s iTunes Preview API. It’s been a fascinating learning experience. If you are interested, you can check it out here: Musicly. The gif at the top of this post is a recording of the project in use.
In the process, I managed to touch some features in iOS that I had I been interested in looking at, but hadn’t had the chance to use. I’ll be posting some examples of those features for the next few posts. To start off with, I want to go over monitoring the progress of a download in your application from both a data and UI perspective.
The example project for this post, which you can find in the gist link at the top, looks like this:
Brief Overview Of URLSession
Before we get started, let’s review some of the features that Apple provides for managing network requests. The URL Loading System is a set or ‘family’ of classes that Apple provides for dealing with URLs and accessing data on the internet.
Apple describes the URL Loading System as:
The URL loading system is a set of classes and protocols that allow your app to access content referenced by a URL. At the heart of this technology is the NSURL class, which lets your app manipulate URLs and the resources they refer to.
One of the most common ways it is used is working with the URL class. This allows us to access resources over the network as well as ones that are stored locally in the filesystem.
One of the most useful sets of functionality is encapsulated within the URLSession class, and its associated delegates. This suite of functions gives us the ability to manipulate how our application interacts with the data it wants to access over the network.
This isn’t the whole story. A lot the magic that makes this work is provided by Apple and happens under the hood, it’s a higher-level abstraction of how iOS deals with networking tasks.
Getting Started
To start with, let’s go over monitoring the progress of a download. This can be done visually with a progress bar or by giving a readout on a text label of the percentage complete.
To access the data, we’ll need to make our API client conform to URLSessionDownloadDelegate. This adventure will take us into the land of the Objective-C runtime. What this means in practical terms is that we will have our APIClient class to be a subclass of NSObject.
Side Note
So far I’ve briefly touched on the topic of networking. The subject is immense, and this doesn’t ever scratch the surface of what there is to learn about it. In fact, this is the opening sentence to Apple’s ‘About Networking’ in ‘Networking Overview’:
The world of networking is complex.
It’s a bit of an understatement but it illustrates my point. Apple has it’s rundown of the aspects of networking that it believes are relevant to developers in their ecosystem. You can find that guide here.
URLSessionDelegate and URLSessionDownloadDelegate
Apple specifies two methods that are relevant to this project in URLSession and URLSessionDownloadDelegate. They are:
urlSessionDidFinishEvents
didWriteData:totalBytesExpectedToWrite
The first method allows us to run networking calls on a background session. The second method gives us the data we need to monitor the progress of our download.
Side Note on BackgroundSessions and AppDelegates
When use background sessions you need to account for situations where the task is completed when your application is no longer running. If your network request completes when your application is no long active, the OS will relaunch it. In our AppDelegate we can handle this sort of relaunch by add this method:
We can then add an optional completion property to the AppDelegate which we can then access inside our APIClient.
To call this completion we add this functionality to our APIClient:
Delegates, Sessions and Models, Oh My!
Let’s create a model class for the state of our the data in between the final data object your class app uses and the URL.
As you might notice, this class has a delegate which is the intermediary between our APIClient, and it’s presentation to the user. To create it, make as class-bound protocol called DownloadDelegate and add it as a weak property to our Download model class.
This delegate specifies a method:
In our Download model we can call the downloadProgressUpdate delegate method in the following manner:
Now we can make our ViewController conform to the download delegate protocol. Since we’re modifying the layout every time the delegate gets called, you will need to specify that this update happens on the main thread (otherwise your application will crash.)
Obviously this isn’t the entire project, but it should be enough to get your started. If you want to see how everything is done, I’ve provided a link to a Gist at the top of this entry that has all the code you need to get up and running.