Downloading files is a common task in most of the iOS app. If you are building an ebook reader, or a comic book reader app, downloading ebooks will be a necessary feature. Last time, I am using a news reader app which will download the pdf newspaper from their website. This feature is also built in music player apps to download mp3 file, video player apps to download video file, even in the wallpaper apps to download wallpapers.
As my learning concept, it is always the best way to learning a new technical through a real project. Therefore, I will build a real download manager app from the ground up. This is the tutorial 1 of Download Manager iOS App. You can also access the whole tutorial sessions by following link:
Tutorial 1 of Download Manager App: Start, Pause, Resume and Stop Download in iOS
Tutorial 2 of Download Manager App: Save and Load Downloaded Data Locally in iOS
To implement a full download manager, it must be able to start a download task, pause a download task and resume the download task. In this tutorial, I will implement a download manager which has all these functions. I will try my best to make this tutorial easy to understand so it could benefits all people.
By the way, as Apple recommends their new language Swift, I will build all my iOS project with Swift. For people who were using objective-c, I think it’s time to change your habit. I understand the pain for people who have very long experience on objective-c. But there is no choice, Apple want us to use Swift.
Download Files in iOS By Swift
In my plan, I will start this tutorial with a simple app first. In this app, I will put three buttons on the stage, start button, pause button and cancel button. When users click on the start downloading button, the app will start to download a file from internet. User also can pause the downloading task or cancel the downloading task at any time. When the downloading task is over, the app will notice the user with an alter view. Additionally, the app will also show a progress bar to show the download progress when downloading task begins. Now, let’s build the simple UI on the xib. Check xib vs storyboard for the reason why I am using xib instead of storyboard.
Launch Customized xib as Default App Screen
Now Apple recommends Storyboard to build App UI. If we want to launch xib as iOS app first screen UI, we need to explicitly tell application to load the xib.
[java]
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
let screenBounds:CGRect = UIScreen.mainScreen().bounds;
self.window = UIWindow.init(frame: screenBounds);
self.window?.autoresizesSubviews = true;
self.viewController = DownloadViewController();
self.window?.rootViewController = self.viewController;
self.window?.makeKeyAndVisible();
return true;
}
[/java]
Download File By NSURLSession in Swift
There are lots of Objective-C downloading file example on internet, but it’s very hard to find complete Swift downloading file examples. In this tutorial, I will write an example in Swift to demonstrate downloading file by NSURLSession.
First, I am using following piece of code to start to download a file from website.
[javascript]
@IBAction func startDownloadSingleFile(sender: AnyObject) {
var urlSession:NSURLSession!;
var urlConfiguration:NSURLSessionConfiguration!;
var downloadTask:NSURLSessionDownloadTask!;
var downloadData:NSData!;
urlConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration();
let queue:NSOperationQueue = NSOperationQueue.mainQueue();
urlSession = NSURLSession.init(configuration: urlConfiguration, delegate: self, delegateQueue: queue);
let url:NSURL = NSURL(string: downloalURL)!;
downloadTask = urlSession.downloadTaskWithURL(url);
downloadTask.resume();
}
[/javascript]
Then, we start to implement several delegate functions to accept the SSL handshake, watch the download progress, and handle the file after downloading is successful.
[javascript]
func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
let credential = NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!);
completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, credential);
}
[/javascript]
The above function will be called once we try to download file from HTTPS hosting. This is a very simple example. For more sophisticated implementation, we can add more logic like compare host name or create username and password credential for https connection.
For downloading files from normal http hosting, we don’t need to implement above function. Now, let us see how to observe the downloading progress.
[javascript]
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite);
print(progress);
self.progressBar.setProgress(progress, animated: false);
}
[/javascript]
Here we update the progress bar in main UI thread. Actually, in real use case, we prefer to update the progress bar in a separate thread. To do that, we can replace this code:
[javascript]
self.progressBar.setProgress(progress, animated: false);
[/javascript]
with this code:
[javascript]
NSOperationQueue.mainQueue().addOperationWithBlock({
self.progressBar.setProgress(progress, animated: false);
});
[/javascript]
When a file is downloaded successfully, we can read the content directly or save the file to app data folder. So we implement following function:
[javascript]
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
do {
//read file data
/*
let fileHandle:NSFileHandle = try NSFileHandle(forReadingFromURL: location);
var fileData:NSData? = nil;
fileData = fileHandle.readDataToEndOfFile();
print(fileData);
*/
//or save file
let fileManager:NSFileManager = NSFileManager.defaultManager();
let nsDocumentDirectory:NSSearchPathDirectory = NSSearchPathDirectory.DocumentDirectory
let nsUserDomainMask:NSSearchPathDomainMask = NSSearchPathDomainMask.UserDomainMask;
let paths:[String] = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true);
if (paths.count > 0) {
let folderPath:String = String(paths[0]);
print(folderPath);
let filePath:String = folderPath + “/download_file.data”;
let fileURL:NSURL = NSURL.init(fileURLWithPath: filePath);
try fileManager.moveItemAtURL(location, toURL: fileURL);
}
} catch {
print(error);
}
downloadData = nil;
}
[/javascript]
In above example, I save the downloaded file into folder with a file name “download_file.data”. I also give the example code to read all data from the downloaded file, but comment it.
Pause Download File
Just now I give the example to start download a file and store the downloaded file in app document folder. I think it’s time to show how to pause a downloading task. Actually, it is very simple. But we must remember to store the data in the memory or local file after we pause the download, so that we can resume the download task later.
[javascript]
@IBAction func pauseDownloadSingleFile(sender: AnyObject) {
if(downloadTask != nil && isDownload) {
downloadTask!.cancelByProducingResumeData({ (resumeData) -> Void in
self.downloadData = NSData.init(data: resumeData!);
})
isDownload = false;
downloadTask = nil;
pauseBtn.setTitle(“Resume”, forState: UIControlState.Normal);
}
if(!isDownload && downloadData != nil) {
downloadTask = urlSession?.downloadTaskWithResumeData(downloadData!);
downloadTask!.resume();
isDownload = true;
downloadData = nil;
pauseBtn.setTitle(“Pause”, forState: UIControlState.Normal);
}
}
[/javascript]
In this example app, I put a pause button on the stage. Actually, it is a trigger button. It will pause the download task if download is running. Otherwise, it will resume a paused download task. downloadData is a class variable with NSData type. When user click the pause button, I will save downloaded data in to downloadData variable. When I resume the download task, I will pass downloaded data to session to initialize the download task.
Stop Download File
In the last step, I will show how to stop a running download task.
[javascript]
@IBAction func stopDownloadSingleFile(sender: AnyObject) {
downloadTask?.cancel();
downloadData = nil;
isDownload = false;
downloadTask = nil;
progressBar.progress = 0;
progressLabel.text = “0%”;
pauseBtn.setTitle(“Pause”, forState: UIControlState.Normal);
}
[/javascript]
Download Manager App 1.0
In this version of Download Manager App, we integrate all download functions in version 1.0. You can check following video to check how the app works:
Get Full Source Code Under $1.99
You can get the whole source code at only $1.99. After you get the source code, you have full permission to use it, modify it and put it in your own project.