2

It seems HKStatisticsCollectionQuery is incredibly slow in iOS 9.3. It can take upwards of 40 seconds to return hourly statistics for a year for active calories where it took 1 or less before.

let predicate = HKQuery.predicateForSamplesWithStartDate(anchorDate, endDate: endDate, options: [])
    let query = HKStatisticsCollectionQuery(quantityType: quantityType,
        quantitySamplePredicate: predicate,
        options: statisticOptions,
        anchorDate: anchorDate,
        intervalComponents: interval)
jestro
  • 2,524
  • 4
  • 27
  • 46

2 Answers2

1

After many hours of trial and error I have found that HKStatisticsCollectionQuery is not thread friendly. In order to solve the problem I used this async NSOperation: https://gist.github.com/calebd/93fa347397cec5f88233

And of course an NSOperationQueue in order to force the HKStatisticsCollectionQuerys to be performed synchronously. Once I did that each query took less than half a second.

jestro
  • 2,524
  • 4
  • 27
  • 46
  • can you please explain how exactly to use this class? Here is the blog link http://swiftgazelle.com/2016/03/asynchronous-nsoperation-why-and-how/ but don't know how to execute query with this class for fetching data. – Hiren Gujarati Apr 25 '17 at 19:00
1

If someone has troubles with long HKStatisticsCollectionQuery request performing (dozens of seconds or minutes) please consider you have set proper dates for your predicate and anchor. The problem is a lot of data that HealthKit should calculate.

For example your goal is gathering data for today:

let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate)
    let query = HKStatisticsCollectionQuery(quantityType: quantityType,
                                            quantitySamplePredicate: predicate,
                                            options: .cumulativeSum,
                                            anchorDate: anchorDate,
                                            intervalComponents: interval)

Here startDate is today's beginning, endDate is beginning for tomorrow, anchorDate is some day's beginning - in my case it's equal to endDate.

Important notice: if you pass nil instead of predicate, HealthKit will gather all data since the beginning and waste your time as a result. Why tomorrow's beginning is necessary as endDate? The reason is statisticsUpdateHandler. Since you have decided to listen to data updates in HealthKit, predicate's end date should be in a future, not Date(). If you don't want to use statisticsUpdateHandler you can set endDate as now.