Random iOS App Crashes with Health Kit (^4.1.1) Version
Random iOS App Crashes with Health Kit (^4.1.1) Version
Issue: The iOS app is experiencing random crashes when attempting to fetch data from Health Kit using the ^4.1.1 version. The root cause appears to be related to the getData function in SWIFT Code, which is responsible for fetching all health points. The issue is suspected to be linked to the handling of multiple threads within the main thread, leading to unexpected behavior. If you want to fetch data in the background then you have to check all safety measures to call this thread otherwise call this function in the main thread.
Here is the updated function:
`func getData(call: FlutterMethodCall, result: @escaping FlutterResult) { let arguments = call.arguments as? NSDictionary let dataTypeKey = (arguments?["dataTypeKey"] as? String)! let dataUnitKey = (arguments?["dataUnitKey"] as? String) let startTime = (arguments?["startTime"] as? NSNumber) ?? 0 let endTime = (arguments?["endTime"] as? NSNumber) ?? 0 let limit = (arguments?["limit"] as? Int) ?? HKObjectQueryNoLimit
// Convert dates from milliseconds to Date()
let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000)
let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000)
let dataType = dataTypeLookUp(key: dataTypeKey)
let predicate = HKQuery.predicateForSamples(withStart: dateFrom, end: dateTo, options: .strictStartDate)
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
let query = HKSampleQuery(sampleType: dataType, predicate: predicate, limit: limit, sortDescriptors: [sortDescriptor]) { [self]
x, samplesOrNil, error in
switch samplesOrNil {
case let (samples as [HKQuantitySample]) as Any:
weak var weakSelf = self
DispatchQueue.main.async {
if let weakSelf = weakSelf {
let unit = weakSelf.unitDict[dataUnitKey!]
let dictionaries = samples.map { sample -> NSDictionary in
return [
"uuid": "\(sample.uuid)",
"value": sample.quantity.doubleValue(for: unit!),
"date_from": Int(sample.startDate.timeIntervalSince1970 * 1000),
"date_to": Int(sample.endDate.timeIntervalSince1970 * 1000),
"source_id": sample.sourceRevision.source.bundleIdentifier,
"source_name": [sample.sourceRevision.source.name](http://sample.sourcerevision.source.name/)
]
}
result(dictionaries)
}
}
case var (samplesCategory as [HKCategorySample]) as Any:
if (dataTypeKey == self.SLEEP_IN_BED) {
samplesCategory = samplesCategory.filter { $0.value == 0 }
}
if (dataTypeKey == self.SLEEP_ASLEEP) {
samplesCategory = samplesCategory.filter { $0.value == 1 }
}
if (dataTypeKey == self.SLEEP_AWAKE) {
samplesCategory = samplesCategory.filter { $0.value == 2 }
}
if (dataTypeKey == self.HEADACHE_UNSPECIFIED) {
samplesCategory = samplesCategory.filter { $0.value == 0 }
}
if (dataTypeKey == self.HEADACHE_NOT_PRESENT) {
samplesCategory = samplesCategory.filter { $0.value == 1 }
}
if (dataTypeKey == self.HEADACHE_MILD) {
samplesCategory = samplesCategory.filter { $0.value == 2 }
}
if (dataTypeKey == self.HEADACHE_MODERATE) {
samplesCategory = samplesCategory.filter { $0.value == 3 }
}
if (dataTypeKey == self.HEADACHE_SEVERE) {
samplesCategory = samplesCategory.filter { $0.value == 4 }
}
let categories = samplesCategory.map { sample -> NSDictionary in
return [
"uuid": "\(sample.uuid)",
"value": sample.value,
"date_from": Int(sample.startDate.timeIntervalSince1970 * 1000),
"date_to": Int(sample.endDate.timeIntervalSince1970 * 1000),
"source_id": sample.sourceRevision.source.bundleIdentifier,
"source_name": [sample.sourceRevision.source.name](http://sample.sourcerevision.source.name/)
]
}
DispatchQueue.main.async {
result(categories)
}
case let (samplesWorkout as [HKWorkout]) as Any:
let dictionaries = samplesWorkout.map { sample -> NSDictionary in
return [
"uuid": "\(sample.uuid)",
"workoutActivityType": workoutActivityTypeMap.first(where: {$0.value == sample.workoutActivityType})?.key,
"totalEnergyBurned": sample.totalEnergyBurned?.doubleValue(for: HKUnit.kilocalorie()),
"totalEnergyBurnedUnit": "KILOCALORIE",
"totalDistance": sample.totalDistance?.doubleValue(for: HKUnit.meter()),
"totalDistanceUnit": "METER",
"date_from": Int(sample.startDate.timeIntervalSince1970 * 1000),
"date_to": Int(sample.endDate.timeIntervalSince1970 * 1000),
"source_id": sample.sourceRevision.source.bundleIdentifier,
"source_name": [sample.sourceRevision.source.name](http://sample.sourcerevision.source.name/)
]
}
DispatchQueue.main.async {
result(dictionaries)
}
case let (samplesAudiogram as [HKAudiogramSample]) as Any:
let dictionaries = samplesAudiogram.map { sample -> NSDictionary in
var frequencies = [Double]()
var leftEarSensitivities = [Double]()
var rightEarSensitivities = [Double]()
for samplePoint in sample.sensitivityPoints {
frequencies.append(samplePoint.frequency.doubleValue(for: HKUnit.hertz()))
leftEarSensitivities.append(samplePoint.leftEarSensitivity!.doubleValue(for: HKUnit.decibelHearingLevel()))
rightEarSensitivities.append(samplePoint.rightEarSensitivity!.doubleValue(for: HKUnit.decibelHearingLevel()))
}
return [
"uuid": "\(sample.uuid)",
"frequencies": frequencies,
"leftEarSensitivities": leftEarSensitivities,
"rightEarSensitivities": rightEarSensitivities,
"date_from": Int(sample.startDate.timeIntervalSince1970 * 1000),
"date_to": Int(sample.endDate.timeIntervalSince1970 * 1000),
"source_id": sample.sourceRevision.source.bundleIdentifier,
"source_name": [sample.sourceRevision.source.name](http://sample.sourcerevision.source.name/)
]
}
DispatchQueue.main.async {
result(dictionaries)
}
default:
DispatchQueue.main.async {
result(nil)
}
}
}
HKHealthStore().execute(query)
}`
Hi, since this includes a fix to the issue, please create a separate PR and link it to this issue!