-1

I'm trying to use HealthKit to determine the amount of time the user spent sleeping in the 24 hours previous to the query. I've requested permission from the user to read step and sleep data and, while I can read step data without any trouble, my sleep query returns no data.

Here's my function:

    func fetchSleepData(completion: @escaping (TimeInterval?) -> Void) {
        let healthStore = HKHealthStore()
        let calendar = Calendar.current

        let endDate = Date()
        let startDate = calendar.date(byAdding: .day, value: -1, to: endDate)!
        let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: .strictStartDate)

        let sleepType = HKObjectType.categoryType(forIdentifier: .sleepAnalysis)!
        let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)

        let query = HKSampleQuery(sampleType: sleepType, predicate: predicate, limit: Int(HKObjectQueryNoLimit), sortDescriptors: [sortDescriptor]) { _, results, error in
            if let error = error {
                print("Error: \(error.localizedDescription)")
                return
            }

            guard let results = results as? [HKCategorySample] else {
                print("No data to display")
                return
            }

            print("Start Date: \(startDate), End Date: \(endDate)")
            print("Fetched \(results.count) sleep analysis samples.")

            var totalSleepTime: TimeInterval = 0

            for result in results {
                if result.value == HKCategoryValueSleepAnalysis.asleepUnspecified.rawValue {
                    let sleepDuration = result.endDate.timeIntervalSince(result.startDate)
                    print("Sample start: \(result.startDate), end: \(result.endDate), value: \(result.value), duration: \(sleepDuration) seconds")
                    totalSleepTime += sleepDuration
                }
            }

            completion(totalSleepTime)
        }

        healthStore.execute(query)
    }

and here's my authentication and function call:

let healthStore = HKHealthStore()
        
let steps = HKQuantityType(.stepCount)
let sleepAnalysis = HKObjectType.categoryType(forIdentifier: .sleepAnalysis)!
        
let healthTypes: Set = [steps, sleepAnalysis]
        
Task {
    do {
        try await healthStore.requestAuthorization(toShare: [], read: healthTypes)
        fetchStepData()
        fetchSleepData { totalSleepTime in
            if let totalSleepTime = totalSleepTime {
                print("User slept for \(totalSleepTime) seconds last night.")
            } else {
                print("No sleep data available for last night.")
            }
        }
    } catch {
        print("Error fetching health data!")
    }
}

When the app runs, the console shows:

Health Manager Init
Fetching step data...
...
Start Date: 2023-07-07 06:00:00 +0000, End Date: 2023-07-08 06:00:00 +0000
Fetched 0 sleep analysis samples.
User slept for 0.0 seconds last night.
...

I've double checked that there is sleep data in the testing device's Health app and that the user has granted permission to the app. I'm wondering if it may be a problem with my environment because every resource I've found online seems to show similar solutions. Thank you in advance for any advice/solutions!

matt
  • 515,959
  • 87
  • 875
  • 1,141

1 Answers1

0

The problem is that .asleepUnspecified does not embrace the actual sleep types, such as .asleepDeep and so forth. What does embrace them all is .allAsleepValues. Change

if result.value == HKCategoryValueSleepAnalysis.asleepUnspecified.rawValue {

To

if HKCategoryValueSleepAnalysis.allAsleepValues.map({$0.rawValue}).contains(result.value) {

Actually I would rewrite your loop (more neatly, I think) like this:

for result in results {
    if let type = HKCategoryValueSleepAnalysis(rawValue: result.value) {
        if HKCategoryValueSleepAnalysis.allAsleepValues.contains(type) {
            let sleepDuration = result.endDate.timeIntervalSince(result.startDate)
            print("""
            Sample start: (result.startDate), \
            end: (result.endDate), \
            value: (result.value), \
            duration: (sleepDuration) seconds
            """)
            totalSleepTime += sleepDuration
        }
    }
}
matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Hey! thanks for the input! This still doesn't quite work for me, and I think the problem stems from the fact that `results` has zero items in it, and so the for loop isn't even running (though I'm sure if it had been running this likely would have been something I need to do anyways. Any thoughts? – Sam Uyemura Jul 08 '23 at 18:37
  • No. For me there are results but your code was throwing them away. Otherwise your code is fine. – matt Jul 08 '23 at 19:16
  • Ok, that's good to know. I'll have to create another project and try to see if there was something else in my code that was causing the problem. Thank you so much for your help. – Sam Uyemura Jul 08 '23 at 19:44