NSURLSession Tutorial

NSURLSession with Delegates

I have already created 2 tutorials (one for obj-c and one for swift) showing how to use NSURLSession in a very simple case scenario to download an image as NSData, and display it on the screen.

That was fine and dandy, but NSURLSession can do sooo much more than that.
In this post I’ll how to use one of the delegates of NSURLSession to download a large image, while displaying how much data you have already downloaded.
Since the image is larger now (5mb from some random website), the image gets automatically placed in a temporary file, which then gets converted to an image.

Final result

As usual, here’s the final result of what this will do:

gif

Gif created with “GifGrabber” from the Mac App Store (Free)

So, let’s get started.

Declare the delegate

In the header file of your class, declare that you’ll implement the “NSURLSessionDownloadDelegate”

@interface ViewController : UIViewController <NSURLSessionDownloadDelegate>

Setup your outlets, and you’re all done with the header file.

Setup Configuration, Session and Task

Since we’re going to get the data and status back from delegate methods, we’re going to do just some basic setup of the Session object. We’ll do this when the app starts, so the viewDidLoad method would work for this:

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.allowsCellularAccess = NO;

// Other options for configuration, if needed. I dont need them for this test
//    [configuration setHTTPAdditionalHeaders:some dictionary values];
//    configuration.timeoutIntervalForRequest = 30.0;
//    configuration.timeoutIntervalForResource = 60.0;
//    configuration.HTTPMaximumConnectionsPerHost = 1;

NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
delegate:self
delegateQueue:nil];
NSURLSessionDownloadTask *task = [session downloadTaskWithURL:[NSURL URLWithString:URL_LARGE_IMAGE_1]];

[task resume];
self.label_downloadURL.text = [NSString stringWithFormat:@"URL = %@", URL_LARGE_IMAGE_1];
}

In the configuration we can set additional settings or parameters. In this case I’m only using one option, but I have listed a few of the most common ones in case somebody needs them later.

  1. Create and initialize the SessionConfiguration object
  2. Since the image is “large” we’re going to limit this to WIFI only
  3. Create the session object, and declare this class (self) as the delegate
  4. Create the task
  5. Start the task ([task resume])
  6. Set the label with the URL we’re downloading, just for fun

Implement the required delegate method
Since you’re volunteering to be the delegate of NSURLSessionDownloadDelegate, you need to implement (or technically just declare in some cases, but that’s a different story) the required methods.
NSURLSessionDownloadDelegate has only 1 required delegate method URLSession:downloadTask:didFinishDownloadingToURL
I implemented this method like this:

- (void)URLSession: (NSURLSession *)session
downloadTask: (NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL: (NSURL *)location
{
NSData *imageData = [NSData dataWithContentsOfURL:location];
dispatch_async(dispatch_get_main_queue(),

^{
self.label_savedLocation.text = [NSString stringWithFormat:@"%@", location];   // too much text for 1 label
self.imageView.image = [UIImage imageWithData:imageData];
});
}
  1. This method gets called when the image, or whatever you’re downloading, has finished downloading, and it will place the downloaded image into a temporary file in your local system. However it does not return the temp file created, but it returns the location of where the temporary file gets saved (in the form of a URL)
  2. I get the NSData from the temporary file location
  3. This method runs on a different/background thread, so if I want to update the UI I have to do it from the main thread. Because of this I call GCD and ask to do something in the main queue
  4. In the main queue, or main/UI thread, I update the status label with the location of the temp file, and I display the image that I downloaded

So that’s all for this!
However, NSURLSessionDownloadDelegate has several other methods you can use and play with. I decided to use the URLSession:downloadTask:didWriteDatatotalBytesWritten:totalBytesExpectedToWrite because this method can give me an updated status on how much data I’m supposed to download and how much I have downloaded so far. This makes for a nice “90% downloaded” user update.
So let’s use that method.

Implementing the update method

With a super long name, the method  URLSession:downloadTask:didWriteDatatotalBytesWritten:totalBytesExpectedToWrite is where we will be updating the labels with the status of how much data has been downloaded so far.
I implemented this method like this:

- (void)URLSession: (NSURLSession *)session

downloadTask: (NSURLSessionDownloadTask *)downloadTask
didWriteData: (int64_t)bytesWritten
totalBytesWritten: (int64_t)totalBytesWritten
totalBytesExpectedToWrite: (int64_t)totalBytesExpectedToWrite
{
long percentage = (totalBytesWritten * 100) / totalBytesExpectedToWrite;
dispatch_async(dispatch_get_main_queue(),
^{
self.label_bytesExpected.text = [NSString stringWithFormat:@"Expected = %lld", totalBytesExpectedToWrite];
self.label_bytesWritten.text = [NSString stringWithFormat:@"Received = %lld (%ld%%)", totalBytesWritten, percentage];
});
}
  1. There might be better ways to calculate the percentage, but this will do it for now
  2. Again, this method runs in a different/background thread, so since I want to update the main/UI thread with data from this method, I need to get GCD to run it in the main queue
  3. Update the labels

That is all!

Remember, NSURLSession can do a lot more, like handle basic HTTP authentication in a more elegant way than NSURLConnection, so go and play with it.

As usual, the source code can be found here.

This article was orignally posted here.