73

I just started learning apple swift programming for iOS coming from android. I basically can now read and manipulate swift code and also learned some common classes used in iOS swift programming but still having some confusion with the syntax and everything.

I'm trying to download file. Like, lets just say coming from this URL

var url = "http://www.mywebsite.com/myfile.pdf"

in a button click. Maybe with visual progress too

Through searching here in stackoverflow, I stumbled upon Alamofire. I might try it but I'm not sure if this is the best way for me to do it.

So, I would like to ask how and what are my options (iOS7 and iOS8) in achieving my goal. Also, pros and cons would be awesome!

Petter Friberg
  • 21,252
  • 9
  • 60
  • 109
r_19
  • 1,858
  • 1
  • 20
  • 16

16 Answers16

85

Example downloader class without Alamofire:

class Downloader {
    class func load(URL: NSURL) {
        let sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration()
        let session = NSURLSession(configuration: sessionConfig, delegate: nil, delegateQueue: nil)
        let request = NSMutableURLRequest(URL: URL)
        request.HTTPMethod = "GET"
        let task = session.dataTaskWithRequest(request, completionHandler: { (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void in
            if (error == nil) {
                // Success
                let statusCode = (response as NSHTTPURLResponse).statusCode
                println("Success: \(statusCode)")

                // This is your file-variable:
                // data
            }
            else {
                // Failure
                println("Failure: %@", error.localizedDescription);
            }
        })
        task.resume()
    }
}

This is how to use it in your own code:

class Foo {
    func bar() {
        if var URL = NSURL(string: "http://www.mywebsite.com/myfile.pdf") {
            Downloader.load(URL)
        }
    }
}

Swift 3 Version

Also note to download large files on disk instead instead in memory. see `downloadTask:

class Downloader {
    class func load(url: URL, to localUrl: URL, completion: @escaping () -> ()) {
        let sessionConfig = URLSessionConfiguration.default
        let session = URLSession(configuration: sessionConfig)
        let request = try! URLRequest(url: url, method: .get)

        let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in
            if let tempLocalUrl = tempLocalUrl, error == nil {
                // Success
                if let statusCode = (response as? HTTPURLResponse)?.statusCode {
                    print("Success: \(statusCode)")
                }

                do {
                    try FileManager.default.copyItem(at: tempLocalUrl, to: localUrl)
                    completion()
                } catch (let writeError) {
                    print("error writing file \(localUrl) : \(writeError)")
                }

            } else {
                print("Failure: %@", error?.localizedDescription);
            }
        }
        task.resume()
    }
}
cornr
  • 653
  • 4
  • 20
Devran Cosmo Uenal
  • 6,055
  • 2
  • 26
  • 29
  • Hi Devran! I tried your code and it is showing 'Success: 200'. I have some few other questions regarding with the code: 1. Does 'Success: 200' means the file is downloaded or that the URL is valid? 2. Is there a way to show a download progress in the notification bar? 3. Where is the file downloaded in the device (just using the emulator)? I need to check if the file is present. Thank you for time some time answering my issue. I will check out NSData as you recommended as well – r_19 Jan 30 '15 at 22:35
  • 2
    1. Technically it means both. Success 200 stands for: the request to that url was accepted and could be answered from the server without errors. (200 is the http status code for OK) [see: List of HTTP codes on Wikipedia](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) – Devran Cosmo Uenal Jan 31 '15 at 01:20
  • 2
    2. For things like that, I would recommend using Alamofire. On the github page of Alamofire, there's an example how to download a file and return the current progress. it also helps you to write less code with the same results :) [see: Downloading a File w/Progress](https://github.com/Alamofire/Alamofire#downloading-a-file-wprogress). – Devran Cosmo Uenal Jan 31 '15 at 01:21
  • 4
    3. Where is the file downloaded in the device (just using the emulator)? I need to check if the file is present. The file is you've downloaded is accessible via code. it's up to you to save it to disk or process the contents of your file in memory. the variable `data` in my example code contains the whole pdf in binary. to return the contents of `data`, just write `println(data)` To learn how to save files on ios, just take a look at: [https://github.com/sketchytech/FileSave](https://github.com/sketchytech/FileSave) (there's also a swift version) – Devran Cosmo Uenal Jan 31 '15 at 01:22
  • Thanks Devran! This is really very helpful! Appreciate it! Is there any chance you know a good PDF reader library in swift or how to display pdf in ios? I need to implement a pdf reader on my app so I can implement banner ads in the pdf reader :) – r_19 Feb 01 '15 at 13:38
  • In that case, you could display your pdf in a WebView :) btw. if i could answer your questions, please accept my answer. [see](http://stackoverflow.com/help/someone-answers) thanks. – Devran Cosmo Uenal Feb 01 '15 at 19:54
  • @DevranCosmoUenal, now that we have the binary data of pdf file in the memory, it there any way to load the data in the browser and render it as PDF, without saving the file to disk? – Daniel Qiu Mar 25 '15 at 19:42
  • @DevranCosmoUenal : I was looking in to this answer and wanted to ask if you would know on how to add headers while using alamofire download function. – Sashi Apr 21 '15 at 18:08
  • @DanielQiu this should help: http://stackoverflow.com/questions/16806739/how-to-generate-pdf-using-nsdata-or-using-data-bytes-objective-c – Devran Cosmo Uenal Apr 22 '15 at 07:11
  • @Sashi sorry, no idea :( – Devran Cosmo Uenal Apr 22 '15 at 07:13
  • @DevranCosmoUenal How could I use your swift3 code? How could i call that method? Sorry, I am so new at iOS! – los.adrian Dec 03 '16 at 09:17
  • @DevranCosmoUenal Hello Sir , I want to add Password Protected after download file, Please guide me. – Shree Prakash Jan 04 '17 at 06:09
  • @DevranCosmoUenal I'm getting error in `.get` method. I need to add any library? – Kavin Kumar Arumugam May 11 '17 at 12:15
  • No library is required -- but the Code might be now incompatible with the most current version of Swift. – Devran Cosmo Uenal May 12 '17 at 07:39
  • 1
    And what about the url and localUrl ? – Pintu Rajput Jul 05 '17 at 06:34
  • Hey How to use localUrl how to write it? – Abdelrahman Mohamed Jul 27 '18 at 16:21
  • HI, This won't work for background downloading(Home btn, locked case) can you provide solution to work with background data downloading in swift? – iOSDude Sep 11 '19 at 07:14
  • What is localURL? The filename I choose? –  Aug 03 '20 at 18:58
54

Swift 4 and Swift 5 Version if Anyone still needs this

import Foundation

class FileDownloader {

    static func loadFileSync(url: URL, completion: @escaping (String?, Error?) -> Void)
    {
        let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!

        let destinationUrl = documentsUrl.appendingPathComponent(url.lastPathComponent)

        if FileManager().fileExists(atPath: destinationUrl.path)
        {
            print("File already exists [\(destinationUrl.path)]")
            completion(destinationUrl.path, nil)
        }
        else if let dataFromURL = NSData(contentsOf: url)
        {
            if dataFromURL.write(to: destinationUrl, atomically: true)
            {
                print("file saved [\(destinationUrl.path)]")
                completion(destinationUrl.path, nil)
            }
            else
            {
                print("error saving file")
                let error = NSError(domain:"Error saving file", code:1001, userInfo:nil)
                completion(destinationUrl.path, error)
            }
        }
        else
        {
            let error = NSError(domain:"Error downloading file", code:1002, userInfo:nil)
            completion(destinationUrl.path, error)
        }
    }

    static func loadFileAsync(url: URL, completion: @escaping (String?, Error?) -> Void)
    {
        let documentsUrl =  FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!

        let destinationUrl = documentsUrl.appendingPathComponent(url.lastPathComponent)

        if FileManager().fileExists(atPath: destinationUrl.path)
        {
            print("File already exists [\(destinationUrl.path)]")
            completion(destinationUrl.path, nil)
        }
        else
        {
            let session = URLSession(configuration: URLSessionConfiguration.default, delegate: nil, delegateQueue: nil)
            var request = URLRequest(url: url)
            request.httpMethod = "GET"
            let task = session.dataTask(with: request, completionHandler:
            {
                data, response, error in
                if error == nil
                {
                    if let response = response as? HTTPURLResponse
                    {
                        if response.statusCode == 200
                        {
                            if let data = data
                            {
                                if let _ = try? data.write(to: destinationUrl, options: Data.WritingOptions.atomic)
                                {
                                    completion(destinationUrl.path, error)
                                }
                                else
                                {
                                    completion(destinationUrl.path, error)
                                }
                            }
                            else
                            {
                                completion(destinationUrl.path, error)
                            }
                        }
                    }
                }
                else
                {
                    completion(destinationUrl.path, error)
                }
            })
            task.resume()
        }
    }
}

Here is how to call this method :-

let url = URL(string: "http://www.filedownloader.com/mydemofile.pdf")
FileDownloader.loadFileAsync(url: url!) { (path, error) in
    print("PDF File downloaded to : \(path!)")
}
Bijender Singh Shekhawat
  • 3,934
  • 2
  • 30
  • 36
Mantu
  • 1,017
  • 1
  • 10
  • 21
  • 3
    @BijenderSinghShekhawat How to get the percentage of download in swift5 – Davender Verma Jan 30 '20 at 09:56
  • Yes is there a way to get this working with the percentage. I tried adding the URLSessionDelegate and adding the urlSession function to calculate the percentage but it doesn't seem to work with this function. – Nick May 05 '21 at 17:32
24

Here's an example that shows how to do sync & async.

import Foundation

class HttpDownloader {

    class func loadFileSync(url: NSURL, completion:(path:String, error:NSError!) -> Void) {
        let documentsUrl =  NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first as! NSURL
        let destinationUrl = documentsUrl.URLByAppendingPathComponent(url.lastPathComponent!)
        if NSFileManager().fileExistsAtPath(destinationUrl.path!) {
            println("file already exists [\(destinationUrl.path!)]")
            completion(path: destinationUrl.path!, error:nil)
        } else if let dataFromURL = NSData(contentsOfURL: url){
            if dataFromURL.writeToURL(destinationUrl, atomically: true) {
                println("file saved [\(destinationUrl.path!)]")
                completion(path: destinationUrl.path!, error:nil)
            } else {
                println("error saving file")
                let error = NSError(domain:"Error saving file", code:1001, userInfo:nil)
                completion(path: destinationUrl.path!, error:error)
            }
        } else {
            let error = NSError(domain:"Error downloading file", code:1002, userInfo:nil)
            completion(path: destinationUrl.path!, error:error)
        }
    }

    class func loadFileAsync(url: NSURL, completion:(path:String, error:NSError!) -> Void) {
        let documentsUrl =  NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first as! NSURL
        let destinationUrl = documentsUrl.URLByAppendingPathComponent(url.lastPathComponent!)
        if NSFileManager().fileExistsAtPath(destinationUrl.path!) {
            println("file already exists [\(destinationUrl.path!)]")
            completion(path: destinationUrl.path!, error:nil)
        } else {
            let sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration()
            let session = NSURLSession(configuration: sessionConfig, delegate: nil, delegateQueue: nil)
            let request = NSMutableURLRequest(URL: url)
            request.HTTPMethod = "GET"
            let task = session.dataTaskWithRequest(request, completionHandler: { (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void in
                if (error == nil) {
                    if let response = response as? NSHTTPURLResponse {
                        println("response=\(response)")
                        if response.statusCode == 200 {
                            if data.writeToURL(destinationUrl, atomically: true) {
                                println("file saved [\(destinationUrl.path!)]")
                                completion(path: destinationUrl.path!, error:error)
                            } else {
                                println("error saving file")
                                let error = NSError(domain:"Error saving file", code:1001, userInfo:nil)
                                completion(path: destinationUrl.path!, error:error)
                            }
                        }
                    }
                }
                else {
                    println("Failure: \(error.localizedDescription)");
                    completion(path: destinationUrl.path!, error:error)
                }
            })
            task.resume()
        }
    }
}

Here's how to use it in your code:

let url = NSURL(string: "http://www.mywebsite.com/myfile.pdf") 
HttpDownloader.loadFileAsync(url, completion:{(path:String, error:NSError!) in
                println("pdf downloaded to: \(path)")
            })
djunod
  • 4,876
  • 2
  • 34
  • 28
  • Any help with that? http://stackoverflow.com/questions/36762491/downloading-pdf-using-nsurlsession-http-post-with-parameters – Thiha Aung Apr 21 '16 at 07:59
22

If you need to download only text file into String you can use this simple way, Swift 5:

let list = try? String(contentsOf: URL(string: "https://example.com/file.txt")!)

In case you want non optional result or error handling:

do {
    let list = try String(contentsOf: URL(string: "https://example.com/file.txt")!)
}
catch {
    // Handle error here
}

You should know that network operations may take some time, to prevent it from running in main thread and locking your UI, you may want to execute the code asynchronously, for example:

DispatchQueue.global().async {
    let list = try? String(contentsOf: URL(string: "https://example.com/file.txt")!)
}
Dmitriy
  • 1,898
  • 1
  • 15
  • 24
19

Devran's and djunod's solutions are working as long as your application is in the foreground. If you switch to another application during the download, it fails. My file sizes are around 10 MB and it takes sometime to download. So I need my download function works even when the app goes into background.

Please note that I switched ON the "Background Modes / Background Fetch" at "Capabilities".

Since completionhandler was not supported the solution is not encapsulated. Sorry about that.

--Swift 2.3--

import Foundation 
class Downloader : NSObject, NSURLSessionDownloadDelegate
{
    var url : NSURL? 
    // will be used to do whatever is needed once download is complete
    var yourOwnObject : NSObject?

    init(yourOwnObject : NSObject)
    {
        self.yourOwnObject = yourOwnObject
    }

    //is called once the download is complete
    func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL)
    {
        //copy downloaded data to your documents directory with same names as source file
        let documentsUrl =  NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first
        let destinationUrl = documentsUrl!.URLByAppendingPathComponent(url!.lastPathComponent!)
        let dataFromURL = NSData(contentsOfURL: location)
        dataFromURL?.writeToURL(destinationUrl, atomically: true)

        //now it is time to do what is needed to be done after the download
        yourOwnObject!.callWhatIsNeeded()
    }

    //this is to track progress
    func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64)
    {
    }

    // if there is an error during download this will be called
    func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?)
    {
        if(error != nil)
        {
            //handle the error
            print("Download completed with error: \(error!.localizedDescription)");
        }
    }

    //method to be called to download
    func download(url: NSURL)
    {
        self.url = url

        //download identifier can be customized. I used the "ulr.absoluteString"
        let sessionConfig = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(url.absoluteString)
        let session = NSURLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil)
        let task = session.downloadTaskWithURL(url)
        task.resume()
    }
}

And here is how to call in --Swift 2.3--

    let url = NSURL(string: "http://company.com/file.txt")
    Downloader(yourOwnObject).download(url!)

--Swift 3--

class Downloader : NSObject, URLSessionDownloadDelegate {

var url : URL?
// will be used to do whatever is needed once download is complete
var yourOwnObject : NSObject?

init(_ yourOwnObject : NSObject)
{
    self.yourOwnObject = yourOwnObject
}

//is called once the download is complete
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL)
{
    //copy downloaded data to your documents directory with same names as source file
    let documentsUrl =  FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
    let destinationUrl = documentsUrl!.appendingPathComponent(url!.lastPathComponent)
    let dataFromURL = NSData(contentsOf: location)
    dataFromURL?.write(to: destinationUrl, atomically: true)

    //now it is time to do what is needed to be done after the download
    yourOwnObject!.callWhatIsNeeded()
}

//this is to track progress
private func URLSession(session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64)
{
}

// if there is an error during download this will be called
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)
{
    if(error != nil)
    {
        //handle the error
        print("Download completed with error: \(error!.localizedDescription)");
    }
}

//method to be called to download
func download(url: URL)
{
    self.url = url

    //download identifier can be customized. I used the "ulr.absoluteString"
    let sessionConfig = URLSessionConfiguration.background(withIdentifier: url.absoluteString)
    let session = Foundation.URLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil)
    let task = session.downloadTask(with: url)
    task.resume()
}}

And here is how to call in --Swift 3--

    let url = URL(string: "http://company.com/file.txt")
    Downloader(yourOwnObject).download(url!)
Ahmet Akkök
  • 466
  • 1
  • 5
  • 13
12

Here is the Swift 4 version:

static func loadFileAsync(url: URL, completion: @escaping (String?, Error?) -> Void)
{
    let documentsUrl =  FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!

    let destinationUrl = documentsUrl.appendingPathComponent(url.lastPathComponent)

    if FileManager().fileExists(atPath: destinationUrl.path)
    {
        completion(destinationUrl.path, nil)
    }
    else
    {
        let session = URLSession(configuration: URLSessionConfiguration.default, delegate: nil, delegateQueue: nil)
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        let task = session.dataTask(with: request, completionHandler:
        {
            data, response, error in
            if error == nil
            {
                if let response = response as? HTTPURLResponse
                {
                    if response.statusCode == 200
                    {
                        if let data = data
                        {
                            if let _ = try? data.write(to: destinationUrl, options: Data.WritingOptions.atomic)
                            {
                                completion(destinationUrl.path, error)
                            }
                            else
                            {
                                completion(destinationUrl.path, error)
                            }
                        }
                        else
                        {
                            completion(destinationUrl.path, error)
                        }
                    }
                }
            }
            else
            {
                completion(destinationUrl.path, error)
            }
        })
        task.resume()
    }
}
inexcitus
  • 2,471
  • 2
  • 26
  • 41
10

Swift 3

you want to download file bite by bite and show in progress view so you want to try this code

import UIKit

class ViewController: UIViewController,URLSessionDownloadDelegate,UIDocumentInteractionControllerDelegate {

    @IBOutlet weak var img: UIImageView!
    @IBOutlet weak var btndown: UIButton!
    var urlLink: URL!
    var defaultSession: URLSession!
    var downloadTask: URLSessionDownloadTask!
    //var backgroundSession: URLSession!
    @IBOutlet weak var progress: UIProgressView!
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view, typically from a nib.

        let backgroundSessionConfiguration = URLSessionConfiguration.background(withIdentifier: "backgroundSession")
        defaultSession = Foundation.URLSession(configuration: backgroundSessionConfiguration, delegate: self, delegateQueue: OperationQueue.main)
        progress.setProgress(0.0, animated: false)
    }

    func startDownloading () {
        let url = URL(string: "http://publications.gbdirect.co.uk/c_book/thecbook.pdf")!
        downloadTask = defaultSession.downloadTask(with: url)
        downloadTask.resume()
    }

    @IBAction func btndown(_ sender: UIButton) {

        startDownloading()

    }

    func showFileWithPath(path: String){
        let isFileFound:Bool? = FileManager.default.fileExists(atPath: path)
        if isFileFound == true{
            let viewer = UIDocumentInteractionController(url: URL(fileURLWithPath: path))
            viewer.delegate = self
            viewer.presentPreview(animated: true)
        }

    }


    // MARK:- URLSessionDownloadDelegate
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {

        print(downloadTask)
        print("File download succesfully")

        let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
        let documentDirectoryPath:String = path[0]
        let fileManager = FileManager()
        let destinationURLForFile = URL(fileURLWithPath: documentDirectoryPath.appendingFormat("/file.pdf"))

        if fileManager.fileExists(atPath: destinationURLForFile.path){
            showFileWithPath(path: destinationURLForFile.path)
            print(destinationURLForFile.path)
        }
        else{
            do {
                try fileManager.moveItem(at: location, to: destinationURLForFile)
                // show file
                showFileWithPath(path: destinationURLForFile.path)
            }catch{
                print("An error occurred while moving file to destination url")
            }
        }



    }

    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
        progress.setProgress(Float(totalBytesWritten)/Float(totalBytesExpectedToWrite), animated: true)
    }

    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        downloadTask = nil
        progress.setProgress(0.0, animated: true)
        if (error != nil) {
            print("didCompleteWithError \(error?.localizedDescription ?? "no value")")
        }
        else {
            print("The task finished successfully")
        }
    }

    func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController
    {
        return self
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

use of this code you want to download file store automatically in Document Directory in your application

this code 100% Working

Amul4608
  • 1,390
  • 14
  • 30
8

Yes you can very easily downloads Files from the remote Url Using this code. This Code is working Fine for Me.

func DownlondFromUrl(){
   // Create destination URL 
let documentsUrl:URL =  FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL!
let destinationFileUrl = documentsUrl.appendingPathComponent("downloadedFile.jpg")

//Create URL to the source file you want to download
let fileURL = URL(string: "https://s3.amazonaws.com/learn-swift/IMG_0001.JPG")

let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig)

let request = URLRequest(url:fileURL!)

let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in
    if let tempLocalUrl = tempLocalUrl, error == nil {
        // Success
        if let statusCode = (response as? HTTPURLResponse)?.statusCode {
            print("Successfully downloaded. Status code: \(statusCode)")
        }

        do {
            try FileManager.default.copyItem(at: tempLocalUrl, to: destinationFileUrl)
        } catch (let writeError) {
            print("Error creating a file \(destinationFileUrl) : \(writeError)")
        }

    } else {
        print("Error took place while downloading a file. Error description: %@", error?.localizedDescription);
    }
}
task.resume()
}
Ayush Dixit
  • 467
  • 4
  • 10
5

Downloading a file in Swift 5 with progress reporting, encapsulated into a copy-paste friendly protocol-accessed class:

protocol FileDownloadingDelegate: class {
    func updateDownloadProgressWith(progress: Float)
    func downloadFinished(localFilePath: URL)
    func downloadFailed(withError error: Error)
}

class FilesDownloader: NSObject, URLSessionDownloadDelegate {
    private weak var delegate: FileDownloadingDelegate?

    func download(from url: URL, delegate: FileDownloadingDelegate) {
        self.delegate = delegate
        let sessionConfig = URLSessionConfiguration.background(withIdentifier: url.absoluteString) // use this identifier to resume download after app restart
        let session = Foundation.URLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil)
        let task = session.downloadTask(with: url)
        task.resume()
    }

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

    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
        DispatchQueue.main.async { self.delegate?.updateDownloadProgressWith(progress: Float(totalBytesWritten)/Float(totalBytesExpectedToWrite)) }
    }

    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        guard let theError = error else { assertionFailure("something weird happened here"); return }
        DispatchQueue.main.async { self.delegate?.downloadFailed(withError: theError) }
    }

}

How to use it:

class MyViewController: UIViewController {

    private lazy var downloader = FilesDownloader()

    func downloadFile(from url: URL) {
        downloader.download(from: url, delegate: self)
    }

}

extension MyViewController: FileDownloadingDelegate {
    func updateDownloadProgressWith(progress: Float) {
        // self.downloadProgressView.setProgress(progress, animated: true)
    }

    func downloadFinished(tempFilePath: URL) {
        print("downloaded to \(tempFilePath)")
        // resave the file into your desired place using 
        // let dataFromURL = NSData(contentsOf: location)
        // dataFromURL?.write(to: yourDesiredFileUrl, atomically: true)
    }

    func downloadFailed(withError error: Error) {
        // handle the error
    }
}
Vitalii
  • 4,267
  • 1
  • 40
  • 45
4

iOS 13 Swift 5, 5.1

@IBAction func btnDownload(_ sender: Any) {
    
    // file location to save download file
    let destination: DownloadRequest.DownloadFileDestination = { _, _ in
        var documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        documentsURL.appendPathComponent(statementPDF)
        return (documentsURL, [.removePreviousFile])
    }
    
    // Alamofire to download file
    Alamofire.download("http://pdf_url", to: destination).responseData { response in
        hideLoader()
        switch response.result {
        case .success:
            // write something here
            if response.destinationURL != nil {
                showAlertMessage(titleStr: APPNAME, messageStr: "File Saved in Documents!")
                
            }
        case .failure:
            showAlertMessage(titleStr: APPNAME, messageStr: response.result.error.debugDescription)
        }
    }
}

Please add these permissions to info.plist, so you will able to check the download file in Document Directory

<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
Sandeep Maurya
  • 1,979
  • 17
  • 22
4

After trying a few of the above suggestions without success (Swift versions...) I ended up using the official documentation: https://developer.apple.com/documentation/foundation/url_loading_system/downloading_files_from_websites

let downloadTask = URLSession.shared.downloadTask(with: url) {
    urlOrNil, responseOrNil, errorOrNil in
    // check for and handle errors:
    // * errorOrNil should be nil
    // * responseOrNil should be an HTTPURLResponse with statusCode in 200..<299
    
    guard let fileURL = urlOrNil else { return }
    do {
        let documentsURL = try
            FileManager.default.url(for: .documentDirectory,
                                    in: .userDomainMask,
                                    appropriateFor: nil,
                                    create: false)
        let savedURL = documentsURL.appendingPathComponent(fileURL.lastPathComponent)
        try FileManager.default.moveItem(at: fileURL, to: savedURL)
    } catch {
        print ("file error: \(error)")
    }
}
downloadTask.resume()
TomTasche
  • 5,448
  • 7
  • 41
  • 67
2

Try This Code only Swift 3.0 First Create NS Object File Copy this code in created File

import UIKit

class Downloader : NSObject, URLSessionDownloadDelegate {

    var url : URL?
    // will be used to do whatever is needed once download is complete
    var obj1 : NSObject?

    init(_ obj1 : NSObject)
    {
        self.obj1 = obj1
    }

    //is called once the download is complete
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL)
    {
        //copy downloaded data to your documents directory with same names as source file
        let documentsUrl =  FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
        let destinationUrl = documentsUrl!.appendingPathComponent(url!.lastPathComponent)
        let dataFromURL = NSData(contentsOf: location)
        dataFromURL?.write(to: destinationUrl, atomically: true)

        //now it is time to do what is needed to be done after the download
        //obj1!.callWhatIsNeeded()
    }

    //this is to track progress
    private func URLSession(session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64)
    {
    }

    // if there is an error during download this will be called
    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)
    {
        if(error != nil)
        {
            //handle the error
            print("Download completed with error: \(error!.localizedDescription)");
        }
    }

    //method to be called to download
    func download(url: URL)
    {
        self.url = url

        //download identifier can be customized. I used the "ulr.absoluteString"
        let sessionConfig = URLSessionConfiguration.background(withIdentifier: url.absoluteString)
        let session = Foundation.URLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil)
        let task = session.downloadTask(with: url)
        task.resume()
    }}

then Copy Below Code And put code in place of you want to download file

 object = "http://www.mywebsite.com/myfile.pdf"
       let url1 = URL(string: object!)
        Downloader(url1! as NSObject).download(url: url1!)
Amul4608
  • 1,390
  • 14
  • 30
0

Swift 3

class ViewController: UIViewController {
    var urlLink: URL!
    var defaultSession: URLSession!
    var downloadTask: URLSessionDownloadTask!
}

// MARK: Button Pressed
    @IBAction func btnDownloadPressed(_ sender: UIButton) {
        let urlLink1 = URL.init(string: "https://github.com/VivekVithlani/QRCodeReader/archive/master.zip")
        startDownloading(url: urlLink!)
}
    @IBAction func btnResumePressed(_ sender: UIButton) {
    downloadTask.resume()
}

@IBAction func btnStopPressed(_ sender: UIButton) {
    downloadTask.cancel()
}

@IBAction func btnPausePressed(_ sender: UIButton) {
    downloadTask.suspend()
}

    func startDownloading (url:URL) {
        let backgroundSessionConfiguration = URLSessionConfiguration.background(withIdentifier: "backgroundSession")
        defaultSession = Foundation.URLSession(configuration: backgroundSessionConfiguration, delegate: self, delegateQueue: OperationQueue.main)
        downloadProgress.setProgress(0.0, animated: false)
        downloadTask = defaultSession.downloadTask(with: urlLink)
        downloadTask.resume()
    }

// MARK:- URLSessionDownloadDelegate
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
    print("File download succesfully")
}

func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
    downloadProgress.setProgress(Float(totalBytesWritten)/Float(totalBytesExpectedToWrite), animated: true)
}

func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
    downloadTask = nil
    downloadProgress.setProgress(0.0, animated: true)
    if (error != nil) {
        print("didCompleteWithError \(error?.localizedDescription)")
    }
    else {
        print("The task finished successfully")
    }
}
Community
  • 1
  • 1
Vivek
  • 4,916
  • 35
  • 40
0

You can also use a third party library that makes life easy, like Just

Just.get("http://www.mywebsite.com/myfile.pdf")

More awesome Swift stuff here https://github.com/matteocrippa/awesome-swift

bachr
  • 5,780
  • 12
  • 57
  • 92
0

Background session

Background session (URLSessionDownloadTask, URLSessionUploadTask) is a part of background transfer approach which allows you to download/upload/cache files in background mode even if app was terminated. In this case when task is done iOS wakes up it (in background) and allows make some competition block in a limited time frame. It works because the downloading task is executed on nsurlsessiond daemon process[About]

[Background tasks]

yoAlex5
  • 29,217
  • 8
  • 193
  • 205
-1

Use URLSessionDownloadTask to download files in background so that they can completed even if the app is terminated.

For more information see:

https://www.ralfebert.de/snippets/ios/urlsession-background-downloads/

It also shows how to implement progress monitoring for multiple tasks running in parallel:

Petter Friberg
  • 21,252
  • 9
  • 60
  • 109
AzSh
  • 135
  • 2
  • 10