In my app the the statisticsUpdateHandler fetches data from the HealthKit when I open the app from sleeping/standby state. This works fine, except when I leave the app running in the foreground and close the phone for an extended period. This triggers the .errorDatabaseInaccessible error. This seems to stop the statisticsUpdateHandler, maybe set it to nil or something similar. What is the proper way to handle the error to keep it running? As shown in the code below I have tried setting the completion data to the current data. But it stills seems to exit, and thus require a complete restart of the app to re-register the update handler.
Thanks Tommy
Task {
do {
try await healthStore.requestAuthorization(toShare: [], read:healtTypes)
fetchPast14daysData()
} catch {
print("Error requesting access to health data")
}
}
func fetchDiscreteData(startDate: Date, type: HKQuantityTypeIdentifier, completion: @escaping([WorkoutMetric]) -> Void) { let datatype = HKQuantityType(type) let interval = DateComponents(day: 1) let query = HKStatisticsCollectionQuery(quantityType: datatype, quantitySamplePredicate: nil, options: .discreteAverage, anchorDate: startDate, intervalComponents: interval)
query.initialResultsHandler = { query, result, error in
...
}
query.statisticsUpdateHandler = { query, statistics, result, error in
var lock_error_detected: Bool = false
// Handle errors here.
if let error = error as? HKError {
switch (error.code) {
case .errorDatabaseInaccessible:
lock_error_detected = true
default:
// Handle other HealthKit errors here.
DispatchQueue.main.async {
self.update_error = error.userInfo[NSLocalizedDescriptionKey] as? String
}
return
}
}
var data = [WorkoutMetric]()
if lock_error_detected {
if (type == .heartRateVariabilitySDNN) {
data = self.HRV
} else {
data = self.restingPulse
}
} else {
guard let result = result else {
completion([])
return
}
result.enumerateStatistics(from: startDate, to: Date()) { statistics, stop in
if ( type == .heartRateVariabilitySDNN)
{
let value = Float(statistics.averageQuantity()?.doubleValue(for: .secondUnit(with: .milli)) ?? 0.0)
//print("HRV data \(value)")
if (value > 1)
{
data.append(WorkoutMetric(date: statistics.startDate, Value: value, Average: 0.0))
}
}
else if ( type == .restingHeartRate)
{
let value = Float(statistics.averageQuantity()?.doubleValue(for: .hertzUnit(with: .none)) ?? 0.0)
//print("Resting hearth rate : \(value) at date \(statistics.startDate)")
if (value > 0.01)
{
data.append(WorkoutMetric(date: statistics.startDate, Value: value*60, Average: 0.0))
}
}
}
let _ = calculate_average(days: self.averagePeriod, workouts: &data)
}
completion(data)
}
healthStore.execute(query)
}
extension HealthManager { func fetchPast14daysData() { fetchCumulativeData(startDate: Date().daysAgoAligned(days: daysToFetchDataFor), type: .distanceWalkingRunning) { dailyDistance in DispatchQueue.main.async { self.distanceData = dailyDistance } .. ..
I will answer this myself.
Solution 1 is to throw fatalError in the case .errorDatabaseInaccessible: statement. This will shutdown the app. But it is not a desirable solution as really forces a crash.
Solution 2 is the reprogram the setup to use HKStatisticsCollectionQueryDescriptor and concurrency instead of completion and statisticsUpdateHandler. Wrapping a "for try await" in a repeat and do loop it is possible to catch errors from the try statement and continue the execution in the repeat loop with a continue statement.