Downloading in background and waking app when fini

2020-08-04 09:48发布

问题:

I use URLSession configured as background session to download some files. When download finishes I use:

func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL)

to move downloaded file. Everything works fine, except that when app is in the background this function is not being called. It's called right after app is restored to foreground, but I would like the system to wake my app after file is downloaded. Is there some way to do this?

回答1:

There is indeed :)

You probably need to change a few things in your setup.

First the URLSessionConfiguration you use should be of type background like so:

URLSessionConfiguration.background(withIdentifier: "your.unique.id.here")

Then, in your AppDelegate you need to implement this method:

func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void)

Where you do something like store the completionHandler so it can be used later on.

And finally, where you normally do your "done downloading" magic... typically here:

 func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL)

You need to check for the presence of a completionHandler and if present, invoke that.

Example

The last two bits of the above may seem a bit...huh?? so here is an example.

In your AppDelegate you can define an optional for the completionHandler so you can store it for later on:

var backgroundSessionCompletionHandler: (() -> Void)?

and the method then looks like this:

func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
    if identifier == "your.unique.id.here" {
        backgroundSessionCompletionHandler = completionHandler
    }
}

And then you "just" need to call that once you are done processing the downloaded file, which you can do like this:

if  let appDelegate = UIApplication.shared.delegate as? AppDelegate,
    let completionHandler = appDelegate.backgroundSessionCompletionHandler {
        appDelegate.backgroundSessionCompletionHandler = nil
        OperationQueue.main.addOperation {
            completionHandler()
        }
    }

Also, take a look at this tutorial for instance.

Hope that helps you.