WKURLSessionRefreshBackgroundTask

Hi,


im trying to play with the Apple WatchBackgroundRefresh Demo App before upgrading my own WatchApp with the new BackgroundRefresh features.

Apple DemoCode:

https://developer.apple.com/library/prerelease/content/samplecode/WatchBackgroundRefresh/Introduction/Intro.html


With watchOS3 Beta 2 (Xcode 8 Beta2) i got the Demo running but the WKURLSessionRefreshBackgroundTask never got called (tested with different URLs, but the WKApplicationRefresh seems to work).

After upgrading to watchOS3 Beta3 (Xcode 8 Beta3) the handle(_ ) constantly gets called with WKApplicationRefreshBackgroundTask after sending the App into Background, even without scheduling a BackgroundRefresh ? The WKURLSessionRefreshBackgroundTask never gets called either.


Anyone got this working ?


Best regards Marco

Accepted Reply

Sorry to see that folks are running into issues here.

A few things...


1. On the Simulator (Watch), it appears that this is not working correctly and we are investigating the issue.

2. On device, the sample code works but you must add the app to the Dock first. Additionally, URLSession tasks have a decently wide window in which to fire although, in our testing, the tasks are generally being served within ~10 minutes.


I hope that helps. Sorry for the confusion.

Replies

Yup im having the same problem.


I got the Sample Code from the developer apple BETA sample code area called:

WatchBackgroundRefresh: Using WKRefreshBackgroundTask to update WatchKit apps in the background


When I run and schedule the background refresh then go into background mode, I can see it call

WKExtension.shared().scheduleBackgroundRefresh


Then the first callback after the timeout specified has the userInfo dictionary supplied, so the the code schedules a WKURLSessionRefreshBackgroundTask.


And then all **** breaks loose.

Its like the delegate method:

func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {


Gets spammed multiple times a second with a background refresh task which causes the app to then try and create a new WKURLSessionRefreshBackgroundTask but of course none of them ever finish or call any of the URLSessionDownloadDelegate methods.


I don't understand why this doesn't work, especially for the example watchOS 3 code from Apple dated 2016-07-18 which must have been released for Xcode 8 Beta 3.


Xcode 8 and watchOS 3 beta is a huge time sink that feels like going nowhere fast.

Ok, i'm glad that im not the only one 😉

So i'm waiting for Beta 4....

Seeing the same thing as well, can't get a background URLSession to work at all with the handleBackgroundTask methods.

Has anyone gotten any further along with this thing? I just tested the sample app with Beta 4 and the same problems seem to exist. I did notice this not in the last two watchOS 3 beta release notes:


"The determination of whether a WatchKit app implements handleBackgroundTasks: in its WKExtensionDelegate is made at launch time and never updated. If your WatchKit app sets [WKExtension sharedExtension].delegate after being launched, it will not properly be checked.


Workaround: Use the WKExtensionDelegate that is designated in your Info.plist file."


The WatchBackgroundRefresh sample project does change the ExtensionDelegate. But, I refactored the app to use the real delegate, and it didn't really change anything....

Yes, just tried again with the same result 😟 Seems we have to wait for Beta 5 ?

Still doesn't work in Beta 5 either (on device or in Simulator) - bug report 26972292

Agreed. Any chance you could add the bug to http://www.openradar.me so we can dupe? (At this point, I'm not really sure how to articulate it.)

Same here. I tried all the tricks I could think of.

Weirdly the user initated scheduleBackgroundRefresh never gets called now, but there isnt constant spam from the system.

Using the Debug -> Simulate Background Refresh seems to initate the code, but the DownloadTask never calls back.


I still don't understand how the URLSession delegate works. You can't set the delegate, so which class becomes the delegate?

One would assume the ExtensionDelegate but its never set anywhere so is it possible thats why its not working, it doesnt have the right delegate to call those methods.


When I look in the Simulator folder I can see the background download is completed in the cache folder but it just never gets moved anywhere.

Tested with Beta 6 - still not working.


But as above, checking in the cache folder, I can see the download going but when it completes, it never gets moved out of the cache folder and the handle delegate doesn't get called.


I've even moved everything out into the ExtensionDelegate class and made sure that that is the only delegate (removed the delegate change from the MainInterfaceController) but no joy.

I just tried with xcode beta 6,with iOS 10 beta 6 and watch os beta 6. It seems that nothing changed


.I can schedule a normal background refesh ,then using the urlsession to fetch some data ,but he system never gives me a WKURLSessionRefreshBackgroundTask callback in handle function.


It's real annoying .I think this kind of background task api is real great but just wonder why it does not work even with apple's sample code.

I've watched all the WWDC 2016 videos and some techniques are described a litte bit different there (and I think the sample code is just wrong). I am now able to download content in the background and get a WKURLSessionRefreshBackgroundTask. Some other things are my own findings so I don't know if they are right...


So this is what I am doing now:

1. Create a NSURLSessionConfiguration with Identifier [[NSUUID UUID] UUIDString]]

2. Create a NSURLSession with Delegate self and delegateQueue nil
3. Remember the NSURLSession

4. Store the Task when getting a WKURLSessionRefreshBackgroundTask to complete it after processing the download (especially this is wrong in the sample code)

5. NSURLSessionDownloadDelegate -> didFinishDownloadingToURL to process the download

6. NSURLSessionDelegate -> URLSessionDidFinishEventsForBackgroundURLSession to finally complete the task (4) and release the reference to the NSURLSession (3)


This is only working on the device for me not on the simulator. On the simulator I never get the WKURLSessionRefreshBackgroundTask.


On the device it takes some time to get the WKURLSessionRefreshBackgroundTask because the amount of tasks per hour is limited. You can get more tasks if your Watch App is in the Dock together with only few other apps. You can also include and activate a complication to get more tasks per hour.


You should also invalidateAndCancel an old NSURLSession if you no longer need the scheduled download(s).


Please correct me if I am wrong.

I'm using a similar strategy to you, and everything you've listed matches my findings. Thanks for providing a neat list.

Any chance of sharing the code in your handling of background tasks? A bit hard with samples not working and simulator not acting as it should :-/


- Christer

I noticed the WWDC talks were different, and in particular that they store the tasks for later completion. There are so many things which seem wrong with the Sample Code but I tried so many permutations of different code and could never get the delegate callback for a background URLSessionDownloadTask.


In your list, what do you mean by

2. Create a NSURLSession with Delegate self and delegateQueue nil


The delegate property of the URLSession is read only, because I assume it calls the info plist designated ExtensionDelegate class?

But that isnt written anywhere and its really confusing. How do you get the delgate callback? The task won't take a trailing closure because its a background task. You cant set the delegate on the URLSession, and making the ExtensionDelegate conform to the URLSessionDownloadDelegate seems to be the obvious thing but is that even correct?


Some sample code would be greatly appreciated. 🙂


Can you post the code here or a non active link (so it doesnt get flagged for moderation) to a GIST?

[NSURLSession sessionWithConfiguration:backgroundConfiguration delegate:self delegateQueue:nil];


One other very important thing: HTTP is not working by default. You can set NSAllowsArbitraryLoads to true but the best way is to use HTTPS.