downloadTask(with:completionHandler:) vs. dataTask(with:completionHandler:)

YES! The Head to Head in downloading data

This is not just about where files are stored on the host device, it’s rather more than that. Although these two instance methods seem similar, we need to know the difference in order to produce great software.

Read on for the details, and there is a rather nice GitHub link at the bottom to help you with practical examples. Isn’t that nice?

Image for post
Image for post
Photo by Bruno van der Kraan on Unsplash

Difficulty: Beginner | Easy | Normal | Challenging

Prerequisites:

  • Some knowledge of downloading using urlsession would be helpful. You might want to use my HTTPManager (guide HERE)
  • In the github link the example uses a UIActivityIndicatorView and this use included a reference to weak self (guide HERE)
  • Some understanding of callbacks (closures) in Swift (guide HERE)
  • Some understanding of GCD (guide HERE)

Terminology

Completion Handlers: Also known as a closure, that is a self-contained block of functionality that can be passed around

Data: Information processed or stored by a computer

Delegation and Delegates: A pattern that enables a class to delegate responsibility for some actions to an instance of another class (the delegate)

downloadTask(with:completionHandler:) An instance method on URLSession that creates a download task to retrieve the contents of a URL, and saves the results to a file

dataTask(with:completionHandler:) An instance method on URLSession that retrieves the contents of a specific url

Instance: An example or single occurrence of something

URL: Uniform Resource Locator. An address of a web page or resource

URLSession: The class and related classes proved an API for downloading data to and from endpoints indicated by URLs

dataTask(with:completionHandler:)

Data task can return data to your app one piece at a time after each piece of data is received, or all at once through a completion handler. That is a completion handler returns the completed data object, or you return then piece by piece.

Think of dataTask working as downloading the resource to memory.

session = URLSession(configuration: .default, delegate: nil, delegateQueue: nil)
let task = session?.dataTask(with: url) {[weak self] data, urlresponse, error in
DispatchQueue.main.async {
self?.activityIndicator?.stopAnimating()
}
}
task?.resume()

Rather than using a completionHandler we can use a delegate to receive the data instead. The method of doing this is explained below.

This means that you receive the data using URLSessionDataDelegate rather than the closure above.

This can be used in the following, but then the app is responsible for accumulating this data if needed (for example by accumulating the Data objects together, as didReceive as described below only receives the data received by the previous call):

func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
print ("Receiving data")
}

This means that we set up the URLSession

var session : URLSession? = nil

Which is then set by setting up a URLSession with the delegate set

session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
let task = session?.dataTask(with: url)

If you wish to see how to set the delegate as a different file containing the appropriate methods, check out the repo link below.

downloadTask(with:completionHandler:)

Creates a downloadTask that retrieves the contents of the specified URL, saves the results to a file, and calls a handler upon completion.

Think of downloadTask as downloading files to disk, giving you more flexibility as to how to handle the response.

A completion handler is used to allow the data to be returned from downloadTask:

session = URLSession(configuration: .default, delegate: nil, delegateQueue: nil)
let task = session?.downloadTask(with: url) {[weak self] url, urlresponse, error in
DispatchQueue.main.async {
self?.activityIndicator?.stopAnimating()
}
}
task?.resume()

Rather than using a completionHandler we can use a delegate to receive the data instead. The method of doing this is explained below.

This means that you receive the data using URLSessionDownloadDelegate rather than the closure above. Now this delegate has the method didFinishDownloadingTo which confirms when the download is finished. Note that the location that the file has been downloaded to is accessible from this method — a crucial difference to dataTask above

func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
DispatchQueue.main.async {
self.activityIndicator?.stopAnimating()
}
}

Once again we set up the URLSession

var session : URLSession? = nil

and then set up a URLSession with the delegate set, but of course now we are using downloadTask

session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
let task = session?.downloadTask(with: url)

If you wish to see how to set the delegate as a different file containing the appropriate methods, check out the repo link below.

So which should you use?

So it really depends on the effect you want.

Here is Apple’s explaination:

For small interactions with remote servers, you can use the URLSessionDataTask class to receive response data into memory (as opposed to using the URLSessionDownloadTaskclass, which stores the data directly to the file system). A data task is ideal for uses like calling a web service endpoint.

But better than that, here is my easy to read explaination:

dataTask

  • Not supported in background sessions (as the data is not stored to a file)
  • Intended for short, interactive requests from the server
  • Unable to pause and result

downloadTask

  • Writes the response to a temporary file
  • Supports background downloading (even when the App is not running!)
  • Pausing and resuming possible
  • Additional features (one shown in depth below)

Here is one in depth example of using downloadTask which can be used to track progress:

downloadTask has URLSessionDownloadDelegate which has the function

func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedtoWrite: Int64)

which can then be used to calculate the progress through downloading so long as the delegate is set as below

let urlSession = URLSession(configuration: URLSessionConfiguration.defualt, delegate: self, delegateQueue: nil)
let task = urlSession.downloadTask(with: request)

A full guide on this has been written HERE,

Conclusion:

Looking for a way to download from your endpoint? Usually dataTask has you covered and will help you to do exactly what you want. Need some granular control, or want to download from the background? Start looking at downloadTask for the functions that you need.

That easy? Perhaps, but to help you out I’ve created the full code for your pleasure using the link below.

Extend your knowledge

  • Apple have a document for dataTask(with:completionHandler:) (Link HERE)
  • Apple have a further document for downloadTask(with:completionHandler:) (Link HERE)

The Twitter contact:

Any questions? You can get in touch with me here

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store