Can HealthKit background delivery launch the application if is not running? Particularly in a terminated state?
Access Encrypted Data For security, the device encrypts the HealthKit store when the user locks the device. As a result, your app may not be able to read data from the store when it runs in the background. However, your app can still write to the store, even when the phone is locked.
Open the Health app. Tap your profile , then tap Devices. Tap your Apple Watch. Tap Privacy Settings and make sure that Fitness Tracking is turned on.
This data is stored in Data Protection class Protected Unless Open. Access to the data is relinquished 10 minutes after device locks, and data becomes accessible the next time user enters their passcode or uses Touch ID or Face ID to unlock the device.
The list of apps compatible with HealthKit includes such popular names as: fitness apps — Strava, Runtastic Steps, Garmin Connect. nutrition apps — Lifesum, Foodzy. healthcare apps — HealthyNow, Hello Doctor.
After a full day of testing (iOS 9.2) I can confirm that HealthKit
background delivery DOES WORK in all of the following application states:
Keep in mind: part 1
Some HealthKit
data types have a minimum update frequency of HKUpdateFrequencyHourly
. That said, even if you set up a background delivery with frequency HKUpdateFrequencyImmediate
, you won't get updates more often than every hour or so.
Unfortunately, there is no info in documentation about minimum frequencies per data types, but my experience with Fitness types
was as follows:
Note: immediate
DOES NOT mean real-time but rather "some time shortly after" the activity data samples has been written to the HealthKit
database/store.
Keep in mind: part 2
If the device is locked with a passcode, none of you background delivery observers will be called. This is intentional due to the privacy concerns (read more: https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HealthKit_Framework/).
That said, as soon as the user unlocks the device, your HealthKit
background delivery observers will be called (if the minimum frequency time has passed, of course).
Sample code:
Take a look at Viktor Sigler's answer. Although, you can skip all three steps from the beginning of his answer since they are not required for HealthKit
background delivery to work.
This answer is some late but I hope this help the people to understand how to work with the HKObserverQuery
successfully.
First of all the HKObserverQuery
works fine in background mode and when the app is closed at all. But you need to set some options first to allow everything works fine.
Required Background Modes
in your info.plist
as in the following picture:You need to set the Background Fetch
in the following way:
3.1. From the Scheme toolbar menu, choose an iOS Simulator or Device.
3.2. From the same menu, choose Edit Scheme.
3.3. In the left column, select Run.
3.4. Select the Options tab.
3.5. Select the Background Fetch checkbox and click Close.
Then you can receive notifications when the app is in background or closed using the following code:
import UIKit import HealthKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? let healthKitStore:HKHealthStore = HKHealthStore() func startObservingHeightChanges() { let sampleType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeight) var query: HKObserverQuery = HKObserverQuery(sampleType: sampleType, predicate: nil, updateHandler: self.heightChangedHandler) healthKitStore.executeQuery(query) healthKitStore.enableBackgroundDeliveryForType(sampleType, frequency: .Immediate, withCompletion: {(succeeded: Bool, error: NSError!) in if succeeded{ println("Enabled background delivery of weight changes") } else { if let theError = error{ print("Failed to enable background delivery of weight changes. ") println("Error = \(theError)") } } }) } func heightChangedHandler(query: HKObserverQuery!, completionHandler: HKObserverQueryCompletionHandler!, error: NSError!) { // Here you need to call a function to query the height change // Send the notification to the user var notification = UILocalNotification() notification.alertBody = "Changed height in Health App" notification.alertAction = "open" notification.soundName = UILocalNotificationDefaultSoundName UIApplication.sharedApplication().scheduleLocalNotification(notification) completionHandler() } func authorizeHealthKit(completion: ((success:Bool, error:NSError!) -> Void)!) { // 1. Set the types you want to read from HK Store let healthKitTypesToRead = [ HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierDateOfBirth), HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierBloodType), HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierBiologicalSex), HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass), HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeight), HKObjectType.workoutType() ] // 2. Set the types you want to write to HK Store let healthKitTypesToWrite = [ HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMassIndex), HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierActiveEnergyBurned), HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning), HKQuantityType.workoutType() ] // 3. If the store is not available (for instance, iPad) return an error and don't go on. if !HKHealthStore.isHealthDataAvailable() { let error = NSError(domain: "any.domain.com", code: 2, userInfo: [NSLocalizedDescriptionKey:"HealthKit is not available in this Device"]) if( completion != nil ) { completion(success:false, error:error) } return; } // 4. Request HealthKit authorization healthKitStore.requestAuthorizationToShareTypes(Set(healthKitTypesToWrite), readTypes: Set(healthKitTypesToRead)) { (success, error) -> Void in if( completion != nil ) { dispatch_async(dispatch_get_main_queue(), self.startObservingHeightChanges) completion(success:success,error:error) } } } func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: .Alert | .Badge | .Sound, categories: nil)) self.authorizeHealthKit { (authorized, error) -> Void in if authorized { println("HealthKit authorization received.") } else { println("HealthKit authorization denied!") if error != nil { println("\(error)") } } } return true } //Rest of the defaults methods of AppDelegate.swift }
In the above method the HKObserver
is activated if the HealthKit authorization is granted by the user and then activate notifications.
I hope this help you.
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