Search code examples
swifthealthkithksamplequery

How to get Apple health data by date wise?


Apple health app gives the data by Date wise as shown in below image.

enter image description here

By using HealthKit i am fetching the steps data from apple health as

let p1 = HKQuery.predicateForSamples(withStart: fromDate, end: Date(), options: .strictStartDate)
let p2 = HKQuery.predicateForObjects(withMetadataKey: HKMetadataKeyWasUserEntered, operatorType: .notEqualTo, value: true)
let timeSortDesriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)//as in health kit entry
let quantityType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)!
let predicate = HKQuery.predicateForSamples(withStart: fromDate, end: Date(), options: .strictStartDate)
let sourceQuery = HKSourceQuery(sampleType: quantityType, samplePredicate: predicate, completionHandler: { query,sources,error in

if sources?.count != 0  && sources != nil {

                let lastIndex = sources!.count - 1
                var sourcesArray = Array(sources!)
                for i in 0..<sourcesArray.count {
                    let sourcePredicate = HKQuery.predicateForObjects(from: sourcesArray[i])
                    let predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [p1, p2,sourcePredicate])
                    let query = HKSampleQuery(sampleType: quantityType, predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: [timeSortDesriptor], resultsHandler: { query, results, error in
                        guard let samples = results as? [HKQuantitySample] else {
                            return
                        }......

sourceQuery gives multiple object like Apple watch, My iPhone. further i am using for loop with HKSampleQuery which gives HKQuantitySample object. Problem is [HKQuantitySample] gives Array of step data which is not sorted datewise. I am looking for data which are club for the date like what apple health show in Health app.

Yes there is workaround like manually sort the data from [HKQuantitySample] by date wise. but there may be workaround by using predicates or something else. Please feel free to ask if you need any extra information.

EDIT: As suggested by @Allan I have added HKStatisticsCollectionQuery, YES it gives data datewise but the steps count receiving is not same as in Apple health App. is something Addition/modification required in below code ?

let last10Day = Calendar.current.date(byAdding: .day, value: -10, to: Date())!
        var interval = DateComponents()
        interval.day = 1
        let quantityType1 = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)!
        // Create the query
        let query = HKStatisticsCollectionQuery(quantityType: quantityType1,
                                                quantitySamplePredicate: nil,
                                                options: .cumulativeSum,
                                                anchorDate: last10Day,
                                                intervalComponents: interval)
        // Set the results handler
        query.initialResultsHandler = {
            query, results, error in

            guard let statsCollection = results else {
                // Perform proper error handling here
                fatalError("*** An error occurred while calculating the statistics: \(String(describing: error?.localizedDescription)) ***")
            }
            let endDate = Date()
            statsCollection.enumerateStatistics(from: last10Day, to: endDate, with: { (statistics, stop) in
                if let quantity = statistics.sumQuantity() {
                    let date = statistics.startDate
                    let value = quantity.doubleValue(for: HKUnit.count())
                    print("--value-->",value, ",--For Date-->",date)
                }
            })
        }
            healthStore.execute(query)

Solution

  • Use HKStatisticsCollectionQuery to fetch data from a certain period. Here is an example showing how to fetch steps for the last 30 days:

    private let healthStore = HKHealthStore()
    private let stepsQuantityType = HKQuantityType.quantityType(forIdentifier: .stepCount)!
    
    func importStepsHistory() {
        let now = Date()
        let startDate = Calendar.current.date(byAdding: .day, value: -30, to: now)!
    
        var interval = DateComponents()
        interval.day = 1
    
        var anchorComponents = Calendar.current.dateComponents([.day, .month, .year], from: now)
        anchorComponents.hour = 0
        let anchorDate = Calendar.current.date(from: anchorComponents)!
    
        let query = HKStatisticsCollectionQuery(
            quantityType: stepsQuantityType,
            quantitySamplePredicate: nil,
            options: [.cumulativeSum],
            anchorDate: anchorDate,
            intervalComponents: interval
        ) 
        query.initialResultsHandler = { _, results, error in
            guard let results = results else {
                log.error("Error returned form resultHandler = \(String(describing: error?.localizedDescription))")
                return
            }
        
            results.enumerateStatistics(from: startDate, to: now) { statistics, _ in
                if let sum = statistics.sumQuantity() {
                    let steps = sum.doubleValue(for: HKUnit.count())
                    print("Amount of steps: \(steps), date: \(statistics.startDate)")
                }
            }
        }
    
        healthStore.execute(query)
    }