I'm developing app based on Health Kit and after some time it stopped receiving completion blocks from HealthKit. In Health app it figures as inactive data source and is not listed in apps allowed to read data, despite permission to read it is switched on.
Is there a way to fix that?
Is it because of query limit, some app blacklisting?
I couldn't find any documentation about this state.
If you were using HKObserverQuery, it will try three times (at increasing time intervals) before giving up and no longer sending notifications of new data. You may need to execute the completionHandler passed with the notification earlier; there is some dispute as to how much time can pass before the block is executed for a successful response to the notification.
I don't know how to clear the inactive application state; presumably, deleting the application from Health and then asking for permission again should do it.
Related
I am reading through the documentation for the Apple HealthKit and am stuck on understanding the difference between a query being registered for background deliveries vs. running on the background. This excerpt from the explanation of anchored object queries seems to differentiate between the two:
Anchored object query. In addition to returning the current snapshot of modified data, an anchored object query can act as a long-running query. If enabled, it continues to run in the background, providing updates as matching samples are added to or removed from the store. Unlike the observer query, these updates include a list of items that have been added or removed; however, anchored object queries cannot be registered for background delivery.
Source: https://developer.apple.com/documentation/healthkit/reading_data_from_healthkit
I am confused about the difference between the two types of "backgrounds." Does the statement "continues to run in the background" really mean "when the app is in the foreground, this query will continue to run without needing to be re-called?"
Background Delivery works like background app refresh, or GPS significant location change events. It wakes up your app to allow you to act on some event. In this case whenever a health sample you are listening for, is added to the store, your app is woken up (once within a given interval) in order to be able to process the event.
Think of a running/cycling app, where a user is trying to tracking their progress on a long workout. The app won't remain open the whole time, but you can ask for the number of steps every X interval so that you can keep your UI updated, or in sync with a server.
More info on background delivery: https://developer.apple.com/documentation/healthkit/hkhealthstore/1614175-enablebackgrounddelivery
The other reference to background, means that it continues to run in another thread, while the app is open. So if you query for heart rate, you can be notified every time a reading comes in, as opposed to just receiving 1 result and having to check every X seconds for a new one
Preamble
I wrote a mobile application which should show upcoming events. The app downloads it's data from server. Data is prepared in batches once every 24 hours and it's ready to be fetched after 4 am. This gives me a perfect opportunity to sync it overnight and to make new data immediately available when the user opens the app.
Background fetch
This was my first approach for syncing data with the server. It's advertised as very powerful feature, but using it alone (out of the test environment) is not enough:
There is no way to force background fetches to execute at exact intervals.
I thought that the frequency of background fetch operations could be configured
// Setup background fetch
let timeIntervalEveryHour: NSTimeInterval = 3600
let sharedApp = UIApplication.sharedApplication()
sharedApp.setMinimumBackgroundFetchInterval(timeIntervalEveryHour)
but it's still dynamic and I think it is never for users who did't use app very often.
If 'Background App Refresh' is disabled, which automatically happen if device is in 'Low power mode', background fetch won't be triggered.
Other problems like Data Protection when device is locked and 30 seconds window for completion of all tasks are considered.
Remote (silent) notifications
So I took the next step and configured the server to post silent notifications once the batch is ready just to found that this is also not enough:
If application is force killed by the user or device is rebooted, notification won't be handled.
Rate limits. Delivery will be delayed, this depends on a variety of factors that are not explicitly specified by Apple, but probably - battery life, whether phone is on cellular, etc.
Sometimes silent push notifications are dispatched when application starts, which could lead to race conditions with the check for manual synchronisation. So I'll try to force it by adding "alert" = ""; to payload. (As it is suggested here)
Silent Push notifications could be disabled by the user be setting off 'Background App Refresh' - source
Manual synchronisation
To be sure that data is always up to date, if it's not recently updated, when app comes to foreground user is presented with alert which asks for manual synchronisation. Also it could be started later from settings tab. Unfortunately, according to analytics, most of the fetch requests are made manually.
First run case is also handled.
Next steps
I'm considering using VOIP notification. They should wake the app even if it's force killed. However I'm concerned that that would cause app get rejected.
Questions
Is there something I'm missing? I know that background synchronisation depends on a variety of factors, there could be no internet connection and etc, but is there any way to make it more reliable?
I am developing an iOS app which needs to receive regular data from the server at specific period of time (every 5 seconds).
Apple gives developers some choices for background working.
I convinced that Background Fetch is the proper method for my problem.
But when I tested it I got confused.
Background Fetch has many issues with my requirements:
It does not fetch data at period you defined when declared it:
application.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum)
I tried this and this:
application.setMinimumBackgroundFetchInterval(5). But no guarantee
that your app will fetch data at this interval.
When the screen is off, your app will not make any fetch until the
phone is unlocked again.
I wonder if there is a way that let my app receives real time data (or at least every 5 seconds).
Note: I read about remote notifications and I do not guarantee that I can execute it now, So I am searching for other solutions.
Thanks
There is no solution to this problem until writing this answer.
If I need a real time notification then I must use Push Notification.
There is no way to check for server data every period of time from the app only, I can send a Remote Notification when new data available on server to wake up the app and let it connect to the server.
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.
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.