flutter-plugins icon indicating copy to clipboard operation
flutter-plugins copied to clipboard

Random iOS App Crashes with Health Kit (^4.1.1) Version

Open alihassan711 opened this issue 2 years ago • 1 comments

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)
}`

alihassan711 avatar Jan 05 '24 15:01 alihassan711

Hi, since this includes a fix to the issue, please create a separate PR and link it to this issue!

hoffmatteo avatar Jan 29 '24 17:01 hoffmatteo