1

I am having trouble getting the latest datapoint for weight using an HKSampleQuery. I have the app permissions set correctly, but HKQuantityTypeIdentifier.bodyMass is not returning the most recent data entry from the Health app.

How am I supposed to grab the latest datapoint for body mass using an HKSampleQuery?

The reason I think this is because the 0.0 I set for Weight is what is returning and I am getting no console output on readWeight


Edit 1

My code including the debugging process is as follows.

public func readWeight(result: @escaping (Double) -> Void) {
    if (debug){print("Weight")}
    let quantityType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bodyMass)

    let weightQuery = HKSampleQuery(sampleType: quantityType!, predicate: nil, limit: 1, sortDescriptors: nil) {

        query, results, error in

        if (error != nil) {
            if (self.debug){print(error!)}
            result(166.2) //Set as average weight for American
            return
        }

        guard let results = results else {
            if (self.debug){print("No results of query")}
            result(166.2)
            return
        }

        if (results.count == 0) {
            if (self.debug){print("Zero samples")}
            result(166.2)
            return
        }

        guard let bodymass = results.first as? HKQuantitySample else {
            if (self.debug){print("Type problem with weight")}
            result(166.2)
            return
        }

        if (self.debug){print("Weight" + String(bodymass.quantity.doubleValue(for: HKUnit.pound())))}

        if (bodymass.quantity.doubleValue(for: HKUnit.pound()) != 0.0) {
            result(bodymass.quantity.doubleValue(for: HKUnit.pound()))
        } else {
            result(166.2)
        }
    }

    healthKitStore.execute(weightQuery)
}

The function is used like this:

var Weight = 0.0 //The probable reason that it returns 0.0
readWeight() { weight in
    Weight = weight
}

Edit 2

Permission Code:

    let healthKitTypesToRead : Set<HKQuantityType> = [
        HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.dietaryWater)!,
        HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bodyMass)!,
        HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.appleExerciseTime)!
    ]

    let healthKitTypesToWrite: Set<HKQuantityType> = [
        HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.dietaryWater)!
    ]

    if (!HKHealthStore.isHealthDataAvailable()) {
        if (self.debug){print("Error: HealthKit is not available in this Device")}
        return
    }

    healthKitStore.requestAuthorization(toShare: healthKitTypesToWrite, read: healthKitTypesToRead) { (success, error) -> Void in
        if (success) {
            DispatchQueue.main.async() {
                self.pointView.text = String(self.currentPoints())
            }
        }

        if ((error) != nil) {
            if (self.debug){print(error!)}
            return
        }
Sam Spencer
  • 8,492
  • 12
  • 76
  • 133
arodebaugh
  • 538
  • 1
  • 8
  • 26
  • Please include your code and a description of the debugging you’ve done so far. – Allan Jul 02 '17 at 18:14
  • Gotcha @Allan let me edit my question – arodebaugh Jul 03 '17 at 12:35
  • @Allan done btw I also used breakpoints to try to debug it – arodebaugh Jul 03 '17 at 12:40
  • @arodebaugh For future reference, please do not use the [tag:swift3] tag (or any other language & version tag) if your question is not specifically about changes made to the language itself between versions. Instead, you can specify the language you are using in the question body. – Sam Spencer Jul 07 '17 at 16:43

1 Answers1

8

As explained in the HealthKit documentation (which I strongly urge you to read in its entirety), an HKSampleQuery makes no guarantees about the samples it returns or the order in which it returns them unless you specify how the samples should be returned.

For your case, returning the most recent data point can be done in a number of ways. Take a look at HKSampleQuery and the following method:

init(sampleType:predicate:limit:sortDescriptors:resultsHandler:)

You can provide a sort order for the returned samples, or limit the number of samples returned.

-- HKSampleQuery Documentation

In your code, you have appropriately limited the query so that it only returns one sample. This is correct and avoids unnecessary overhead in your use case. However, your code specifies nil for the sortDescriptors parameter. This means that the query can return samples in whatever order it pleases (thus, the single sample being returned to you is usually not what you're looking for).

An array of sort descriptors that specify the order of the results returned by this query. Pass nil if you don’t need the results in a specific order.

Note
HealthKit defines a number of sort identifiers (for example, HKSampleSortIdentifierStartDateand HKWorkoutSortIdentifierDuration). Use the sort descriptors you create with these identifiers only in queries. You cannot use them to perform an in-memory sort of an array of samples.

-- HKSampleQuery.init(...) Documentation

So, the solution then, is to simply provide a sort descriptor that asks the HKSampleQuery to order samples by date in descending order (meaning the most recent one will be first in a list).


I hope that the answer above is more helpful than a simple copy/paste of the code you need to fix the issue. Even so, the code to provide the correct sample for this specific use case is below:

// Create an NSSortDescriptor
let sort = [
    // We want descending order to get the most recent date FIRST
     NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false)
]

let weightQuery = HKSampleQuery(sampleType: quantityType!, predicate: nil, limit: 1, sortDescriptors: sort) {
    // Handle errors and returned samples...
}
Sam Spencer
  • 8,492
  • 12
  • 76
  • 133
  • Thank you so so so much :D Finally :D – arodebaugh Jul 08 '17 at 15:51
  • Any reason that this would only pull down results from the past 7 days? https://stackoverflow.com/questions/48717614/hksamplequery-will-only-return-values-from-past-7-days – GarySabo Feb 10 '18 at 16:07