Query HealthKit for HKCategoryTypeIdentifierSleepAnalysis
Asked Answered
F

2

8

I have built a method that imports a sleep sample but I can't get it to return the proper value for hours asleep.

The method to query for sleep data looks like this:

func updateHealthCategories() {

    let categoryType = HKObjectType.categoryTypeForIdentifier(HKCategoryTypeIdentifierSleepAnalysis)

    let start = NSDate(dateString:"2015-11-04")
    let end = NSDate(dateString:"2015-11-05")

    let categorySample = HKCategorySample(type: categoryType!,
        value: HKCategoryValueSleepAnalysis.Asleep.rawValue,
        startDate: start,
        endDate: end)

    self.hoursSleep = Double(categorySample.value)

    print(categorySample.value)
}

The date is formatted like this:

extension NSDate
{
    convenience
    init(dateString:String) {
        let dateStringFormatter = NSDateFormatter()
        dateStringFormatter.dateFormat = "yyyy-MM-dd"
        dateStringFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
        let d = dateStringFormatter.dateFromString(dateString)!
        self.init(timeInterval:0, sinceDate:d)
    }
}

I'm calling data from November 4-5, which contains this data:

However, the categorySample.value returns 1 instead of 3.

Finland answered 6/11, 2015 at 0:9 Comment(0)
D
9

The value you are accessing is the category sample value, an HKCategoryType, and not the number of hours of sleep.

The definition for HKCategoryTypeIdentifierSleepAnalysis

typedef enum : NSInteger {
   HKCategoryValueSleepAnalysisInBed,
   HKCategoryValueSleepAnalysisAsleep,
} HKCategoryValueSleepAnalysis;

defines two possible values, 0 or 1 where the value of 1 matches HKCategoryValueSleepAnalysisAsleep.

Getting the hours asleep requires setting up a HKSampleQuery.

The code looks something like this:

if let sleepType = HKObjectType.categoryTypeForIdentifier(HKCategoryTypeIdentifierSleepAnalysis) {

    let predicate = HKQuery.predicateForSamplesWithStartDate(startDate, endDate: endDate, options: .None)
    let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
    let query = HKSampleQuery(sampleType: sleepType, predicate: predicate, limit: 30, sortDescriptors: [sortDescriptor]) { (query, tmpResult, error) -> Void in                  
        if let result = tmpResult {
            for item in result {
                if let sample = item as? HKCategorySample {                     
                    let value = (sample.value == HKCategoryValueSleepAnalysis.InBed.rawValue) ? "InBed" : "Asleep"                     
                    print("sleep: \(sample.startDate) \(sample.endDate) - source: \(sample.source.name) - value: \(value)")
                    let seconds = sample.endDate.timeIntervalSinceDate(sample.startDate)
                    let minutes = seconds/60
                    let hours = minutes/60
                }
            }
        }
    }

    healthStore.executeQuery(query)
}

I summarized this from http://benoitpasquier.fr/sleep-healthkit/.

Duelist answered 6/11, 2015 at 0:44 Comment(0)
D
0

Here is Swift 5, iOS 16 compatible answer if someone is still looking. You can parse/operate data as per your needs.

func getSleepAnalysis() {
    let healthStore = HKHealthStore()
    let endDate = Date()

    guard let startDate = Calendar.current.date(byAdding: .day, value: -7, to: endDate) else {
        fatalError("*** Unable to create the start date ***")
    }


    // first, we define the object type we want
    guard let sleepType = HKObjectType.categoryType(forIdentifier: .sleepAnalysis) else {
        return
    }

    // we create a predicate to filter our data
    let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: .strictStartDate)

    // I had a sortDescriptor to get the recent data first
    let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)

    // we create our query with a block completion to execute
    let query = HKSampleQuery(sampleType: sleepType, predicate: predicate, limit: Int(HKObjectQueryNoLimit), sortDescriptors: [sortDescriptor]) { (query, result, error) in
        if error != nil {
            // handle error
            return
        }

        if let result = result {

            // do something with those data
            result
                .compactMap({ $0 as? HKCategorySample })
                .forEach({ sample in
                    guard let sleepValue = HKCategoryValueSleepAnalysis(rawValue: sample.value) else {
                        return
                    }

                    let isAsleep = sleepValue == .asleep
                    print("HealthKit sleep \(sample.startDate) \(sample.endDate) - source \(sample.sourceRevision.source.name) - isAsleep \(isAsleep)")
                })
        }
    }

    // finally, we execute our query
    healthStore.execute(query)
}

I hope you'll get the authorization for SleepAnalysis before this so data is retrived.

Dol answered 29/10, 2022 at 10:49 Comment(2)
First off, thank you for the iOS16 update. I'm trying to use this to grab sleep data for my app but it's giving me an error 'Value of optional type 'HKCategorySample?' must be unwrapped to refer to member 'value' of wrapped base type 'HKCategorySample''Minutiae
You should probably do unwrapping the value before using it.Dol

© 2022 - 2024 — McMap. All rights reserved.