Hello,
I am writing as I am seeing a very strange behavior when attempting to run an HKStatisticsCollectionQuery over multiple app starts.
Steps:
- Initially load and launch my app into an iOS simulator (running iOS 17.2) via Xcode (version 15.3).
- Execute code path which invokes the following method below.
- Authorize necessary Read permission for the HKQuantityTypeIdentifierStepCount type.
- Observed that a non-nil HKStatisticsCollection is returned in the initialResultsHandler, with corresponding expected datapoints. These datapoints are in no way stored or retained on the device at all.
- Stop the app via Xcode.
- Relaunch the app via Xcode.
- Execute the same code path as in step 2.
- Observed (via breakpoints) that even though the query is executed by the HKHealthStore, the initialResultsHandler is never called and no HKStatisticsCollection is ever returned.
Input parameters are the same in both instances of the call, and can confirm there is data in the devices HealthKit datastore.
I would expect, reading Apple's documentation, that both queries should execute and return the exact same datapoints in an HKStatisticsCollection, but please feel free to correct me if I am misunderstanding something, or if my code is incorrect in some way.
func fetchStatisticsCollection(with quantityType: HKQuantityType,
predicate: NSPredicate? = nil,
options: HKStatisticsOptions,
anchorDate: Date? = nil,
interval: DateComponents? = nil,
initialResultsCompletion: @escaping (Result<HKStatisticsCollection, Error>) -> Void,
updateHandler: (Result<HKStatisticsCollection, Error>) -> Void?) {
var statisticsQueryAnchorDate: Date = Date()
if let anchor = anchorDate {
statisticsQueryAnchorDate = anchor
} else if let yesterdayAnchor = createStaticsQueryAnchorDateYesterday(){
statisticsQueryAnchorDate = yesterdayAnchor
}
let dateInterval = interval ?? DateComponents(day: 1)
// Create the query
let query = HKStatisticsCollectionQuery(quantityType: quantityType,
quantitySamplePredicate: predicate,
options: options,
anchorDate: statisticsQueryAnchorDate,
intervalComponents: dateInterval)
// Set the results handler
query.initialResultsHandler = { query, results, error in
if let error = error as? HKError{
// Return wrapped HKError
initialResultsCompletion(.failure(HealthKitManagerError.hkError(error: error)))
return
}
guard let statsCollection = results else {
// Return custom Empty Results error.
initialResultsCompletion(.failure(HealthKitManagerError.emptyResults))
return
}
initialResultsCompletion(.success(statsCollection))
}
if let updateHandler = updateHandler {
query.statisticsUpdateHandler = { query, statistics, statisticsCollection, error in
if let error = error as? HKError{
// Return custom wrapped HKError.
updateHandler(.failure(HealthKitManagerError.hkError(error: error)))
return
}
guard let statsCollection = statisticsCollection else {
// Return custom Empty Results error.
updateHandler(.failure(HealthKitManagerError.emptyResults))
return
}
updateHandler(.success(statsCollection))
}
}
if activeTypeQueries[quantityType.identifier] == nil {
healthStore.execute(query)
activeTypeQueries[quantityType.identifier] = query
healthStore.enableBackgroundDelivery(for: quantityType, frequency: .immediate) { (success, error) in
if let error = error {
return
}
if success {
print("background enabled")
}
}
} else {
print("NOT executing query, we already have one query running for this type")
}
}
Thank you