Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WKURLSessionRefreshBackgroundTask isn't called when attempting to do background refreshes in watchOS

I'm working on a complication to get scheduled data from a web service. Every 20-30 minutes (or manually), I am scheduling a WKRefreshBackgroundTask to do this.

As suggested by Apple, I want the OS to handle the fetching of this data via a background NSURLSession.

This is the function I use to download the data I need:

func scheduleURLSession()
{
    print("\nScheduling URL Session...")

    let backgroundSessionConfig:URLSessionConfiguration = URLSessionConfiguration.background(withIdentifier: NSUUID().uuidString)
    backgroundSessionConfig.sessionSendsLaunchEvents = true

    let backgroundSession = URLSession(configuration: backgroundSessionConfig)

    let downloadTask = backgroundSession.downloadTask(with: URL(string: "https://www.myserver.com/somedata")!)
    downloadTask.resume()
}

A few things about this:

  1. It does get called when I schedule it. I see its print statement in the console.
  2. It's nearly identical to Apple's example.
  3. I've omitted the URL. That same URL works in the iOS/watchOS apps just fine so nothing is wrong with it.

The problem is, even though I am calling resume() on the task and allowing it to wake my app when it is complete, it doesn't seem to do either.

When it completes, it should be coming back to a WKExtensionDelegate handler:

func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>)

as a WKURLSessionRefreshBackgroundTask, but it does not.

My code, identical to Apple's sample code, then creates another session but rejoins it via the WKURLSessionRefreshBackgroundTask's identifier. This is where the delegate is set in order to process the downloaded data. Check the code:

func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
    for task in backgroundTasks {
        switch task {

        case let backgroundTask as WKApplicationRefreshBackgroundTask:
            print("\nWatchKit - WKApplicationRefreshBackgroundTask")
            // self.updateComplicationDataArrivalTimes(backgroundTask)
            self.scheduleURLSession()
            backgroundTask.setTaskCompleted()

        case let snapshotTask as WKSnapshotRefreshBackgroundTask:
            // Snapshot tasks have a unique completion call, make sure to set your expiration date
            print("\nWatchKit - WKSnapshotRefreshBackgroundTask")
            snapshotTask.setTaskCompleted(restoredDefaultState: true, estimatedSnapshotExpiration: Date.distantFuture, userInfo: nil)

        case let connectivityTask as WKWatchConnectivityRefreshBackgroundTask:
            print("\nWatchKit - WKWatchConnectivityRefreshBackgroundTask")
            connectivityTask.setTaskCompleted()

        case let urlSessionTask as WKURLSessionRefreshBackgroundTask:
            print("\nWatchKit - WKURLSessionRefreshBackgroundTask")
            let backgroundConfigObject = URLSessionConfiguration.background(withIdentifier: urlSessionTask.sessionIdentifier)
            let backgroundSession = URLSession(configuration: backgroundConfigObject, delegate: self, delegateQueue: nil)

            print("Rejoining session ", backgroundSession)
            urlSessionTask.setTaskCompleted()

        default:
            // make sure to complete unhandled task types
            task.setTaskCompleted()
        }
    }
}

But again, it doesn't seem to ever come back. I can't seem to figure out why this is working despite the code is identical to Apple's sample code for this project: WatchBackgroundRefresh: Using WKRefreshBackgroundTask to update WatchKit apps in the background.

Is there some setting in my project that I am missing? I am putting all this code in the ExtensionDelegate, new in watchOS 3. I am also conforming to WKExtensionDelegate and URLSessionDownloadDelegate.

Thanks for your help in advance!

like image 472
Mario A Guzman Avatar asked Dec 15 '16 04:12

Mario A Guzman


2 Answers

I got it working! I started out by looking a lot at the WWDC'16 videoes/notes, comparing them to the WatchBackgroundRefresh example from Apple. Then, while browsing the Apple Dev forums, I found this thread, with the same problem we encountered. There, a guy, "Daniel Fontes" posts his 'recipe' for getting it to work (note, not the accepted answer!), and that combined with the videos worked for me.

What I did to make the Apple example work:

  1. Make the MainInterfaceController an URLSessionDelegate
  2. Create a local variable: var savedTask:WKRefreshBackgroundTask?
  3. In the handle( backgroundTasks:) function, save the WKURLSessionRefreshBackgroundTaskto the local variable self.savedTask = task - do not task.setTaskCompleted() (!!)
  4. In the urlSession - didFinishDownloading:, set the saved task to completed: self.savedTask?.setTaskCompleted() after you've read the data received.
  5. Ensure that the resource you are accessing is https://, otherwise add the "App Transport Security Settings" to your Info.plist file.

Hope this can help!

ps. this works in the simnulator (xcode 8.3.3, watchOS 3.2)

like image 147
Jens Peter Avatar answered Oct 16 '22 21:10

Jens Peter


I never did get Apple's example working, but I did get background refresh working. Unlike their example you will need to make yourself the delegate of the session. So create your background session like this instead:

let backgroundSession = URLSession(configuration: backgroundSessionConfig, delegate: self, delegateQueue: nil)

I have a detailed code example here (see the question's code):

WatchOS 3 WKApplicationRefreshBackgroundTask didReceiveChallenge

Hope it helps.

like image 29
CodenameDuchess Avatar answered Oct 16 '22 21:10

CodenameDuchess