Crash on fetching health kit data

I am receiving the following crash when trying to query data from health kit. It seems to be complaining about end date being before start date however with the values I am passing in that is not possible. Also I tried to purposefully pass in end dates before start date and I still was not able to reproduce this crash. I have attached my code snippet and the crash logs here.

Please Help :(

self.statisticfetcher(
    type: HKObjectType.quantityType(forIdentifier: .bodyMass),
    options: .discreteAverage,
    metricType: .bodyWeight,
    statsHandler: 
{ stats  in
        if let quantity = stats.averageQuantity() {
            ...
        }
        return nil
})

private func statisticfetcher(
    type: HKQuantityType?,
    options: HKStatisticsOptions,
    metricType: HealthMetricType,
    statsHandler: @escaping (HKStatistics) -> BASchema.HealthDataEntryInput?,
    resultHandler: ((HKStatisticsCollection?, Date, Date, Error?) -> Void)? = nil) 
{
    let res = getStartEndDate()
    let endDate =  res.endDate
    let startDate = res.startDate
    guard let type = type else {
        return
    }

    let query = HKStatisticsCollectionQuery(quantityType: type,
                                            quantitySamplePredicate: nil,
                                            options: options,
                                            anchorDate: startDate,
                                            intervalComponents: DateComponents(day: 1))
    
    let defaultResultHandler: (HKStatisticsCollection?, Date, Date, Error?) -> Void = { statCollection, startDate, endDate, error in
        if let error = error {
            return
        }
        guard let statCollection = statCollection else {
            return
        }
        statCollection.enumerateStatistics(from: startDate, to: endDate) { stats, _ in
            ...
        }
    }

    let resultHandler = resultHandler ?? defaultResultHandler
    query.initialResultsHandler = { _, statCollection, error in
        resultHandler(statCollection, startDate, endDate, error)
    }

    query.statisticsUpdateHandler = { _, _, statCollection, error in
        resultHandler(statCollection, startDate, endDate, error)
    }

    self.store.execute(query)
    self.subscribeToBackgroundDelivery(type: type)
}

private func subscribeToBackgroundDelivery(type: HKObjectType) {
    store.enableBackgroundDelivery(for: type, frequency: .immediate) { success, error in
        if(error != nil && !success) { print("Background Delivery for \(type) Failed!") }
    }
}```




Fatal Exception: NSInternalInconsistencyException 0 CoreFoundation 0x9cb4 __exceptionPreprocess 1 libobjc.A.dylib 0x183d0 objc_exception_throw 2 Foundation 0x4e156c _userInfoForFileAndLine 3 HealthKit 0xe0d2c -[HKDateInterval initWithStartDate:endDate:] 4 HealthKit 0xc7a5c -[NSDateComponents(HealthKit) hk_dateIntervalForDate:anchorDate:outIndex:] 5 HealthKit 0x1a0f98 -[HKStatisticsCollection _statisticsDateIntervalAndIndex:forDate:] 6 HealthKit 0x1a0420 -[HKStatisticsCollection _insertStatistics:] 7 HealthKit 0x1a059c -[HKStatisticsCollection _resetStatistics:] 8 HealthKit 0x19efc8 -[HKStatisticsCollectionQuery _queue_deliverResetStatisticsObjects:forQuery:] 9 HealthKit 0x19e8bc __98-[HKStatisticsCollectionQuery client_deliverStatisticsBatch:resetStatistics:isFinal⚓ query:]_block_invoke 10 libdispatch.dylib 0x2320 _dispatch_call_block_and_release 11 libdispatch.dylib 0x3eac _dispatch_client_callout 12 libdispatch.dylib 0xb534 _dispatch_lane_serial_drain 13 libdispatch.dylib 0xc0d8 _dispatch_lane_invoke 14 libdispatch.dylib 0x16cdc _dispatch_workloop_worker_thread 15 libsystem_pthread.dylib 0xddc _pthread_wqthread 16 libsystem_pthread.dylib 0xb7c start_wqthread

  • Could you show two actual date values that cause the issue to appear?

  • Got the same issue for quite some time. I have a guard statement before the query execution that ensures that the start date is before the end date and it still crashes for a small amount of users.

Add a Comment

Replies

Crash details: Fatal Exception: NSInternalInconsistencyException Invalid parameter not satisfying: [startDate hk_isBeforeOrEqualToDate:endDate]

Fatal Exception: NSInternalInconsistencyException 0 CoreFoundation 0x9cb4 __exceptionPreprocess 1 libobjc.A.dylib 0x183d0 objc_exception_throw 2 Foundation 0x4e156c _userInfoForFileAndLine 3 HealthKit 0xe0d2c -[HKDateInterval initWithStartDate:endDate:] 4 HealthKit 0xc7a5c -[NSDateComponents(HealthKit) hk_dateIntervalForDate:anchorDate:outIndex:] 5 HealthKit 0x1a0f98 -[HKStatisticsCollection _statisticsDateIntervalAndIndex:forDate:] 6 HealthKit 0x1a0420 -[HKStatisticsCollection _insertStatistics:] 7 HealthKit 0x1a059c -[HKStatisticsCollection _resetStatistics:] 8 HealthKit 0x19efc8 -[HKStatisticsCollectionQuery _queue_deliverResetStatisticsObjects:forQuery:] 9 HealthKit 0x19e8bc __98-[HKStatisticsCollectionQuery client_deliverStatisticsBatch:resetStatistics:isFinal⚓ query:]_block_invoke 10 libdispatch.dylib 0x2320 _dispatch_call_block_and_release 11 libdispatch.dylib 0x3eac _dispatch_client_callout 12 libdispatch.dylib 0xb534 _dispatch_lane_serial_drain 13 libdispatch.dylib 0xc0d8 _dispatch_lane_invoke 14 libdispatch.dylib 0x16cdc _dispatch_workloop_worker_thread 15 libsystem_pthread.dylib 0xddc _pthread_wqthread 16 libsystem_pthread.dylib 0xb7c start_wqthread

I've faced a similar issue while fetching today's steps from HealthKit. I've managed to fix it by creating a predicate using HKQuery.predicateForSamples(withStart:end:options:) and using the .strictStartDate query option. The predicate is then passed to the HKStatisticsCollectionQuery initialiser. Here's an example how to fetch today's current steps and subscribe to updates with this approach:

/// Fetches today's total steps and subscribes to all future changes of this value
/// - Parameter onUpdate: callback which is called on the initial fetch and every time the value changes in the future
func subscribeToStepUpdates(onUpdate: @escaping (Int?) -> Void) {
    func finishWithResult(_ value: Int?) {
        DispatchQueue.main.async {
            onUpdate(value)
        }
    }

    guard let type = HKQuantityType.quantityType(forIdentifier: .stepCount) else {
        finishWithResult(nil)
        return
    }

    // set the anchor for 0 a.m.
    let components = DateComponents(calendar: Calendar.current, timeZone: Calendar.current.timeZone, hour: 0, minute: 0, second: 0)
    guard let anchorDate = Calendar.current.nextDate(
        after: Date(),
        matching: components,
        matchingPolicy: .nextTime,
        repeatedTimePolicy: .first,
        direction: .backward
    ) else {
        finishWithResult(nil)
        return
    }

    let predicate = HKQuery.predicateForSamples(withStart: Calendar.current.startOfDay(for: Date()), end: nil, options: [.strictStartDate])

    let query = HKStatisticsCollectionQuery(
        quantityType: type,
        quantitySamplePredicate: predicate,
        options: .cumulativeSum,
        anchorDate: anchorDate,
        intervalComponents: DateComponents(day: 1)
    )

    // initial response
    query.initialResultsHandler = { [weak self] _, result, error in
        guard
            error == nil,
            let statsCollection = result
        else {
            finishWithResult(nil)
            return
        }

        let endDate = Date()
        let startDate = Calendar.current.startOfDay(for: endDate)
        var resultCount = 0

        statsCollection.enumerateStatistics(from: startDate, to: endDate) { statistics, _ in
            if let sum = statistics.sumQuantity() {
                resultCount = Int(sum.doubleValue(for: .count()))
            }
        }

        self?.logger.info("Got today's steps (initial): \(resultCount)")
        finishWithResult(resultCount)
    }

    // subscribe for updates
    query.statisticsUpdateHandler = { [weak self] _, statistics, _, error in
        guard error == nil else { return }

        if let sum = statistics?.sumQuantity() {
            let steps = Int(sum.doubleValue(for: .count()))
            self?.logger.info("Got today's steps (update): \(steps)")
            finishWithResult(steps)
        }
    }

    healthStore?.execute(query)
}