Docset Viewer: Resuming large downloads with NSURLConnection

As I’ve shown in my previous post announcing Docset Viewer, I want this series of posts to be more than me talking about my new app. In keeping with the instructional nature of my site, I’m going to show you a few things that I did in my new app Docset Viewer and how I put it together. This time around I’m going to show how I use NSURLConnection for downloading large files, and even resuming them.

In Docset Viewer I’ve added the ability to download docsets directly from Atom feeds, either from custom URLs or from a pre-configured list of Apple’s available docsets. Since you may not be consistently connected to the Internet, it’s important to be able to download documentation packages incrementally, especially since they can be anywhere from 300MB to 500MB.

Resuming large downloads with NSURLConnection

Apple’s NSURLConnection class is extremely easy to use, especially when you want to perform a simple network request.  Customizing a request involves a little bit more work, but it needn’t be complicated.  You begin by creating an NSMutableURLRequest object and checking to see if the request can be made.

In order to resume a network download, you need to know where your download left off.  For that we need to check how many bytes we’ve downloaded so far, if any.

The code above checks if the download’s temporary file exists and checks its file length. If the file doesn’t exist, we create it so there’s a file for us to append to. From here now all we have to do is check to see if we need to resume a download, or start the download from scratch.

The above code sets the HTTP-Range header, indicating which byte-range we want to download. By specifying a “XXX-” range, with a trailing dash, this tells the server that we want to download to the end of the file, starting from the indicated byte offset.

There are a few additional goals I had. In addition to downloading the content I wanted to make sure that very little memory was used, so I needed to save the file to disk as it’s downloaded. I also wanted to be able to cancel the download at any point. NSURLConnection has several different modes it can function in, so in this case I did the following:

From this point forward the connection will begin downloading. Your class needs to implement the NSURLConnectionDataDelegate and NSURLConnectionDelegate protocols. The first protocol defines a set of methods that notifies you whenever a buffer of data is received from the connection. The second protocol defines methods that inform you when the network connection starts, when it fails, and when it completes. Lets go through each of these delegate methods one by one.

That’s the easy one, but what we really care about is finding out from the server whether or not it was able to honour the HTTP-Range header so we know if we have to wipe our temporary file clean and start over again.

From what you can see above, we expect an HTTP 206 response code from the server when it tells us it’s able to fulfill the HTTP-Range request. But at this point I don’t want to blindly trust the server, so I inspect the HTTP headers and parse out the byte range it’s sending me to verify where the byte range begins. If all goes well we can seek our file handle to the appropriate byte offset, and we can resume the file from where we left off.

There are undoubtedly some bugs in here where, for example, the server returns the proper range response but perhaps the server malformed the header, meaning we’re storing corrupted content. But in that event simply deleting the file and starting over is probably an acceptable option.

Our next step is storing the data as it’s downloaded.

And finally closing the file when the download is complete.

From this point the rest is yours. You can track your download progress like I do by sending NSNotificationCenter messages when convenient chunks of data are downloaded (I don’t post those messages on every connection:didReceiveData: message so I don’t overload the main thread with notifications), you can pause and resume downloads, and can do even more.

Once you begin working with NSURLConnection you can efficiently transfer as much data as you want, and can even play with multiple-range requests if you want to get really fancy.

Good luck, and if you want to check this out in action (and support my app at the same time), try out Docset Viewer on the iTunes App Store.


About Michael Nachbaur

iOS app developer, livin' the dream. Working from wherever I find myself; Hawaii, Santa Monica, Vancouver, and elsewhere.

One Response to “Docset Viewer: Resuming large downloads with NSURLConnection”

  1. Ed April 2, 2012 8:43 am

    Thank you. This is what I was looking for.