Is there any way to get Sleep Data by HKSampleQuery like HKStatisticsCollectionQuery?

Hello all!

Unlike Steps and Distance is QuantityType so I can use HKStatisticsCollectionQuery and set options to filter them like below
Code Block language
options: [HKStatisticsOptions.separateBySource, .cumulativeSum]


Sleep is CategoryType so I can only use HKSampleQuery to get data like code below:

Code Block language
func getHealthKitSleep() {
let healthStore = HKHealthStore()
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
// Get all samples from the last 24 hours
let endDate = Date()
let startDate = endDate.addingTimeInterval(-1.0 * 60.0 * 60.0 * 24.0)
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: [])
// Sleep query
let sleepQuery = HKSampleQuery(
sampleType: HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis)!,
predicate: predicate,
limit: 0,
sortDescriptors: [sortDescriptor]){ (query, results, error) -> Void in
if error != nil {return}
// Sum the sleep time
var minutesSleepAggr = 0.0
if let result = results {
for item in result {
if let sample = item as? HKCategorySample {
if sample.value == HKCategoryValueSleepAnalysis.asleep.rawValue && sample.startDate >= startDate {
let sleepTime = sample.endDate.timeIntervalSince(sample.startDate)
let minutesInAnHour = 60.0
let minutesBetweenDates = sleepTime / minutesInAnHour
minutesSleepAggr += minutesBetweenDates
}
}
}
self.sleep = Double(String(format: "%.1f", minutesSleepAggr / 60))!
print("HOURS: \(String(describing: self.sleep))")
}
}
// Execute our query
healthStore.execute(sleepQuery)
}


This works great if the user has only one sleep app as the source for the data. The problem is if the user is using 2 sleep apps, for example, as sources, the data will be doubled. I just want show sleep data like HealthApp when user change data source priority, is there some ways to do it.

Replies

For a now, I found the only way to separate data by parsing the description property. Something like this

Code Block
let regex = try! NSRegularExpression(pattern: "\\bwatch\\b" ,options: .caseInsensitive)
              let isMatch = regex.firstMatch(in: item.description, options: [], range: NSMakeRange(0, sample.description.utf16.count)) != nil
              if (isMatch) {
                sumTime += sample.endDate.timeIntervalSince(sample.startDate)}


where pattern
Code Block
pattern: "\\bwatch\\b"
left data only if description string contain "Watch" word.

The property sample.description contains data like this

Code Block
A81532F3-8BC1-BB1C131B76EC "Joy Apple Watch" (7.0), "Watch5,1" (7.0)metadata: {
HKTimeZone = "Europe/Berlin";
} (2020-09-18 04:53:40 +0100 - 2020-09-18 04:55:40 +0300)
Healthkit sleep: start - HKCategoryTypeIdentifierSleepAnalysis ++++ 2020-09-18 01:53:40 +0000 finish - 2020-09-18 01:55:40 +0000 - value: Asleep