How to Force a Hkquery to Load the Most Recent Steps Counts

How to force a HKQuery to load the most recent steps counts?

HealthKit does not always have an up-to-date count of the user's steps and distance travelled. It imports these values, which actually come from CoreMotion.framework, periodically and in response to certain events. If a running application has an open HKObserverQuery or HKStatisticsCollectionQuery then HealthKit will stream the values to the client but otherwise the samples are just a snapshot from the last import.

So if you'd like to observe changes for a sample type, you should subscribe to updates using an HKObserverQuery and then query HealthKit again for the latest values. A more efficient approach would be to use HKStatisticsCollectionQuery, though, which has an update handler that will be invoked as the statistics for the samples matching the predicates change.

Finally, if you're only interested up-to-date step counts or distance travelled for at most the past 7 days then I recommend that you consider using CoreMotion.framework directly instead.

HealthKit Swift getting today's steps

Here is the right way using HKStatisticsCollectionQuery courtesy of the direction from the code above.

This is written in Swift 3 so you may have to convert some of the code back to 2.3 or 2 if not on 3 yet.

Swift 3

 func retrieveStepCount(completion: (stepRetrieved: Double) -> Void) {

// Define the Step Quantity Type
let stepsCount = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)

// Get the start of the day
let date = Date()
let cal = Calendar(identifier: Calendar.Identifier.gregorian)
let newDate = cal.startOfDay(for: date)

// Set the Predicates & Interval
let predicate = HKQuery.predicateForSamples(withStart: newDate, end: Date(), options: .strictStartDate)
var interval = DateComponents()
interval.day = 1

// Perform the Query
let query = HKStatisticsCollectionQuery(quantityType: stepsCount!, quantitySamplePredicate: predicate, options: [.cumulativeSum], anchorDate: newDate as Date, intervalComponents:interval)

query.initialResultsHandler = { query, results, error in

if error != nil {

// Something went Wrong
return
}

if let myResults = results{
myResults.enumerateStatistics(from: self.yesterday, to: self.today) {
statistics, stop in

if let quantity = statistics.sumQuantity() {

let steps = quantity.doubleValue(for: HKUnit.count())

print("Steps = \(steps)")
completion(stepRetrieved: steps)

}
}
}


}

storage.execute(query)
}

Objective-C

HKQuantityType *type = [HKSampleType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];

NSDate *today = [NSDate date];
NSDate *startOfDay = [[NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian] startOfDayForDate:today];

NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startOfDay endDate:today options:HKQueryOptionStrictStartDate];
NSDateComponents *interval = [[NSDateComponents alloc] init];
interval.day = 1;

HKStatisticsCollectionQuery *query = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:type quantitySamplePredicate:predicate options:HKStatisticsOptionCumulativeSum anchorDate:startOfDay intervalComponents:interval];

query.initialResultsHandler = ^(HKStatisticsCollectionQuery * _Nonnull query, HKStatisticsCollection * _Nullable result, NSError * _Nullable error) {
if (error != nil) {
// TODO
} else {
[result enumerateStatisticsFromDate:startOfDay toDate:today withBlock:^(HKStatistics * _Nonnull result, BOOL * _Nonnull stop) {
HKQuantity *quantity = [result sumQuantity];
double steps = [quantity doubleValueForUnit:[HKUnit countUnit]];
NSLog(@"Steps : %f", steps);
}];
}
};

[self.storage executeQuery:query];

Get the latest (most recent) Step value from HealthKit (HKStatistics)

If you want to use just quantities, etc. Just use the HKSampleQuery instead of the HKStatisticsQuery.

After that, you just have to cast the result list of HKSample to a [HKQuantitySample].

Now you have the start and end date but also the quantity.

Getting all steps of today but truncating manually added steps, from Health kit using swift

This might not be the best solution, But I believe it will work. What you can do is get all the steps which were added manually using HKSampleQuery. here is an example.

 func todayManuallyAddedStepsSteps(completion: (Double, NSError?) -> () )
{

let type = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount) // The type of data we are requesting

let date = NSDate()
let cal = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let newDate = cal.startOfDayForDate(date)
let predicate = HKQuery.predicateForSamplesWithStartDate(newDate, endDate: NSDate(), options: .None) // Our search predicate which will fetch all steps taken today

// The actual HealthKit Query which will fetch all of the steps and add them up for us.

let query = HKSampleQuery(sampleType: type!, predicate: predicate, limit: 0, sortDescriptors: nil) { query, results, error in
var steps: Double = 0

if results?.count > 0
{
for result in results as! [HKQuantitySample]
{
print("Steps \(result.quantity.doubleValueForUnit(HKUnit.countUnit()))")
print()

// checking and adding manually added steps
if result.sourceRevision.source.name == "Health" {
// these are manually added steps
print(result.sourceRevision.source.name)
print("Steps \(result.quantity.doubleValueForUnit(HKUnit.countUnit()))")
steps += result.quantity.doubleValueForUnit(HKUnit.countUnit())
}
else{
// these are auto detected steps which we do not want from using HKSampleQuery
}
}
print(steps)
}
completion(steps, error)
}

executeQuery(query)
}

and then get the today total steps using HKStatisticsCollectionQuery like below

func TodayTotalSteps(completion: (stepRetrieved: Double) -> Void) {

let type = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount) // The type of data we are requesting

let calendar = NSCalendar.currentCalendar()
let interval = NSDateComponents()
interval.day = 1

let anchorComponents = calendar.components([.Day , .Month , .Year], fromDate: NSDate())
anchorComponents.hour = 0
let anchorDate = calendar.dateFromComponents(anchorComponents)

let stepsQuery = HKStatisticsCollectionQuery(quantityType: type!, quantitySamplePredicate: nil, options: .CumulativeSum, anchorDate: anchorDate!, intervalComponents: interval)

stepsQuery.initialResultsHandler = {query, results, error in
let endDate = NSDate()
let startDate = calendar.dateByAddingUnit(.Day, value: 0, toDate: endDate, options: [])
if let myResults = results{ myResults.enumerateStatisticsFromDate(startDate!, toDate: endDate) { statistics, stop in
if let quantity = statistics.sumQuantity(){
let date = statistics.startDate
let steps = quantity.doubleValueForUnit(HKUnit.countUnit())
print("\(date): steps = \(steps)")
completion(stepRetrieved: steps)
}
}
}
}
executeQuery(stepsQuery)
}

Now you can call these methods and subtract manually added steps like below

todayManuallyAddedSteps({ (steps , error) in
if error != nil{
print(error)
}
else{
// truncating manuall steps
TodayTotalSteps({ (stepRetrieved) in
// steps without manuall steps
print(Int(stepRetrieved - steps))

})
}
})

Getting yesterdays steps from HealthKit

This is the method I am using in my healthStore class

    func TodayTotalSteps(completion: (stepRetrieved: Double) -> Void) {

let type = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount) // The type of data we are requesting

let calendar = NSCalendar.currentCalendar()
let interval = NSDateComponents()
interval.day = 1

let anchorComponents = calendar.components([.Day , .Month , .Year], fromDate: NSDate())
anchorComponents.hour = 0
let anchorDate = calendar.dateFromComponents(anchorComponents)

let stepsQuery = HKStatisticsCollectionQuery(quantityType: type!, quantitySamplePredicate: nil, options: .CumulativeSum, anchorDate: anchorDate!, intervalComponents: interval)

stepsQuery.initialResultsHandler = {query, results, error in
let endDate = NSDate()

var steps = 0.0
let startDate = calendar.dateByAddingUnit(.Day, value: 0, toDate: endDate, options: [])
if let myResults = results{ myResults.enumerateStatisticsFromDate(startDate!, toDate: endDate) { statistics, stop in
if let quantity = statistics.sumQuantity(){
let date = statistics.startDate
steps = quantity.doubleValueForUnit(HKUnit.countUnit())
// print("\(date): steps = \(steps)")
}

completion(stepRetrieved: steps)
}
} else {

completion(stepRetrieved: steps)
}
}
executeQuery(stepsQuery)
}

and here is How I am using it

func getStepsData() {

// I am sendng steps to my server thats why using this variable
var stepsToSend = 0

MyHealthStore.sharedHealthStore.todayManuallyAddedSteps({ (steps , error) in
if error != nil{
// handle error
}
else{
// truncating manuall steps
MyHealthStore.sharedHealthStore.TodayTotalSteps({ (stepRetrieved) in
stepsToSend = Int(stepRetrieved - steps)
})
}
})
}

and here is the function used above for manually added steps which we are truncating in order to get exact steps

    func todayManuallyAddedSteps(completion: (Double, NSError?) -> () )
{
let type = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount) // The type of data we are requesting

let date = NSDate()
let cal = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let newDate = cal.startOfDayForDate(date)
let predicate = HKQuery.predicateForSamplesWithStartDate(newDate, endDate: NSDate(), options: .None) // Our search predicate which will fetch all steps taken today

// The actual HealthKit Query which will fetch all of the steps and add them up for us.

let query = HKSampleQuery(sampleType: type!, predicate: predicate, limit: 0, sortDescriptors: nil) { query, results, error in
var steps: Double = 0

if results?.count > 0
{
for result in results as! [HKQuantitySample]
{

// checking and adding manually added steps
if result.sourceRevision.source.name == "Health" {
// these are manually added steps

steps += result.quantity.doubleValueForUnit(HKUnit.countUnit())
}
else{
// these are auto detected steps which we do not want from using HKSampleQuery
}
}
completion(steps, error)
} else {
completion(steps, error)
}
}
executeQuery(query)
}

I hope it helps. Let me know if you face any issue.

How to obtain Health data from the last 7 days

Try using HKStatisticsCollectionQuery, which will do the date math for you and bucket the results automatically. Here's an example that should provide the step counts for the last 7 days:

    let stepsQuantityType = HKQuantityType.quantityType(forIdentifier: .stepCount)!

let now = Date()
let exactlySevenDaysAgo = Calendar.current.date(byAdding: DateComponents(day: -7), to: now)!
let startOfSevenDaysAgo = Calendar.current.startOfDay(for: exactlySevenDaysAgo)
let predicate = HKQuery.predicateForSamples(withStart: startOfSevenDaysAgo, end: now, options: .strictStartDate)

let query = HKStatisticsCollectionQuery.init(quantityType: stepsQuantityType,
quantitySamplePredicate: predicate,
options: .cumulativeSum,
anchorDate: startOfSevenDaysAgo,
intervalComponents: DateComponents(day: 1))

query.initialResultsHandler = { query, results, error in
guard let statsCollection = results else {
// Perform proper error handling here...
}

statsCollection.enumerateStatistics(from: startOfSevenDaysAgo, to: now) { statistics, stop in

if let quantity = statistics.sumQuantity() {
let stepValue = quantity.doubleValueForUnit(HKUnit.countUnit())
// ...
}
}
}

HealthKit not returning updated info

You are displaying only the quantity of the last sample.

Lets assume you have 100 samples.
You're updating your label 100 times with a different quantity value and what you are seeing is the quantity value of the last update.

To see the sum of all selected samples, you have to sum up all quantity values of all (relevant) samples.



Related Topics



Leave a reply



Submit