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:
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 the WKURLSessionRefreshBackgroundTask
to the local variable self.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)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With