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:
- It does get called when I schedule it. I see its print statement in the console.
- It's nearly identical to Apple's example.
- 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!
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:
var savedTask:WKRefreshBackgroundTask?
handle( backgroundTasks:)
function, save theWKURLSessionRefreshBackgroundTask
to the local variableself.savedTask = task
- do not task.setTaskCompleted() (!!)urlSession - didFinishDownloading:
, set the saved task to completed:self.savedTask?.setTaskCompleted()
after you've read the data received.Hope this can help!
ps. this works in the simnulator (xcode 8.3.3, watchOS 3.2)