HKObserverQuery has the following method that supports receiving updates in the background:
- initWithSampleType:predicate:updateHandler:
The updateHandler has a completionHandler which has the following documentation:
This block is passed to the update handler. You must call this block
as soon as you are done processing the incoming data. Calling this
block tells HealthKit that you have successfully received the
background data. If you do not call this block, HealthKit continues to
attempt to launch your app using a backoff algorithm. If your app
fails to respond three times, HealthKit assumes that your app cannot
receive data, and stops sending you background updates.
From looking at other posts it seems like there's a lot of confusion revolving around this handler. Below are some questions that I have about it:
When should the handler be called? If called too late, then HK might think that the app never received the query update causing you to hit the background update 3-strikes back-off algorithm. The documentation states that it should be called after handling other queries. Depending on how long it would take to run those queries, it sounds like you could get dangerously close to hitting the background update strikes.
Why is this needed? Shouldn't the system know that the app has been launched and has received the background update? When using CoreBluetooth in the background it just wakes your app up in the background for 10 seconds. No need to call any handler or deal with the background update 3-strikes.
If you hit the background update 3-strikes and HK stops sending updates is that permanent? Does HK ever start sending the background updates again? What if there's a bug that prevented the handler to be called and now you've fixed it. Is the app stuck never receiving the updates? Or will it reset when the app is re-launched or updated?
Does HK keep your app running in the background until the handler is called? Is that part of its purpose or just a side effect? If it's part of its purpose how long can we run before needing to stop (and hit the first background update strike)?
When should the handler be called?
Call it after you are done your job. Your code should not do complex operations. The app is in the background and the user does not see what's changed. You can just set a "flag" that data is updated and do complex operations after the user launched the app. If your decision about either notifies the user or not based on complex operations, then try to refactor code so that all necessary data is pre-calculated (e.g. in UserDefaults) and extra data is simply fetched with that data. So, 1-2 seconds is enough for your calculation.
Why is this needed?
All such handlers have completion closures. They are needed for iOS to know if your app works fine. If your app will eat too much CPU time, then iOS could become slow. Hence, Apple wants to be sure that iOS works fine despite bad apps.
If you hit the background update 3-strikes and HK stops sending updates is that permanent?
No.
Does HK ever start sending the background updates again?
Yes. But it depends on many factors. It may try to call your app again in 1-2 days. If nothing changes it will call it rarely.
Does HK keep your app running in the background until the handler is called?
This is unknown. It depends on many factors. Probably if iPhone is charging it will allow running your app longer just to estimate if the completion handle is called or not. If your iPhone is not charging and closed to 0% battery, then more likely iOS will kill your app. So, you should not do any job after you called the completion handler. And try to keep it simple.
Recommendations
You should process new data as quickly as possible. If you need to fetch a lot of data, then try to optimize this and pre-calculate it when the app is in foreground, then save somewhere (UserDefault), and use new data with cached data to make a decision (e.g. notify user about something; I believe you need background updates exactly for that).
1-2 seconds or less is a good time for background updates.
Related
I am a bit confused with how performFetchWithcompletionHandler engaged with the iOS app life cycle. In the docs it's mentioned that it gives us 30 seconds to download any data but will our app freeze for 30 seconds at that time?
... will our app freeze for 30 seconds at that time?
No, the exact opposite. This method is called when your app is suspended, but the OS is going to let you check for data to retrieve. So, your app will be awaken, run in background, and this method will be called, at which point you can perform your quick fetch (not more than 30 seconds) to see if there is any data to retrieve. When you’re done processing your quick fetch, you call the completion handler to let the OS know that you’re done and that your app can safely be suspended again.
If, though, you fail to complete your request in 30 seconds, your app may be summarily terminated and not participate in future background fetches. So it’s important to finish in the allotted time.
As the docs say:
Implement this method if your app supports the fetch background mode. When an opportunity arises to download data, the system calls this method to give your app a chance to download any data it needs. Your implementation of this method should download the data, prepare that data for use, and call the block in the completionHandler parameter.
When this method is called, your app has up to 30 seconds of wall-clock time to perform the download operation and call the specified completion handler block. In practice, your app should call the completion handler block as soon as possible after downloading the needed data. If you do not call the completion handler in time, your app is terminated. More importantly, the system uses the elapsed time to calculate power usage and data costs for your app’s background downloads. If your app takes a long time to call the completion handler, it may be given fewer future opportunities to fetch data in the future. For more information about supporting background fetch operations, see Background Execution in App Programming Guide for iOS.
This is regarding background fetch, like when an app receive silent notifications. In this case, iOS wakes up an application in background for max 30 sec. and do the stuff as per writer code and the kill the app. So user will be unaware of this.
It is not recommended to write complex logic in this method performFetchWithcompletionHandler. The reason is the time limit(30 sec.) and the app developer doesn't have a control over it.
To get this work, background mode should be enabled in capabilities of project.
One of the example about this is silent push notification (notification payload has the key content-available = 1)
For more details see this
I can't seem to find the right documentation, tutorial or SO post to point me towards the right implementation so far... even though there are many posts about this topic floating around.
Problem
I have an app that connects (pairs) to a BT device I built. What I want, is when the user presses the home button or backgrounds the app I want to send an update through a BT service layer to a BT device on a regular interval. I am doing this now (only fires once) in the applicationWillResignActive and applicationDidEnterBackground methods and it works perfectly. The problem is that I can only perform this once it seems. Any loops, timers, background type services that I start up in these methods, die very soon there after.
What I need
Is for the application to keep looking for my BT device in the background and every 10 seconds or so and send an update to the device. I first need to check to make sure the app is still backgrounded, but once I know its in the background I wanna send the update. When the app comes back in focus I can stop the updates to the BT device but its crucial that it sends them when the app is not in focus.
Research
I have found these SO posts (long-running tasks, background task execution handler, using this in an NSTimer loop... but it dies) but they have not helped with implementation at all.
When I wrote this post I was far too new to OBJ-C and didn't quite understand the concept of delegate methods with the proper access level to fire in the background (i.e. info.plist access under bluetooth-central).
The end result a month later was to build a peripheral device that could wake up the application in a timed loop via subscribed characteristic updates. Once the app wakes up, you have 10 seconds to handle the event that has just occurred and since I am only saving off a copy of the data that the device posted, all is well.
After working with complications for a few days, I feel confident saying the following about the update process for updates that happen at a prescribed interval:
The system calls requestedUpdateDidBegin()
This is where you can determine if your data has changed. If it hasn't, your app doesn't have to do anything. If your data has changed, you need to call either:
reloadTimelineForComplication if all your data needs to be reset.
extendTimelineForComplication if you only need to add new items to the end of the complication timeline.
Note: the system may actually call requestedUpdateBudgetExhausted() instead of requestedUpdateDidBegin() if you've spent too much of your complication's time budget for the day. This is the reason for this question.
If you called reloadTimelineForComplication, the system will call getCurrentTimelineEntryForComplication (along with the future and past variants that get arrays, depending on your time travel settings)
This is conjecture as I haven't tested it yet, but I believe if you called extendTimelineForComplication that only the getTimelineEntriesForComplication(... afterDate date: NSDate ...) would be called.
The system will then call getNextRequestedUpdateDateWithHandler so you can specify how long until your complication requires a new update.
Apple's documentation is quite clear that you should not ask for updates too often, or conduct too much processing in the complication code or you will exhaust your time budget and your complication will stop updating. So, my question is: where and when do you do the update?
For context, my scenario is a URL with return data that changes up to two times per hour.
The most obvious place in which to put the URL fetch code is func requestedUpdateDidBegin() Fetch the data, store it, and if there's no change, just return. If there was a change then extend or reload the timeline.
However, a URL fetch can be costly. Alternatives:
Put the code on the phone app and send it over with a WCSession, but if the user closes that app then the updates will no longer happen.
Use push updates, but this isn't a web app, so I have no place to send them from.
Obviously I will update all the data when the user interacts with the watch app, but that now means it only gets updated when the user uses the app, which negates the need for a complication.
Is there anywhere else? Can I have a periodic function in the watch app that isn't part of the complication? Where is the right place to fetch the data for a complication update?
For watchOS 3, Apple recommends that you switch from using the complication datasource getNextRequestedUpdateDate scheduled update to update your complication.
The old way for watchOS 2
requestedUpdateDidBegin() is really only designed to update the complication. Keeping your complication (and watch app) up to date usually involves far more than reloading the timeline (and asynchronously retrieving data never fit in well with the old approach).
The new way for watchOS 3
The new and better approach is to use background refresh app tasks. You can use a series of background tasks to schedule and handle your app extension being woken in the background to:
Fetch new data
using WKWatchConnectivityRefreshBackgroundTask to obtain data from the phone, or
using WKURLSessionRefreshBackgroundTask to download data from a server
update your model once the data arrives,
update your complication from the model (by reloading or extending the timeline), and finally
update your app's dock snapshot to show the data on the dock
Call each tasks’s setTaskCompleted method as soon as the task is complete.
Other benefits of using app tasks
One of the key features about this design is that the watch extension can now handle a variety of foreground and background scenarios which cover:
initially loading data when your app/complication starts,
updating data in the background, when the extension is woken by a background task, and
updating data in the foreground, when the user resumes your app from the dock.
Apple recommends that you use each opportunity you are given regardless of whether your app is in the foreground or background to keep your complication, app, and dock snapshot up to date.
Are there any limitations?
The number of total available tasks per day is divided among the number of apps in the dock. The fewer apps in the dock, the more tasks your app could utilize. The more apps in the dock, the fewer you can utilize.
If your complication is active, your app can be woken up at least four times an hour.
If your complication is not active, your app is guaranteed to be woken at least once an hour.
Since your app is now running in the background, you're expected to efficiently and quickly complete your background tasks.
Background tasks are limited by the amount of CPU time and CPU usage allowed them. If you exceed the CPU time (or use more than 10% of the CPU while in the background), the system will terminate your app (resulting in a crash).
For more information
A good introduction explaining when and why to update your watch app is covered in Designing Great Apple Watch Experiences.
For specifics, the Keeping Your Watch App Up to Date session covers everything you need to know to keep your complication, app, and dock snapshot up to date.
WatchBackgroundRefresh sample code demonstrates how to use WKRefreshBackgroundTask to update WatchKit apps in the background.
Edit: El Tea (op) has posted a good answer at https://stackoverflow.com/a/32994055/630614
This is an interesting question/problem, and I've been wondering about a lot of the same!
For the most part, it seems that when I'm working on a new complication I need to step back and see when I really want to update it. A "countdown" complication could set all future timeline entries at one time, when the "end date" is set. An app that shows the current status of a web service could have relevant data stored in NSUserDefaults when an APNS comes through.
If you don't have access to APNS, don't want to run your iOS app in a background mode, and don't want to make HTTP requests from Apple Watch, I can think of 2 other options.
1) Schedule local notifications. The good part is that your Apple Watch should run didReceiveLocalNotification, but the bad part is that the user will get a notification when you're simply trying to check the status without a disruption.
2) Send a message to iOS via sendMessage(_:replyHandler:errorHandler:) in your reloadTimelineForComplication method, setting nil for the replyHandler to make it as quick as possible:
Calling this method from your WatchKit extension while it is active and running wakes up the corresponding iOS app in the background and makes it reachable.
Your iOS app could perform whatever network requests are needed and then store the information or push it to Apple Watch. Unfortunately, I don't think the watch extension will have it's session.didReceive... called until you run it, but you could access the data on the next call to requestedUpdateDidBegin.
As I said, I'm very interested in this same thing, so post some thoughts back and maybe we can extrapolate on some best practices here.
I'm writing a iOS/Swift application which reads data from a REST service each X minutes and updates the UI accordingly.
Now I would like that when the app is put in the background, a task keeps being invoked at X minutes intervals reading from the REST service and, in case the data just read satisfies a given condition, show a notification prompting the user to bring the app back to the foreground.
In my searches I've read that during applicationDidEnterBackground event, I should start a task with beginBackgroundTaskWithExpirationHandler.
The problem is that, if I've understood correctly, this allows a maximum of 10/15 minutes after which the app is terminated if the task is not stopped with endBackgroundUpdateTask, while I want the task to keep polling the service indefinitely (at least until the user disable it from the app's settings)
My question is:
How is this kind of functionality performed normally? Do some common solutions or best practices exist for the solution of such a problem?
Use iOS Background Fetch feature where you can specify minimum background fetch interval. But actual interval between successive invocation of your code will be determined by iOS framework. For details checkout this link: http://code.tutsplus.com/tutorials/ios-7-sdk-working-with-background-fetch--mobile-20520
I use this approach in my app and I think it is a preferred way of doing.
You can use a local notification that can be presented from the background in case your condition is met.
Correct, iOS will eventually shut down the background process, you can't enforce continuous background activity. Use the backgroundTimeRemaining property to check how much time your application has left and try to handle it as gracefully as possible by calling endBackgroundTask so that iOS does not force kill your app.
As a solution, you could think about using remote notifications with with content-available : YES, which runs the didReceiveRemoteNotification
Have a look at the Parse.com Their local datastore is an abstraction for what you are trying to acheive.
By the way, is it really necessary to refresh in the background. If call is relatively quick, there is no need to refresh until the user open's the app. Background processes like that, using the net can be quite battery consuming when the user are not on a Wifi. So consider the use case carefully!
iOS 7+, 8 (last one not released yet, however targeting on it).
The app.
As a user I start the app and switch to other apps (mail, safari, etc.), leaving the app running but not a foreground one.
The app establishes HTTP connection to server via Internet and starts periodically sending GPS location data to the server (with some interval).
Is it possible while the app is not on the foreground? I mean is it possible to get geolocation data and periodically send it from the app to the server via HTTP POST while using other apps?
If the answer is "YES", please help me with references. I will investigate it further.
Yes, and the method you want to research is performFetchWithCompletionHandler:
Implement this method if your app supports the fetch background mode.
When an opportunity arises to download data, the system calls this
method to give your app a chance to download any data it needs. Your
implementation of this method should download the data, prepare that
data for use, and call the block in the completionHandler parameter.
When this method is called, your app has up to 30 seconds of
wall-clock time to perform the download operation and call the
specified completion handler block. In practice, your app should call
the completion handler block as soon as possible after downloading the
needed data. If you do not call the completion handler in time, your
app is terminated. More importantly, the system uses the elapsed time
to calculate power usage and data costs for your app’s background
downloads. If your app takes a long time to call the completion
handler, it may be given fewer future opportunities to fetch data in
the future. For more information about supporting background fetch
operations, see “App States and Multitasking” in iOS App Programming
Guide.
https://developer.apple.com/library/ios/documentation/uikit/reference/uiapplicationdelegate_protocol/Reference/Reference.html#//apple_ref/occ/intfm/UIApplicationDelegate/application:performFetchWithCompletionHandler:
Just as Mike mentioned, you should look into background fetch. For more details, checkout this objc.io post.
The information you need should be in the Background Fetch section.