Search code examples
swiftbackground-processdownloadurlsession

Downloading in background and waking app when finished


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?


Solution

  • 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.