Как получить ежедневную продолжительность сна с помощью Apple HealthKit?
Я делаю приложение, которое читает ежедневные шаги и данные о сне из Apple HealthKit.
Для Steps это довольно легко, потому что это HKQuantityType
так что я могу подать заявку HKStatisticsOptionCumulativeSum
Вариант на это. Введите дату начала, дату окончания и интервал дат, и вы получите это.
- (void)readDailyStepsSince:(NSDate *)date completion:(void (^)(NSArray *results, NSError *error))completion {
NSDate *today = [NSDate date];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *comps = [calendar components:NSCalendarUnitDay|NSCalendarUnitMonth|NSCalendarUnitYear fromDate:date];
comps.hour = 0;
comps.minute = 0;
comps.second = 0;
NSDate *midnightOfStartDate = [calendar dateFromComponents:comps];
NSDate *anchorDate = midnightOfStartDate;
HKQuantityType *stepType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
HKStatisticsOptions sumOptions = HKStatisticsOptionCumulativeSum;
NSPredicate *dateRangePred = [HKQuery predicateForSamplesWithStartDate:midnightOfStartDate endDate:today options:HKQueryOptionNone];
NSDateComponents *interval = [[NSDateComponents alloc] init];
interval.day = 1;
HKStatisticsCollectionQuery *query = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:stepType quantitySamplePredicate:dateRangePred options:sumOptions anchorDate:anchorDate intervalComponents:interval];
query.initialResultsHandler = ^(HKStatisticsCollectionQuery *query, HKStatisticsCollection *result, NSError *error) {
NSMutableArray *output = [NSMutableArray array];
// we want "populated" statistics only, so we use result.statistics to iterate
for (HKStatistics *sample in result.statistics) {
double steps = [sample.sumQuantity doubleValueForUnit:[HKUnit countUnit]];
NSDictionary *dict = @{@"date": sample.startDate, @"steps": @(steps)};
//NSLog(@"[STEP] date:%@ steps:%.0f", s.startDate, steps);
[output addObject:dict];
}
dispatch_async(dispatch_get_main_queue(), ^{
if (completion != nil) {
NSLog(@"[STEP] %@", output);
completion(output, error);
}
});
};
[self.healthStore executeQuery:query];
}
Но для сна это не так просто. Есть много вещей, на которых я застрял.
- Во-первых, в отличие от шагов, сон
HKCategoryType
, Поэтому мы не можем использоватьHKStatisticsCollectionQuery
суммировать, потому что этот метод принимает толькоHKQuantityType
, - Также есть 2 ценностных типа сна,
HKCategoryValueSleepAnalysisInBed
а такжеHKCategoryValueSleepAnalysisAsleep
, Я не уверен, какое значение лучше всего для продолжительности сна. Я просто буду использоватьHKCategoryValueSleepAnalysisAsleep
только для простоты. - Данные сна поступают в массив
HKCategorySample
объекты. Каждый с начальной и конечной датой. Как эффективно объединить эти данные, обрезать их с точностью до дня и получить из них ежедневную продолжительность сна (в минутах)? Я нашел этот класс DTTimePeriodCollection в модуле DateTool, который может выполнять эту работу, но я еще не понял этого.
Проще говоря, если кто-нибудь знает, как получить ежедневную продолжительность сна с помощью Apple HealthKit, пожалуйста, сообщите мне!
1 ответ
Проверьте, как я это сделал, он помогает мне получить сбор данных о сне
func sleepTime() {
let healthStore = HKHealthStore()
// startDate and endDate are NSDate objects
// first, we define the object type we want
if let sleepType = HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis) {
// You may want to use a predicate to filter the data... startDate and endDate are NSDate objects corresponding to the time range that you want to retrieve
//let predicate = HKQuery.predicateForSamplesWithStartDate(startDate,endDate: endDate ,options: .None)
// Get the recent data first
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
// the block completion to execute
let query = HKSampleQuery(sampleType: sleepType, predicate: nil, limit: 100000, sortDescriptors: [sortDescriptor]) { (query, tmpResult, error) -> Void in
if error != nil {
// Handle the error in your app gracefully
return
}
if let result = tmpResult {
for item in result {
if let sample = item as? HKCategorySample {
let startDate = sample.startDate
let endDate = sample.endDate
print()
let sleepTimeForOneDay = sample.endDate.timeIntervalSince(sample.startDate)
}
}
}
}
}
Это дает массив входных слотов.
Я использовал это:
@import HealthKit;
@implementation HKHealthStore (AAPLExtensions)
- (void)hkQueryExecute:(void (^)(double, NSError *))completion {
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *now = [NSDate date];
NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:now];
NSDate *startDate = [calendar dateFromComponents:components];
NSDate *endDate = [calendar dateByAddingUnit:NSCalendarUnitDay value:1 toDate:startDate options:0];
HKSampleType *sampleType = [HKSampleType categoryTypeForIdentifier:HKCategoryTypeIdentifierSleepAnalysis];
NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionNone];
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:sampleType predicate:predicate limit:0 sortDescriptors:nil resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
if (!results) {
NSLog(@"An error occured fetching the user's sleep duration. In your app, try to handle this gracefully. The error was: %@.", error);
completion(0, error);
abort();
}
double minutesSleepAggr = 0;
for (HKCategorySample *sample in results) {
NSTimeInterval distanceBetweenDates = [sample.endDate timeIntervalSinceDate:sample.startDate];
double minutesInAnHour = 60;
double minutesBetweenDates = distanceBetweenDates / minutesInAnHour;
minutesSleepAggr += minutesBetweenDates;
}
completion(minutesSleepAggr, error);
}];
[self executeQuery:query];
}
А потом ввиду контроллер:
- (void)updateUsersSleepLabel {
[self.healthStore hkQueryExecute: ^(double minutes, NSError *error) {
if (minutes == 0) {
NSLog(@"Either an error occured fetching the user's sleep information or none has been stored yet.");
dispatch_async(dispatch_get_main_queue(), ^{
self.sleepDurationValueLabel.text = NSLocalizedString(@"Not available", nil);
});
}
else {
int hours = (int)minutes / 60;
int minutesNew = (int)minutes - (hours*60);
NSLog(@"hours slept: %ld:%ld", (long)hours, (long)minutesNew);
dispatch_async(dispatch_get_main_queue(), ^{
self.sleepDurationValueLabel.text = [NSString stringWithFormat:@"%d:%d", hours, minutesNew] ;
});
}
}];
}