Get the data from NSURLSession DownloadTaskWithRequest from completion handler
Asked Answered
V

1

11

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

  • Generally , I have 2 options for (as far as I know) DataTask(e.x dataTaskWithRequest) And DownloadTask(e.x DownloadTaskWithRequest) - Using their delegate method , or use the completion handler , Can't do both. I have managed to receive DATA using dataTaskWithRequest like this :

    let request = NSMutableURLRequest(URL: dataSourceURL!)
    request.HTTPMethod = "POST"
    
    let postString = "lastid=\(id)"
    request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
    let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
        data, response, error in
    
        if error != nil {
            println("error=\(error)")
            return
        }
        if data != nil {
            println("works")
            //Handle data
    
    
        }
        //println("response = \(response)")
    
    
    }
    task.resume()      
    

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!

Valerle answered 6/4, 2015 at 16:11 Comment(0)
C
16

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.

Caphaitien answered 6/4, 2015 at 16:34 Comment(9)
Rob, Honestly i would give everything for your knowledge on NSURLSession :D , The problem is that for better user experience i wanted to save files(images only) locally , so if the users exit and open it again it wont re-download it. Can you explain me about the difference between Documents or Cache folder when it comes to efficiency ? And how can i access the data later and load it to e.g image view? thanks!!Valerle
Documents vs Cache folder doesn't matter re efficiency. See File System Basics for discussion of Documents vs Cache (former is for user files that are backed up; latter is for resources that can be removed if the device runs low in storage and is only for resources that can be re-retrieved). The key is that if you avoid NSData with your download tasks, you'll ensure a smaller memory footprint while the app is running.Caphaitien
But is it possible to load imageview with file from document directory? Can you show an example? If i understand you correctly, when i download the images you save then as a file at certain path?Romans
Sure, you can use UIImage(contentsOfFile:) (or if using NSURL, use NSData(contentsOfURL:) with the file URL, and then UIImage(data:) to then load the image property of UIImageView).Caphaitien
Rob, im sorry i havent replied i was a at work (im a waiter lol) , Thanks for your awesome replies , i will read them try to get most of them and hit you if i have a problem . :DValerle
Rob, does this line : let fileURL = documents.URLByAppendingPathComponent("test.jpg") changes the name of the file after i download it? because i want to rename the files after i download it , and it dosent seems workings ,, let me know , thanks!Valerle
No, you can rename it during this move process. If the rename failed and you implemented it like I have above (where I println the error), it should print an error message. What error did it print? By the way, if you're putting it in a subdirectory, make sure to create that subdirectory first.Caphaitien
@Caphaitien thanks for yet another informative answer. don't you also need to check for whether error != nil? it seems this only prints an error if location is missing?Charwoman
Yeah, but in practice, if the request failed for any reason, location is nil. But it's prudent to check for both and have revised answer accordingly.Caphaitien

© 2022 - 2024 — McMap. All rights reserved.