0

I am using following code, to download some files:

    let url = NSURL(string:"https://www.example.org/")!
    let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) in
        if error != nil {
            NSLog("%@", "Error: \(error)");
            return
        }

        NSLog("Loaded %i bytes", data!.length)
    }
    task.resume()

I want to process some of the files and quit my application after downloading. Therefore I need to know, when the download process is finished. The best would be, if there is a way to do this synchronously (no problem, if UI is blocked - it is just a spash screen with a progressbar). But as far as I understood this topic after some research, this is not possible in Swift any more...

I did wrap this code in a function, therefore I can't just add some code after the NSLog statement. What I need to know is: When did the last file finish downloading? How can I retrive this information?

EDIT: This code did work for me (but be aware, its deprecated!):

    // download file synchonously ////////////////////////////////////////////////////
    func downloadSync(fromURL: String, toPath: String) {
        let request = NSURLRequest(URL: NSURL(string: fromURL)!)
        var response: NSURLResponse?
        do {
            let data = try NSURLConnection.sendSynchronousRequest(request, returningResponse: &response)
            data.writeToFile(toPath, atomically: true)
        } catch {
            print("Error while trying to download following file: " + fromURL)
        }
    }
PjotrC
  • 311
  • 1
  • 6
  • 16
  • 1
    If you want to download some files and process them, consider (a) staying with asynchronous patterns so you always have responsive app; (b) employ background session. With background `NSURLSession`, your app doesn't need to be running for the download to progress. Why do you want to force the user to keep the app running in order to finish the downloads? And if they do happen to have the app running, why wouldn't you want to show them progress of the download? – Rob Jun 23 '16 at 01:04

1 Answers1

2

After you invoke task.resume(), the download starts.

When the download does complete (or an error is received) the code inside the { } following dataTaskWithURL is called. That's a closure and it's called asynchronously.

let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) in
    // This code is executed asynchronously after data has been received
}
task.resume()

Inside the closure you receive 3 params:

  1. data: the NSData you requested
  2. response: the whole NSURLResponse
  3. error: an NSError object

These 3 values are optional, so could be nil.

E.g. error could be populated and data could be nil.

The synchronous way [DEPRECATED in iOS 9]

This approach has been deprecated in iOS 9 and you should NOT use it, however here's the code

var response: NSURLResponse?
var error: NSError?
let urlData = try NSURLConnection.sendSynchronousRequest(request, returningResponse: &response)
Luca Angeletti
  • 58,465
  • 13
  • 121
  • 148
  • Yes, I know, that this example is asynchronously. I haven't found a synchronous way. I read here somewhere, that this option got removed... Is that true? I understand, that I should add an endless loop to my main thread. In that loop I check if all variables are true: for each file I have to indroduce such a variable, which is set to true, after the file is written. - Or is there a better way? Does closure mean, that it is an unnamed function? For what does the `in` after the params stand for? – PjotrC Jun 22 '16 at 22:02
  • 1
    @PjotrC: Why do you want a synchronous download? There's a way to do it but it has been deprecated in iOS 9. [Look here](http://stackoverflow.com/questions/31557688/synchronous-url-request-on-swift-2). – Luca Angeletti Jun 22 '16 at 22:08
  • Why not synchronous? Its easier and has no drawbacks for my use case. The example from you link does not work and I was not able to fix this issue... :( – PjotrC Jun 22 '16 at 22:46
  • @PjotrC: Please try the new code I added to my answer. As I told you you should avoid synchronous because it has been deprecated by Apple. – Luca Angeletti Jun 22 '16 at 23:00
  • You could use semaphores and wait on one until released in the completion handler – Feldur Jun 23 '16 at 07:53
  • It's ok to do sync sometimes. for instance, on app startup, you might need to download a bunch of resource files if the server says so. You have an indeterminate list of files, and rather than implement your own counter or semaphore scheme, just downloading a manifest, then downloading all the files listed in the manifest synchronously makes for a simple and well-readable slice of code, no....? Fine, it's deprecated; I still think it's a perfectly reasonable question. – ChrisH Mar 02 '19 at 04:34
  • Agree with ChrisH - there are cases where it's valid to do synchronous downloads because it makes for massively simplified code. I myself have such a use case on app startup: I have a compute intensive task I run on a background thread that is dependent on data downloaded from (several) url sessions. The point is that the thread is already background - I don't need to run the URL sessions on further background threads as the whole process is not time dependent. Because of Apple's decision I have to implement complicated thread coordination between my background thread and the URL session. – Tomm P Nov 12 '21 at 08:01