How to download file in swift?
Solution 1
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()
}
}
Solution 2
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!)")
}
Solution 3
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)")
})
Solution 4
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")!)
}
Solution 5
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!)
r_19
Updated on July 05, 2022Comments
-
r_19 almost 2 years
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!
-
r_19 about 9 yearsHi 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
-
Devran Cosmo Uenal about 9 years1. 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
-
Devran Cosmo Uenal about 9 years2. 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.
-
Devran Cosmo Uenal about 9 years3. 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 ofdata
, just writeprintln(data)
To learn how to save files on ios, just take a look at: https://github.com/sketchytech/FileSave (there's also a swift version) -
r_19 about 9 yearsThanks 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 :)
-
Devran Cosmo Uenal about 9 yearsIn that case, you could display your pdf in a WebView :) btw. if i could answer your questions, please accept my answer. see thanks.
-
r_19 about 9 yearsSure! Thanks for the help!
-
Daniel Qiu about 9 years@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?
-
Sashi about 9 years@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.
-
Devran Cosmo Uenal about 9 years@DanielQiu this should help: stackoverflow.com/questions/16806739/…
-
Devran Cosmo Uenal about 9 years@Sashi sorry, no idea :(
-
Thiha Aung about 8 yearsAny help with that? stackoverflow.com/questions/36762491/…
-
jackbravo over 7 yearsYou could use a swift protocol to use a delegate and encapsulate your solutions a little bit more.
-
los.adrian over 7 years@DevranCosmoUenal How could I use your swift3 code? How could i call that method? Sorry, I am so new at iOS!
-
Shree Prakash over 7 years@DevranCosmoUenal Hello Sir , I want to add Password Protected after download file, Please guide me.
-
Kavin Kumar Arumugam almost 7 years@DevranCosmoUenal I'm getting error in
.get
method. I need to add any library? -
Kavin Kumar Arumugam almost 7 yearsI'm Getting Error in
downloadProgress
:Use of unresolved identifier
. How to Fix it? What is the type ofdownloadProgress
variable -
Devran Cosmo Uenal almost 7 yearsNo library is required -- but the Code might be now incompatible with the most current version of Swift.
-
Vivek almost 7 yearsdownloadProgress is UIProgressView class
-
Pintu Rajput almost 7 yearsAnd what about the url and localUrl ?
-
Lohith Korupolu over 6 yearsand where is this file saved to?
-
Ahmet Akkök over 6 yearsIt downloads the file to documents folder of your application. You can customize that in urlSession method though.
-
Pintu Rajput over 6 yearsBut major task is how to hide from device, Data can be access through app only.
-
Abdelrahman Mohamed almost 6 yearsHey How to use localUrl how to write it?
-
Naresh almost 5 yearshow to download audio file from url and save it in library?
-
iOSDude over 4 yearsHI, This won't work for background downloading(Home btn, locked case) can you provide solution to work with background data downloading in swift?
-
Dani over 3 yearsWhat is localURL? The filename I choose?