How can you display HealthKit data on a complication that is refreshed in the background?

I am trying to build a watchOS 2 complication that displays a user's health data when the watch is unlocked, such as steps (but in theory it should be able to display any health data the user has given the app permission to view). When the complication first launches, I can query Healthkit and get all the data I query because the first launch is considered to be in the foreground. However, I am having trouble retrieving the HealthKit data in the background when new health data is available. There are two places I could get this data, the watch and the iPhone.


I have tried to get the data from the iPhone using HKObserverQuery. I have the observer query that can wake up the iPhone once an hour (the max time for step data). However, if the iPhone is locked when the observer completion handler executes my step query, HKHealthStore's execute method refuses to return any query results. I think this makes sense here and there is probably not a way around this because Apple's docs mention that the Health Store is encrypted when a device is locked, so you can't read from the store (only write). BUT in the watch's case when it is on someones wrist it is not locked, the screen is just turned off.


So, I have tried to get the data from the watch itself when the complication's background refresh is triggered from the date set in getNextRequestedUpdateDateWithHandler. However, when I call HKHealthStore's execute method, it does not return any query results if the app (or in this case the complication) is running the background. Instead of calling the query directly I have also tried to setup an HKAnchoredObject query that should return my results immediately when the process resumes, but this also doesn't seem to return any results unless I manually launch the app extension on the watch. Here is my watch code, called from my ExtensionDelegate's init method after health kit permissions are requested:


func setupComplicationDataCache() {
        let now = NSDate()
        var startDate: NSDate? = nil
        var interval: NSTimeInterval = 0
    
        self.calendar.rangeOfUnit(NSCalendarUnit.Day, startDate: &startDate, interval: &interval, forDate: now)
        let stepSampleType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)!
    
        // Match samples with a start date after the workout start
        let predicate = HKQuery.predicateForSamplesWithStartDate(startDate, endDate: nil, options: .None)
        let query = HKAnchoredObjectQuery(type: stepSampleType, predicate: predicate, anchor: nil, limit: 0) { (query, samples, deletedObjects, anchor, error) -> Void in
            // Handle when the query first returns results
            self.handleStepResults(query, samples: samples, deletedObjects: deletedObjects, anchor: anchor, error: error)
        }
    
        query.updateHandler = { (query, samples, deletedObjects, anchor, error) -> Void in
            // Handle update notifications after the query has initially run
            self.handleStepResults(query, samples: samples, deletedObjects: deletedObjects, anchor: anchor, error: error)
        }
    
        self.healthStore.executeQuery(query);
    }

    func handleStepResults(query: HKAnchoredObjectQuery, samples: [HKSample]?, deletedObjects: [HKDeletedObject]?, anchor: HKQueryAnchor?, error: NSError?) {
        if error != nil {
            self.timelineModel.currentEntry = TimelineEntryModel(value: NSNumber(int: -1), endDate: NSDate())
        } else if samples == nil || samples?.count == 0 {
            self.timelineModel.currentEntry = TimelineEntryModel(value: NSNumber(int: 0), endDate: NSDate())
        } else {
            let newStepSamples = samples as! [HKQuantitySample]
            var stepCount = self.timelineModel.currentEntry.value.doubleValue
            var currentDate = self.timelineModel.currentEntry.endDate
        
            // Add the current entry to the collection of past entries
            self.timelineModel.pastEntries.append(self.timelineModel.currentEntry)
        
            // Add all new entries to the collection of past entries
            for result in newStepSamples {
                stepCount += result.quantity.doubleValueForUnit(self.countUnit)
                currentDate = result.endDate
                self.timelineModel.pastEntries.append(TimelineEntryModel(value: NSNumber(double: stepCount), endDate: currentDate))
            }
        
            // Retrieve the latest sample as the current item
            self.timelineModel.currentEntry = self.timelineModel.pastEntries.popLast()
            if self.timelineModel.currentEntry == nil {
                self.timelineModel.currentEntry = TimelineEntryModel(value: NSNumber(int: -3), endDate: NSDate())
            }
        }
    
        // Reload the complication
        let complicationServer = CLKComplicationServer.sharedInstance()
        for complication in complicationServer.activeComplications {
            complicationServer.reloadTimelineForComplication(complication)
        }
    }


This code works as long as the watch extension is in the forground. Does anyone know how to get HealthKit updates to show up on a complication when a refresh happens in the background, either in iOS or on watchOS 2?

I have gone through the exact same thought process and been working on this for a while.

So firstly, I dont have an answer. Ive been trying and trying but still no luck.


So heres what i know, there are apps, like pedometer++ that have a complication for steps and they work, ive tested them. their updating around about every hour which is fine.


I have tried, iOS background HKObserverQueries with HKSampleQueries and come unstuck with no access to health data on lock.

I have tried using a HKSampleQuery on the watch when ever the complication updates, this works the first time on the complication but never again, no return. I have an active developer support ticket on this one open with Apple, However I started that back in December and have just been told repeatedly 'The engineers are looking into it', so useless so far.

I have tried emailing the developer of pedometer++ to ask how he does it (no reply so far).

I am currently trying using an HKObserverQuery to the trigger transferCurrentComplicationUserInfo and then in the Extension delegate didRecieveUserInfo on the Watch Im implementing a HKSampleQuery there, no luck so far.


I really want to make this work, but keep hitting brick walls. I would have given up a while ago if it wasnt for the fact that some apps seem to have it nailed. My 4 month search for a solution to this problem continues!


Please do get in touch though on here with any findings, if i get a solution, I will pass it on!

Cheers

How can you display HealthKit data on a complication that is refreshed in the background?
 
 
Q