HealthStore enableBackgroundDelivery when screen is locked
Asked Answered
Z

4

6

Hello I'm trying to setup the health store observer with background delivery enabled. My problem is that it won't deliver anything when the screen is locked. I have simplified my code for this question to get to the point :) I have HealthKit in my plist and I have accepted healthStore type step count. Everything is fine when the app is open and when the screen is not locked. But when the screen is locked I don't get any observations. For test purpose the frequency is set to immediate.

My code is as follows

- (void)setupHealthStore{
if ([HKHealthStore isHealthDataAvailable])
{
    NSSet *readDataTypes = [self dataTypesToRead];
    self.healthStore = [[HKHealthStore alloc]init];
    [self.healthStore requestAuthorizationToShareTypes:nil readTypes:readDataTypes completion:^(BOOL success, NSError *error)
     {
         if (success)
         {
             HKQuantityType *quantityType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
             [self.healthStore enableBackgroundDeliveryForType:quantityType frequency:HKUpdateFrequencyImmediate withCompletion:^(BOOL success, NSError *error)
             {
                 if (success)
                 {
                     [self setupObserver];
                 }
             }];
         }
     }];
}

}

The above method is called in AppDelegate didfinishLaunchWithOptions

The next method is

- (void)setupObserver{
HKQuantityType *quantityType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
HKObserverQuery *query = [[HKObserverQuery alloc]initWithSampleType:quantityType predicate:nil updateHandler:^(HKObserverQuery *query, HKObserverQueryCompletionHandler completionHandler, NSError *error)
{
    if (!error)
    {
        [self alarm];
        if (completionHandler)
        {
            NSLog(@"Completed");
            completionHandler();
        }
    }
    else
    {
        if (completionHandler)
        {
            completionHandler();
        }
    }
}];
[self.healthStore executeQuery:query];

}

When I open the app it immediately returns the observation.

Zepeda answered 11/2, 2015 at 15:34 Comment(1)
Look at my answer here: https://mcmap.net/q/363332/-healthkit-background-delivery-when-app-is-not-runningHalbeib
B
9

When iPhone is locked, you cannot access healthKit data with any way.

When iPhone is unlocked but the app is in background, you can only use HKObserverQuery, which is used to know whether some new samples are added or not.

When iPhone is unlocked and the app is in foreground, you can use everything related to HealthKit Framework.

Buckskin answered 14/10, 2015 at 17:29 Comment(3)
This should be the correct answer. HealthKit is not query-able when the device screen is off and a passcode is set (this is referred to as locked).Dormeuse
When the phone is unlocked and my app is in the background on iOS 12, I am able to query the HealthKit store.Budgie
@JoshuaC.Lerner You need to know that this answer was written in 2014. There might be some changes in HealthKit.Buckskin
T
3

I was able to get this to work observing weight and blood glucose changes to HealthKit.

In ApplicationDelegate:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    // Override point for customization after application launch.        

    GlobalHealthManager.startObservingWeightChanges()

    return true
}

HealthManager.swift

    let past = NSDate.distantPast() as NSDate
    let now   = NSDate()
    return HKQuery.predicateForSamplesWithStartDate(past, endDate:now, options: .None)

    }()

    //here is my query:
    lazy var query: HKObserverQuery = {[weak self] in
    let strongSelf = self!
    return HKObserverQuery(sampleType: strongSelf.weightQuantityType,
        //predicate: strongSelf.longRunningPredicate,
        predicate : nil, //all samples delivered
        updateHandler: strongSelf.weightChangedHandler)
    }()




func startObservingWeightChanges(){
        healthKitStore?.executeQuery(query)
        healthKitStore?.enableBackgroundDeliveryForType(weightQuantityType,
            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)")
                }
            }

    })
}



/** this should get called in the background */
func weightChangedHandler(query: HKObserverQuery!,
    completionHandler: HKObserverQueryCompletionHandler!,
    error: NSError!){

        NSLog(" Got an update Here ")

     /** this function will get called each time a new weight sample is added to healthKit.  

    //Here, I need to actually query for the changed values.. 
   //using the standard query functions in HealthKit.. 

         //Tell IOS we're done... updated my server, etc. 
         completionHandler()         
}


}
Tinaret answered 2/3, 2015 at 20:33 Comment(9)
I should note that this works even when the app isn't running, provided you have the correct permissions.Tinaret
Thank you for the answer. Are you able to actually query the HealthStore for data ? Because according to the docs HealthStore is protected when the screen is locked.. With locked I mean locked with passcode. Anyways it is nice if it is working but I need it for the step data type..Zepeda
Yes, I'm able to query for the data using the HK query APIs. I'm confused about the passcode issue.. if the screen is locked and your app (or whatever app is writing this data) is able to write the steps to HK database, then I would think the background observers will work.Tinaret
The problem is that I'm not writing data, I'm reading data. But I used a support ticket at Apple and got the answer that all HealthStore data is protected when the screen is locked (with passcode) it is possible to get updates from the healthStore observer if the application is awaken in background by other services like location service or fetch in background. But the observer only reports the observation change not any actual data, I'm still not able to read any data because it is protected.Zepeda
@Thomas, can you add your Apple support ticket for the reference?Doit
@Tinaret your answer appears to miss the declaration of a function on HealthManager.swiftCamass
Will your app be invoked even if the user has explicitly killed the app?Mongolism
@Victor Sigler Can you please share the Objective C sample for this?Interatomic
@Tinaret can you update the complete HealthManager.swift file. Some point i cannot able to integrateJennette
L
0

Steps have a minimum update frequency of 1 hour, meaning your app will only get woken up once/hour. Once you open the app, your observer queries get fired off right away. See the note in the discussion for enableBackgroundDeliveryForType.

https://developer.apple.com/library/prerelease/ios/documentation/HealthKit/Reference/HKHealthStore_Class/index.html#//apple_ref/occ/instm/HKHealthStore/enableBackgroundDeliveryForType:frequency:withCompletion:

Lashondalashonde answered 6/3, 2015 at 20:1 Comment(4)
I read it a bit different, I understand the minimum update frequency as I should as a minimum get it once per hour, I can get it immediate if I want that, but I can not set the minimum to anything over once per hour, e.g once per day is not allowed. I have testet this and I get steps immediately when set the frequency to that.Zepeda
Well, does your experimentation bear that out? Is your app getting woken up immediately, or once an hour?Lashondalashonde
The observer gets notified immediately, but only if the application is awaken by other services, like location service or background fetch.Zepeda
Try disabling everything else that would wake up your app, adding a steps reading, then waiting an hour to see if the steps wake up your app after that hour. Should work!Lashondalashonde
D
0

In case of HKObserverQuery, you have to enable any of the background modes of the application to get notified for the ObserverQuery in background(application not killed).

enter image description here

Doit answered 19/3, 2015 at 13:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.