Save and Load Downloaded File Locally

On March 29, 2016, in Swift, by James Liu

My Download Manager app is a real project which will manage download tasks. It allow users to start download a file, pause or resume the download task, and stop a download task. In tutorial 1, I already show you all the basic functions, such as starting a download, pause, resume and stop it. However, these basic functions are really not enough. For example, if I pause the download task and quit the app, the app will lose all downloaded data.

The only solution for that is saving the downloaded data into iOS device locally when we pause the download data. So we will not lose the data even we quit the app. If we want to resume the download task, we can load the data from where we store it locally and continue to download the rest of it.

This is the tutorial 2 of iOS download manager app tutorial series. In this tutorial, I will give some example to show how to save downloaded file in iOS locally and load the downloaded file from iOS locally. After you finish this tutorial, you will know how to save and load local file in iOS app. You can also access the whole tutorial series with following links:

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
Tutorial 3 of Download Manager App: Download Multiple Files Simultaneously in iOS

Where Shall We Save Data in iOS App

Different from Android App, each iOS App has its own folder to save data. The path may change basing on the installation. So we can use following code to get the folder path.

let homeFolderPath:String = NSHomeDirectory();

Above code will return application’s home directory. In iOS app, the path of the folder looks like:

/var/mobile/Containers/Data/Application/1F55B608-B498-4E25-A8B7-DA43BDE31366

However, we are not allowed to write data to home folder directly. In above folder, there are two default folders, Documents and tmp. These two folders are the place for us to saving data inside.

Documents folder is the most common folder where we can save data inside. tmp folder is for saving temporary files.

To get Documents in the home directory, we can use following code:

        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);
        }

The above code will return a folder path like this:

/var/mobile/Containers/Data/Application/1F55B608-B498-4E25-A8B7-DA43BDE31366/Documents

Please remember that never access the path by absolute path. As each time we install the app on the iPhone or any other Apple device, the home directory changes all the time. Therefore, when we want to write data into the Documents folder, we must use cocoa API to get the folder path.

To get tmp folder in the home directory, we can use following code:

print(NSTemporaryDirectory());

It’s much simple to get tmp folder than getting Documents folder. I still recommend to save data in Documents folder as Apple system also writes temporary files into tmp folder.

In my Download Manager app, I will create a cache folder in Documents. So I create a function to make it easily to access.

var cacheFolder:NSURL?;
    func getCacheFolderURL() -> NSURL? {
        if(cacheFolder != nil) {
            return cacheFolder;
        }
        
        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]);
            cacheFolder = NSURL.init(fileURLWithPath: folderPath);
            cacheFolder = cacheFolder?.URLByAppendingPathComponent("cache", isDirectory: true);
            return cacheFolder;
        }
        
        return nil;
    }

Save Downloaded Data When Pause Downloading Task

As we mentioned just now, when we pause the downloading task, we need to save downloaded data in device locally. So after we quit the app, we can still resume the download task when we launch the app next time. Let me show you the example source code to save data when pausing the download task.

    @IBAction func pauseDownloadSingleFile(sender: AnyObject) {
        if(downloadTask != nil && isDownload) {
            downloadTask!.cancelByProducingResumeData({ (resumeData) -> Void in
                self.downloadData = NSData.init(data: resumeData!);
                self.writeCacheFile(self.downloadData, fileName: String(self.downloadHTTPSURL.hash));
            })
            
            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);
        }
    }

    func writeCacheFile(cacheData:NSData?, fileName:String) {
        var cacheFileURL:NSURL? = self.getCacheFolderURL();
        cacheFileURL = cacheFileURL?.URLByAppendingPathComponent(fileName, isDirectory: false);
        
        if(cacheFileURL != nil && cacheData != nil) {
            cacheData!.writeToURL(cacheFileURL!, atomically: true);
        }
    }

When user clicks on “pause” button, pauseDownloadSingleFile will be called. Then, I will call cancelByProducingResumeData on downloadTask to store the downloaded data. Actually, resumeData in the callback function is not the real downloaded content. It is just a record data which represents the NSURLSessionDownloadTask status. The real downloaded content is handled by NSURLSessionDownloadTask itself, which is located in tmp folder.

The function writeCacheFile will write the resumeData into a cache folder.

Load Cache File From Local

Just now, I finished saving downloaded content when users click pause button. If users quit the app, all download status are still stored locally. When users launch the app next, the app can read the download status locally and resume to download the rest, instead of downloading from the beginning. Let me show you how to load local files by Swift.

    @IBAction func startDownloadSingleFile(sender: AnyObject) {
        self.downloadData = self.readCacheFile(String(self.downloadHTTPSURL.hash));
        if(self.downloadData != nil) {
            self.pauseDownloadSingleFile(NSObject.init());
        }
        
        if(!isDownload && downloadData == nil) {
            let url:NSURL = NSURL(string: downloadHTTPSURL)!;
            downloadTask = urlSession!.downloadTaskWithURL(url);
            downloadTask!.resume();
            isDownload = true;
            progressLabel.text = "0%";
        }
    }

    func readCacheFile(fileName:String) -> NSData? {
        var cacheFileURL:NSURL? = self.getCacheFolderURL();
        cacheFileURL = cacheFileURL?.URLByAppendingPathComponent(fileName, isDirectory: false);
        if(cacheFileURL != nil) {
            return NSData.init(contentsOfURL: cacheFileURL!);
        }
        
        return nil;
    }

Here, I will try to load local cache files first when users start to download files. If the cache files exist, I will call NSURLSession.downloadTaskWithResumeData to resume a download task with downloaded content. Otherwise, I will start a new download task by calling NSURLSession.downloadTaskWithURL.

Delete Local Files by Swift

Now, we know how to save data to local files and load local files. Sometimes, we also need to delete local files. For example, when the download tasks finish, we shall delete the local cache files which are created when users click pause before. So, here is the example source code to show you how to delete local files by Swift:

    func clearCacheFile(fileName:String) {
        var cacheFileURL:NSURL? = self.getCacheFolderURL();
        cacheFileURL = cacheFileURL?.URLByAppendingPathComponent(fileName, isDirectory: false);
        if(cacheFileURL != nil) {
            let manager = NSFileManager.defaultManager();
            if(manager.fileExistsAtPath(cacheFileURL!.path!)) {
                do {
                    try manager.removeItemAtURL(cacheFileURL!);
                } catch {
                    print(error);
                }
            }
        }
    }

Add Cache Features in Download Manager App

In this version of Download Manager App, I add the cache features so it can store the download status when user clicks the pause button. Even user quits the app, the download status are still stored in the local device. When user launches the app next time and start to download the file again, Download Manager App will try to read the cache file from local device and resume the download task from last pause position. Check the following video to see how the app works.

Get Full Source Code Under $2.99

You can get Download Manager App version 2.0 at only $2.99. After you get the source code, you have full permission to use it, modify it and put it in your own project.

 

1 Response » to “Save and Load Downloaded File Locally”

  1. salamat ali says:

    Hi, i am a student of computer science and i have to do the project in college which downloads and saves a file on iphone i.e simple download manager. I have searched a lot on internet but did not find properly defined/explained method for a student to make a simple download manager. I can pay you $1 for providing me this as i don’t have a card. I will be paying you using my fathers visa debit card as we dont have paypal here.

Leave a Reply

Premium WordPress Themes

WordPress Themes