问题
I have the following code that gets the sum of sleep hours. However, it is summing inbed and asleep together. What I am trying to get is a sum for just the asleep time.
func readSleepAnalysis(date: Date) {
if let sleepType = HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis) {
let startDate = convertSleepStartDate(StartDate: date)
let endDate = convertSleepEndDate(EndDate: date)
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: .strictStartDate)
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
let query = HKSampleQuery(sampleType: sleepType, predicate: predicate, limit: 30, sortDescriptors: [sortDescriptor]) {
(query, samples, error) in
guard
error == nil,
samples == samples as? [HKCategorySample] else {
print("Something went wrong getting sleep analysis: \(String(describing: error))")
return
}
let total = samples?.map(self.calculateSleepHours).reduce(0, {$0 + $1}) ?? 0
DispatchQueue.main.async {
self.userSleepMinutes = total
print("userSleepHours = \(self.userSleepMinutes)")
}
}
healthKit.execute(query)
}
}
func calculateSleepHours(sample: HKSample) -> TimeInterval {
let hours = sample.endDate.timeIntervalSince(sample.startDate) / 60 / 60
return hours
}
I previously discovered that Apple records all data based on UTC. Make sense! However, this may work for active energy and other data like that but total sleep time can't be calculated like this. I am calculating the total time from 6 PM the night prior to 05:59 AM that day. Here is how I am doing that (there may be a better way but it's beyond me at this moment).
func convertSleepStartDate(StartDate: Date) -> Date {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyy-MM-dd '18':'00':'01' +0000"
let dateString = dateFormatter.string(from: StartDate)
dateFormatter.dateFormat = "yyy-MM-dd HH:mm:ss +0000"
let date = dateFormatter.date(from: dateString)
let datePrior = Calendar.current.date(byAdding: .hour, value: -24, to: date!)
print(datePrior as Any)
return datePrior!
}
func convertSleepEndDate(EndDate: Date) -> Date {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyy-MM-dd '17':'59':'59' +0000"
let dateString = dateFormatter.string(from: EndDate)
dateFormatter.dateFormat = "yyy-MM-dd HH:mm:ss +0000"
let date = dateFormatter.date(from: dateString)
print(date as Any)
return date!
}
Any ideas?
回答1:
Better late than never: In your calculateSleepHours function, you should check sample.value against the HKCategoryValueSleepAnalysis constants (inBed, asleep, awake). That will tell you the interval's type. It will also teach you how Apple measures and returns this data. Keep an eye on the intervals, because when you hit the end of the planned sleep time on the watch, it will give you a summary of inBed time.
let value1 = (sample.value == HKCategoryValueSleepAnalysis.inBed.rawValue) ? "InBed" : "Asleep"
let value2 = (sample.value == HKCategoryValueSleepAnalysis.asleep.rawValue) ? "Asleep" : "Awake"
let value3 = (sample.value == HKCategoryValueSleepAnalysis.awake.rawValue) ? "Awake" : "Asleep"
The 3 values contain the type of constant that each interval is measuring. It is almost always asleep, but may differ depending on the Watch OS versions.
You can print the values and see, on a per interval basis, exactly what is being measured. Be sure to print the sample.startDate and sample.endDate too. That will teach you all you need to know to get actual sleep time between two Dates.
来源:https://stackoverflow.com/questions/61236981/calculate-asleep-time-in-healthkit-using-swift