iOS開發(fā)-關(guān)于蘋果健康數(shù)據(jù)的獲取

  • 關(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)鍵字NSHealthShareUsageDescriptionNSHealthUpdateUsageDescription砌梆,寫入和讀取都需要。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),如圖:

屏幕快照 2016-08-25 下午5.15.31.png

此外打包證書也需要勾選蘋果健康選項访雪,由于開發(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)步哀军!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末沉眶,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子杉适,更是在濱河造成了極大的恐慌谎倔,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猿推,死亡現(xiàn)場離奇詭異片习,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蹬叭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進(jìn)店門藕咏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人秽五,你說我怎么就攤上這事孽查。” “怎么了坦喘?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵盲再,是天一觀的道長。 經(jīng)常有香客問我瓣铣,道長答朋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任坯沪,我火速辦了婚禮绿映,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己叉弦,他們只是感情好丐一,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著淹冰,像睡著了一般库车。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上樱拴,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天柠衍,我揣著相機(jī)與錄音,去河邊找鬼晶乔。 笑死珍坊,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的正罢。 我是一名探鬼主播阵漏,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼翻具!你這毒婦竟也來了履怯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤裆泳,失蹤者是張志新(化名)和其女友劉穎叹洲,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體工禾,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡运提,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了帜篇。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片糙捺。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖笙隙,靈堂內(nèi)的尸體忽然破棺而出洪灯,到底是詐尸還是另有隱情,我是刑警寧澤竟痰,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布签钩,位于F島的核電站,受9級特大地震影響坏快,放射性物質(zhì)發(fā)生泄漏铅檩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一莽鸿、第九天 我趴在偏房一處隱蔽的房頂上張望昧旨。 院中可真熱鬧拾给,春花似錦、人聲如沸兔沃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乒疏。三九已至额衙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間怕吴,已是汗流浹背窍侧。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留转绷,地道東北人伟件。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像暇咆,于是被迫代替她去往敵國和親锋爪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容