- 關(guān)于iOS10的報錯
Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: 'NSHealthUpdateUsageDescription must be set in the app's Info.plist
in order to request write authorization.'
處理方法:在Info.plist文件中添加關(guān)鍵字NSHealthShareUsageDescription
和NSHealthUpdateUsageDescription
砌梆,寫入和讀取都需要。iOS10對于涉及用戶隱私的很多數(shù)據(jù)獲取都添加了類似的設(shè)置,可根據(jù)提示在plist文件中添加相應(yīng)的權(quán)限字段。
在蘋果的 iOS8 系統(tǒng)中自帶了健康應(yīng)用,可以記錄我們在一天當(dāng)中的運(yùn)動數(shù)據(jù)各拷,比如走了多少步,一天走了或跑了多少公里等。還可以接入硬件設(shè)備以寫入運(yùn)動數(shù)據(jù)柠衅,HealthKit框架為蘋果為采集各種健康數(shù)據(jù)源提供的接口,可以用來采集和整合各種健康數(shù)據(jù)的來源籍琳,如運(yùn)動手環(huán)菲宴,AppleWatch,以及iPhone設(shè)備提供的健康數(shù)據(jù)趋急。這里簡單介紹下如何獲取蘋果健康的數(shù)據(jù)喝峦。
實現(xiàn)流程:
一、在工程中添加HealthKit庫呜达。
二谣蠢、創(chuàng)建模型(或在其他模型中添加#import),添加HKHealthStore屬性查近,HKHealthStore是使用HealthKit中關(guān)鍵的一個類眉踱。
三、創(chuàng)建調(diào)用方法霜威,在方法實現(xiàn)中主要要做的有3步:
1谈喳、判斷設(shè)備是否支持HealthKit框架
2、請求蘋果健康的認(rèn)證
3侥祭、獲取蘋果健康的數(shù)據(jù)
*// 方法代碼
- (void)getIphoneHealthData{
self.healthSteps = [NSMutableArray array];
self.healthDistances = [NSMutableArray array];
self.healthCalories = [NSMutableArray array];
NSSet *getData;
// 1.判斷設(shè)備是否支持HealthKit框架
if ([HKHealthStore isHealthDataAvailable]) {
getData = [self getData];
} else {
NSLog(@"---------不支持 HealthKit 框架");
}
store = [[HKHealthStore alloc] init];
// 2.請求蘋果健康的認(rèn)證
[store requestAuthorizationToShareTypes:nil readTypes:getData completion:^(BOOL success, NSError * _Nullable error) {
if (!success) {
NSLog(@"--------請求蘋果健康認(rèn)證失敗");
return ;
}
dispatch_async(dispatch_get_main_queue(), ^{
// 3.獲取蘋果健康數(shù)據(jù)
[self getHealthStepData];
[self getHealthDistanceData];
});
}];
}
在第一步判斷設(shè)備支持HealthKit框架后叁执,設(shè)置好要獲取的數(shù)據(jù)類型,筆者此處獲取的是步數(shù)矮冬,距離數(shù)據(jù)谈宛。
*// getData方法
- (NSSet *)getData{
HKQuantityType *step = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
HKQuantityType *distance = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning];
return [NSSet setWithObjects:step,distance, nil];
}
第二步請求蘋果健康的認(rèn)證,此處用到HKHealthStore屬性胎署,實例化屬性后調(diào)用認(rèn)證方法吆录,注意方法中的參數(shù)ShareTypes 和 readTypes,一個是認(rèn)證寫入類型琼牧,一個是認(rèn)證讀取類型恢筝,認(rèn)證成功后開始最重要的第三步哀卫,獲取數(shù)據(jù),此處為異步執(zhí)行撬槽。
在獲取數(shù)據(jù)的方法中時間的設(shè)置是比較麻煩的此改,不過都是一樣的;HKQuantityType實例對象用于設(shè)置要獲取的數(shù)據(jù)類型侄柔,NSPredicate實例對象用于設(shè)置獲取數(shù)據(jù)的時間段共啃,HKStatisticsCollectionQuery實例對象用于獲取數(shù)據(jù),在獲取的數(shù)據(jù)結(jié)果中需要把每一條數(shù)據(jù)遍歷出來暂题,可以做一些需要的格式的處理移剪。
*// 代碼
- (void)getHealthStepData{
HKHealthStore *healthStore = [[HKHealthStore alloc]init];
NSCalendar *calendar = [NSCalendar currentCalendar];
// 設(shè)置時間支持單位
NSDateComponents *anchorComponents =
[calendar components:NSCalendarUnitDay | NSCalendarUnitMonth |
NSCalendarUnitYear | NSCalendarUnitWeekday fromDate:[NSDate date]];
NSDate *anchorDate = [calendar dateFromComponents:anchorComponents];
// 獲取數(shù)據(jù)的截止時間 今天
NSDate *endDate = [NSDate date];
// 獲取數(shù)據(jù)的起始時間 此處取從今日往前推100天的數(shù)據(jù)
NSDate *startDate = [NSDate dateWithTimeIntervalSinceNow:-100*24*60*60];
// 數(shù)據(jù)類型
HKQuantityType *type = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
// Your interval: sum by hour
NSDateComponents *intervalComponents = [[NSDateComponents alloc] init];
intervalComponents.day = 1;
// Example predicate 用于獲取設(shè)置時間段內(nèi)的數(shù)據(jù)
NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionStrictStartDate];
HKStatisticsCollectionQuery *query = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:type quantitySamplePredicate:predicate options:HKStatisticsOptionCumulativeSum anchorDate:anchorDate intervalComponents:intervalComponents];
query.initialResultsHandler = ^(HKStatisticsCollectionQuery *query, HKStatisticsCollection *result, NSError *error) {
for (HKStatistics *sample in [result statistics]) {
// NSLog(@"--------------%@ 至 %@ : %@", sample.startDate, sample.endDate, sample.sumQuantity);
NSDate *date = sample.endDate;
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"yyyy-MM-dd"];
NSString *dateTime = [formatter stringFromDate:date];
double totalStep = [sample.sumQuantity doubleValueForUnit:[HKUnit countUnit]];
NSString *value = [NSString stringWithFormat:@"%f",totalStep];
NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:
dateTime,@"dateTime",
value,@"value",nil];
[self.healthSteps addObject:dic];
// NSLog(@"gaizaoDateStyle:%@ Dic = %@",self.healthSteps,dic);
}
self.healthCalories = self.healthSteps;
NSDictionary *healthSteps = [NSDictionary dictionaryWithObjectsAndKeys:
self.healthSteps,@"healthSteps",
self.healthCalories,@"healthCalories",nil];
NSLog(@"改造數(shù)據(jù)格式:%@",healthSteps);
};
[healthStore executeQuery:query];
}
由于蘋果健康應(yīng)用中,用戶可以自己手動添加數(shù)據(jù)薪者,所以為了獲取數(shù)據(jù)的準(zhǔn)確性纵苛,像微信運(yùn)動會把這些數(shù)據(jù)過濾掉,那么如何過濾呢言津?
在獲取的數(shù)據(jù)結(jié)果中攻人,每一條結(jié)果都有數(shù)據(jù)來源屬性,打印HKStatistics實例對象的sources屬性就可以看到數(shù)據(jù)的所有來源(因為此處獲取的是一天的數(shù)據(jù)纺念,一天內(nèi)的數(shù)據(jù)來源都會被打印出來)贝椿,如果有手動添加的數(shù)據(jù),可以在打印結(jié)果中看到com.apple.Health數(shù)據(jù)源陷谱,我們要過濾的就是它烙博。
接下來就是用獲取的數(shù)據(jù)總數(shù)把手動添加的數(shù)據(jù)減掉,結(jié)果就是我們想要獲取的準(zhǔn)確的數(shù)據(jù)烟逊。
*// 粘貼代碼
double totalStep = [sample.sumQuantity doubleValueForUnit:[HKUnit countUnit]];
NSString *appleHealth = @"com.apple.Health";
double editStep = 0.0;
for (HKSource *source in sample.sources) {
if ([source.bundleIdentifier isEqualToString:appleHealth]) {
// 獲取用戶自己添加的數(shù)據(jù) 并減去渣窜,防止用戶手動刷數(shù)據(jù)
HKSource *healthSource = source;
editStep = [[sample sumQuantityForSource:healthSource] doubleValueForUnit:[HKUnit countUnit]];
}
}
NSInteger step = (NSInteger)totalStep - (NSInteger)editStep;
NSString *value = [NSString stringWithFormat:@"%ld",step];
最后是獲取距離的方法,和前邊類似宪躯。
*// 代碼
- (void)getHealthDistanceData{
HKHealthStore *healthStore = [[HKHealthStore alloc]init];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *anchorComponents =
[calendar components:NSCalendarUnitDay | NSCalendarUnitMonth |
NSCalendarUnitYear | NSCalendarUnitWeekday fromDate:[NSDate date]];
NSDate *anchorDate = [calendar dateFromComponents:anchorComponents];
NSDate *endDate = [NSDate date];
NSDate *startDate = [NSDate dateWithTimeIntervalSinceNow:-100*24*60*60];
HKQuantityType *type = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning];
// Your interval: sum by hour
NSDateComponents *intervalComponents = [[NSDateComponents alloc] init];
intervalComponents.day = 1;
// Example predicate
NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionStrictStartDate];
HKStatisticsCollectionQuery *query = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:type quantitySamplePredicate:predicate options:HKStatisticsOptionCumulativeSum anchorDate:anchorDate intervalComponents:intervalComponents];
query.initialResultsHandler = ^(HKStatisticsCollectionQuery *query, HKStatisticsCollection *result, NSError *error) {
for (HKStatistics *sample in [result statistics]) {
// NSLog(@"+++++++++++++++%@ 至 %@ : %@", sample.startDate, sample.endDate, sample.sumQuantity);
NSDate *date = sample.endDate;
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"yyyy-MM-dd"];
NSString *dateTime = [formatter stringFromDate:date];
double totalDistance = [sample.sumQuantity doubleValueForUnit:[HKUnit meterUnit]];
NSString *appleHealth = @"com.apple.Health";
// double floor = [sample.sumQuantity doubleValueForUnit:[HKUnit yardUnit]];
double editDistance = 0.0;
for (HKSource *source in sample.sources) {
if ([source.bundleIdentifier isEqualToString:appleHealth]) {
// 獲取用戶自己添加的數(shù)據(jù) 并減去乔宿,防止用戶手動刷數(shù)據(jù)
HKSource *healthSource = source;
editDistance = [[sample sumQuantityForSource:healthSource] doubleValueForUnit:[HKUnit meterUnit]];
}
}
double distance = totalDistance/1000 - editDistance/1000;
NSString *value = [NSString stringWithFormat:@"%f",distance];
NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:
dateTime,@"dateTime",
value,@"value",nil];
[self.healthDistances addObject:dic];
}
NSLog(@"改造距離格式:%@",self.healthDistances);
};
[healthStore executeQuery:query];
}
在打包時候需要打開HealthKit使用的開關(guān),如圖:
此外打包證書也需要勾選蘋果健康選項访雪,由于開發(fā)者賬號劃歸別的部門負(fù)責(zé)了详瑞,所以沒法截圖了。臣缀。坝橡。
踩的坑
之前做了一個健康運(yùn)動的APP,要把蘋果健康的數(shù)據(jù)和fitbit手環(huán)的數(shù)據(jù)比較處理后做展示精置,結(jié)果發(fā)現(xiàn)請求fitbit的數(shù)據(jù)產(chǎn)生的起止時間段內(nèi)如果有沒數(shù)據(jù)的计寇,比如步數(shù),卡路里,等番宁,會都返回0元莫,還比較人性化。但是蘋果健康就沒這么友好了蝶押,如果沒有數(shù)據(jù)那就是沒有踱蠢,根本不會給你0,比如2015年1月1號到2015年1月20號的數(shù)據(jù)中1月10號這天的數(shù)據(jù)被刪了播聪,那返回的數(shù)據(jù)就只有19條朽基,10號這天的不會返回0布隔,直接就沒有這條數(shù)據(jù)离陶。再或者本身開始使用蘋果健康記錄數(shù)據(jù)的時間就是從2015年1月10號開始的,那1月10號之前的數(shù)據(jù)就都沒有衅檀,不會返回0給你招刨。
demo地址 GitHub給個Star噢!
喜歡就點個贊唄!
歡迎大家提出更好的改進(jìn)意見和建議,一起進(jìn)步哀军!