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.

Juan Catalan
  • 2,299
  • 1
  • 17
  • 23
Thomas
  • 131
  • 2
  • 5

4 Answers4

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.

jeongmin.cha
  • 768
  • 8
  • 22
  • 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). – lehn0058 Jan 06 '16 at 18:46
  • 1
    When the phone is unlocked and my app is in the background on iOS 12, I am able to query the HealthKit store. – Joshua C. Lerner Jan 27 '19 at 13:29
  • 1
    @JoshuaC.Lerner You need to know that this answer was written in 2014. There might be some changes in HealthKit. – jeongmin.cha Mar 17 '19 at 13:33
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()         
}


}
Victor Sigler
  • 23,243
  • 14
  • 88
  • 105
cmollis
  • 137
  • 3
  • 1
    I should note that this works even when the app isn't running, provided you have the correct permissions. – cmollis Mar 02 '15 at 20:39
  • 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.. – Thomas Mar 03 '15 at 11:03
  • 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. – cmollis Mar 04 '15 at 19:06
  • 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. – Thomas Mar 09 '15 at 14:57
  • @Thomas, can you add your Apple support ticket for the reference? – Bhanu Prakash Mar 19 '15 at 06:04
  • @cmollis your answer appears to miss the declaration of a function on HealthManager.swift – Hugo Alonso May 28 '15 at 20:53
  • Will your app be invoked even if the user has explicitly killed the app? – Pratik Stephen Nov 22 '15 at 07:41
  • @Victor Sigler Can you please share the Objective C sample for this? – Vidhya Sri Jul 10 '18 at 13:25
  • @cmollis can you update the complete HealthManager.swift file. Some point i cannot able to integrate – Apple Oct 30 '18 at 14:21
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:

drdaanger
  • 350
  • 1
  • 10
  • 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. – Thomas Mar 09 '15 at 14:51
  • Well, does your experimentation bear that out? Is your app getting woken up immediately, or once an hour? – drdaanger Mar 10 '15 at 16:30
  • The observer gets notified immediately, but only if the application is awaken by other services, like location service or background fetch. – Thomas Mar 11 '15 at 08:26
  • 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! – drdaanger Mar 12 '15 at 12:45
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

Bhanu Prakash
  • 1,493
  • 8
  • 20