iosswiftnsdatansurlsessionnsurlsessiondownloadtask

Get the data from NSURLSession DownloadTaskWithRequest from completion handler


So I'm having hard time understanding something. This are the things I understand about NSURSession :

It works perfectly. The problem is that I need to DOWNLOAD the data to the disk and not only to the memory(I'm downloading images). So I tried the same with DownloadTaskWithRequest + his completion handler and I have noticed that the parameters he takes are the same expect the first one which is NSURL and in DataTaskWithRequest is NSData so it makes things very simpler. ex.

 let task2 = NSURLSession.sharedSession().downloadTaskWithRequest(request, completionHandler: { (location : NSURL!, response : NSURLResponse!, error : NSError?) -> Void in
            if error != nil {
                return
            }

            //How do I get the data??
        })
task2.resume()

My Question is this : I know I can fetch the DATA out of the Location(NSURL) using :

    var data = NSData(contentsOfURL: location)

1)Will contentsOfURL will make another "request" do get this data , or that he is working locally? If it sending request again , how can I avoid it?

2)Is this the right way(I know I can use the delegate methods, I prefer not)?

3)How can I store the data i have downloaded(after questions number 1 and 2 answered) locally , and access it if needed?

Thank you guys!! Sorry for newbie question , I really do care about efficient - Thank you!


Solution

  • When using download tasks, generally one would simply use the location provided by the completionHandler of the download task to simply move the file from its temporary location to a final location of your choosing (e.g. to the Application Support directory, or Documents folder, or Cache folder, or whatever) using FileManager.

    let task = URLSession.shared.downloadTask(with: url) { location, response, error in
        guard let location, error == nil else {
            print(error ?? URLError(.unknown))
            return
        }
    
        do {
            let fileManager = FileManager.default
            let fileURL = try fileManager
                .url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
                .appending(path: "test.jpg")
            try fileManager.moveItem(at: location, to: fileURL)
        } catch {
            print(error)
        }
    }
    task.resume()
    

    Or from Swift concurrency (i.e., async-await):

    let (location, _) = try await URLSession.shared.download(from: url)
    let fileManager = FileManager.default
    let fileURL = try fileManager
        .url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
        .appending(path: "test.jpg")
    try fileManager.moveItem(at: location, to: fileURL)
    

    You certainly could also load the object into a Data using contentsOf. Yes, it works with local resources. And, no, it won't make another request ... if you look at the URL it is a file URL in your local file system. But you lose much of the memory savings of download tasks that way, so you might use a data task if you really wanted to get it into a Data. But if you wanted to move it to persistent storage, the above pattern probably makes sense, avoiding using a Data object altogether.