iOS——HealthKit(蘋果健康)的增刪改查

1. plist配置權(quán)限

NSHealthShareUsageDescription:讀取用戶健康數(shù)據(jù)
NSHealthUpdateUsageDescription:更改用戶健康數(shù)據(jù)

2. 設(shè)備支持與授權(quán)

HealthKit是iOS8加入的API
HealthKit在iPad上不可用
通過HKHealthStore類方法 + (BOOL)isHealthDataAvailable;判斷設(shè)備是否支持HealthKit

    BOOL isSupport = [HKHealthStore isHealthDataAvailable];
    if (isSupport)  {
        HKQuantityType *weightType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass];
        HKQuantityType *BodyFat = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyFatPercentage];
        /// 體重和體脂率
        NSSet *set = [NSSet setWithObjects:weightType, BodyFat,nil];
        
        HKHealthStore *healthStore = [[HKHealthStore alloc]init];
        /// ShareTypes寫入權(quán)限申請(qǐng)    readTypes讀取權(quán)限申請(qǐng)
        [healthStore requestAuthorizationToShareTypes:set readTypes:set completion:^(BOOL success, NSError * _Nullable error) {
        
        }];
    }
    
3. 健康數(shù)據(jù)的寫入與讀取

以體重為例,將體重寫入至健康,

健康類型對(duì)象構(gòu)建——HKQuantitySample quantitySampleWithType::::
  • quantityType:數(shù)據(jù)類型(體重)
  • quantity:值類型(kg)
  • startDate:起始時(shí)間(NSDate)
  • endDate:結(jié)束時(shí)間(NSDate),對(duì)于非持續(xù)性的數(shù)據(jù)類型用同一個(gè)就行函荣。
寫入方法——HKQuantitySample saveObject:
  • saveObject:健康類型的數(shù)據(jù)對(duì)象
    HKHealthStore * healthStore = [[HKHealthStore alloc] init];
    HKQuantityType *weightType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass];
    HKQuantity *weightQuantity = [HKQuantity quantityWithUnit:[HKUnit unitFromString:@"kg"] doubleValue:69.3];
    
    theDate = [NSDate date];
    HKQuantitySample *weightSample = [HKQuantitySample quantitySampleWithType:weightType quantity:weightQuantity startDate:theDate endDate:theDate];
        
    [healthStore saveObject:weightSample withCompletion:^(BOOL success, NSError *error) {
            if (!success) {
                NSLog(@"An error occured saving the weight sample %@. In your app, try to handle this gracefully. The error was: %@.", weightSample, error);
                return ;
            }
    }];        

數(shù)據(jù)讀取有多種方式,我主要說下HKSampleQuery(樣本查詢)和HKStatisticsCollectionQuery(統(tǒng)計(jì)集合查詢)

HKSampleQuery 這是使用最多的查詢磅甩。使用樣本查詢來讀取任何類型的樣本數(shù)據(jù)假残。當(dāng)你想要對(duì)結(jié)果進(jìn)行排序或者限制返回的樣本總數(shù)時(shí)米间,樣本查詢就特別有用滩援。更多信息栅隐,參見 HKSampleQuery Class Reference

樣本查詢——HKSampleQuery initWithSampleType
  • SampleType:查詢值類型(體重)
  • predicate:查詢條件(起始-結(jié)束時(shí)間)
  • limit:最大結(jié)果數(shù)(HKObjectQueryNoLimit-不作限制)
  • sortDescriptors:結(jié)果排序(時(shí)間正序)
  • resultsHandler:回調(diào),一般需求遍歷results就行
- (void)testHKSampleQuery {
    
    HKHealthStore *healthStore = [[HKHealthStore alloc]init];
    
    HKQuantityType *sampleType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass];
    
    NSDate *now = [NSDate date];
    NSDate *endDate = [now dateTools_dateByAddingDays:1];
    NSDate *startDate = [endDate dateTools_dateByAddingDays:-100];
    NSPredicate *pre = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionStrictStartDate];
    
    /// 測(cè)試了下key用HKSampleSortIdentifierEndDate  和上述pre的options不一致 也有正常結(jié)果
    NSSortDescriptor *start = [NSSortDescriptor sortDescriptorWithKey:HKSampleSortIdentifierStartDate ascending:YES];
    
    HKSampleQuery *sampleQuery = [[HKSampleQuery alloc] initWithSampleType:sampleType predicate:pre limit:HKObjectQueryNoLimit sortDescriptors:@[start] resultsHandler:^(HKSampleQuery * _Nonnull query, NSArray<__kindof HKSample *> * _Nullable results, NSError * _Nullable error) {
        
        for (HKQuantitySample *result in results) {
            
            double weight = [result.quantity doubleValueForUnit:[HKUnit unitFromString:@"kg"]];
            
            NSString *value = [NSString stringWithFormat:@"%.2f",weight];
                        
            NSString *createTime = [NSString stringWithFormat:@"%ld", (long)[result.startDate timestamp]];
            
            NSLog(@"====value:%@====createTime:%@====", value, createTime);
        }
    }];
    
    /// 執(zhí)行查詢
    [healthStore executeQuery:sampleQuery];
}

HKStatisticsCollectionQuery玩徊,使用這種查詢來在一系列長(zhǎng)度固定的時(shí)間間隔中執(zhí)行多次統(tǒng)計(jì)查詢租悄。通常使用這種查詢來生成圖表。查詢提供了一些簡(jiǎn)單的方法來計(jì)算某些值恩袱,例如泣棋,每天消耗的總熱量或者每5分鐘行走的步數(shù)。統(tǒng)計(jì)集合查詢是長(zhǎng)時(shí)間運(yùn)行的畔塔。查詢可以返回當(dāng)前的統(tǒng)計(jì)集合外傅,也可以監(jiān)測(cè)HealthKit存儲(chǔ)纪吮,并對(duì)更新做出響應(yīng)。更多信息萎胰,參見 HKStatisticsCollectionQuery Class Reference

統(tǒng)計(jì)集合查詢——HKStatisticsCollectionQuery initWithQuantityType
  • quantityType:查詢值類型(體重)
  • quantitySamplePredicate:查詢條件(起始-結(jié)束時(shí)間)
  • options:用于定義執(zhí)行的統(tǒng)計(jì)計(jì)算的類型以及合并來自多個(gè)源的數(shù)據(jù)的方式
    • HKStatisticsOptionSeparateBySource 數(shù)據(jù)來源統(tǒng)計(jì)
    • HKStatisticsOptionDiscreteAverage 平均值統(tǒng)計(jì)
    • HKStatisticsOptionDiscreteMin 最小值統(tǒng)計(jì)
    • HKStatisticsOptionDiscreteMax 最大值統(tǒng)計(jì)
    • HKStatisticsOptionCumulativeSum 和統(tǒng)計(jì)
    • HKStatisticsOptionMostRecent 最近的值
  • anchorDate:統(tǒng)計(jì)數(shù)據(jù)時(shí)間間隔的定位時(shí)間(星期一上午12:00)
  • intervalComponents:統(tǒng)計(jì)數(shù)據(jù)的時(shí)間間隔(3天)
- (void)tesHKStatisticsCollectionQuery {

    HKHealthStore *healthStore = [[HKHealthStore alloc]init];
    // 數(shù)據(jù)類型
    HKQuantityType *type = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass];
    
    // 獲取數(shù)據(jù)的截止時(shí)間 今天
    NSDate *endDate = [NSDate date];
    // 獲取數(shù)據(jù)的起始時(shí)間 此處取從今日往前一年的數(shù)據(jù)
    NSDate *startDate = [NSDate dateWithTimeIntervalSinceNow:-365*24*60*60];
    // 查詢條件棚辽,用于獲取設(shè)置時(shí)間段內(nèi)的數(shù)據(jù)
    NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionStrictStartDate];

    // 設(shè)置時(shí)間支持單位
    NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
    NSDateComponents *anchorComponents = [calendar components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear | NSCalendarUnitWeekday fromDate:[NSDate date]];
    NSInteger offset = (7 + anchorComponents.weekday - 2) % 7;
    /// 日期設(shè)置為星期一上午12:00  也就是第一個(gè)統(tǒng)計(jì)數(shù)據(jù)的開始時(shí)間為星期一12點(diǎn)
    anchorComponents.day -= offset;
    anchorComponents.hour = 12;
    NSDate *anchorDate = [calendar dateFromComponents:anchorComponents];
    
    // 統(tǒng)計(jì)數(shù)據(jù)的時(shí)間間隔(一定要比0大)技竟,例如設(shè)為3,則返回的統(tǒng)計(jì)數(shù)據(jù)跨度為3天屈藐,例如2020-07-10 —— 2020-07-13
    NSDateComponents *intervalComponents = [[NSDateComponents alloc] init];
    intervalComponents.day = 3;

    HKStatisticsCollectionQuery *query = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:type quantitySamplePredicate:predicate options:HKStatisticsOptionSeparateBySource | HKStatisticsOptionDiscreteAverage anchorDate:anchorDate intervalComponents:intervalComponents];
    
    query.initialResultsHandler = ^(HKStatisticsCollectionQuery *query, HKStatisticsCollection *result, NSError *error) {
        
        NSLog(@"%@",[result statistics]);
        for (HKStatistics *sample in [result statistics]) {
            
            double weight = [sample.averageQuantity doubleValueForUnit:[HKUnit unitFromString:@"kg"]];
            
            NSString *value = [NSString stringWithFormat:@"%.2f",weight];
                                    
            NSLog(@"====averageValue:%@====startDate:%@======", value, sample.startDate);
        }
    };
    [healthStore executeQuery:query];
}
4. 健康數(shù)據(jù)的修改

健康的數(shù)據(jù)只有添加和刪除榔组,所謂修改就是刪除一條再添加一條。
我這里的思路联逻,刪除的依據(jù)是時(shí)間搓扯,對(duì)體重?cái)?shù)據(jù)的修改只能修改數(shù)值,不能修改時(shí)間包归,所以查出同時(shí)間的數(shù)據(jù)锨推,刪除舊的,再添加新的公壤。

deleteObject的對(duì)象和saveObject雖然類型一致换可,但是倘若按照saveObject的方法去構(gòu)建,是不能成功刪除的厦幅,所以經(jīng)測(cè)試后只能先查詢沾鳄,再刪除。
- (void)testUpdate {
    
    HKHealthStore * healthStore = [[HKHealthStore alloc] init];
    HKQuantityType *weightType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass];
    
    /// 已添加體重的時(shí)間
    NSDate *theDate = [NSDate dateWithTimeIntervalSince1970:1594608650];
    /// 修改后的數(shù)值
    double weight  = 75;
    HKQuantity *weightQuantity = [HKQuantity quantityWithUnit:[HKUnit unitFromString:@"kg"] doubleValue:weight];
    
    HKQuantitySample *weightSample = [HKQuantitySample quantitySampleWithType:weightType quantity:weightQuantity startDate:theDate endDate:theDate];
    
    ///查詢條件 指定時(shí)間~指定時(shí)間+1S  查詢條數(shù)為1
    NSPredicate *pre = [HKQuery predicateForSamplesWithStartDate:theDate endDate:[theDate dateTools_dateByAddingSeconds:1] options:HKQueryOptionStrictStartDate];
    NSSortDescriptor *start = [NSSortDescriptor sortDescriptorWithKey:HKSampleSortIdentifierStartDate ascending:YES];
    
    HKSampleQuery *sampleQuery = [[HKSampleQuery alloc] initWithSampleType:weightType predicate:pre limit:1 sortDescriptors:@[start] resultsHandler:^(HKSampleQuery * _Nonnull query, NSArray<__kindof HKSample *> * _Nullable results, NSError * _Nullable error) {
        //打印查詢結(jié)果
        NSLog(@"%@",results);
        if (results.count > 0) {
            HKQuantitySample *result = results.firstObject;
            [healthStore deleteObject:result withCompletion:^(BOOL success, NSError * _Nullable error) {
                if (!success) {
                    NSLog(@"An error occured delete the weight sample %@. In your app, try to handle this gracefully. The error was: %@.", weightSample, error);
                }
                [healthStore saveObject:weightSample withCompletion:^(BOOL success, NSError *error) {
                    if (!success) {
                        NSLog(@"An error occured saving the weight sample %@. In your app, try to handle this gracefully. The error was: %@.", weightSample, error);
                    }
                }];
            }];
        } else {
            [healthStore saveObject:weightSample withCompletion:^(BOOL success, NSError *error) {
                if (!success) {
                    NSLog(@"An error occured saving the weight sample %@. In your app, try to handle this gracefully. The error was: %@.", weightSample, error);
                }
            }];
        }
    }];
    [healthStore executeQuery:sampleQuery];
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末确憨,一起剝皮案震驚了整個(gè)濱河市译荞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌休弃,老刑警劉巖吞歼,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異玫芦,居然都是意外死亡浆熔,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門桥帆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來医增,“玉大人,你說我怎么就攤上這事老虫∫豆牵” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵祈匙,是天一觀的道長(zhǎng)忽刽。 經(jīng)常有香客問我天揖,道長(zhǎng),這世上最難降的妖魔是什么跪帝? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任今膊,我火速辦了婚禮,結(jié)果婚禮上伞剑,老公的妹妹穿的比我還像新娘斑唬。我一直安慰自己,他們只是感情好黎泣,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布恕刘。 她就那樣靜靜地躺著,像睡著了一般抒倚。 火紅的嫁衣襯著肌膚如雪褐着。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天托呕,我揣著相機(jī)與錄音含蓉,去河邊找鬼。 笑死镣陕,一個(gè)胖子當(dāng)著我的面吹牛谴餐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播呆抑,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼岂嗓,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了鹊碍?” 一聲冷哼從身側(cè)響起厌殉,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎侈咕,沒想到半個(gè)月后公罕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡耀销,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年楼眷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片熊尉。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡罐柳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出狰住,到底是詐尸還是另有隱情张吉,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布催植,位于F島的核電站肮蛹,受9級(jí)特大地震影響勺择,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜伦忠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一省核、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧昆码,春花似錦芳撒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽芥备。三九已至冬耿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間萌壳,已是汗流浹背亦镶。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留袱瓮,地道東北人缤骨。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像尺借,于是被迫代替她去往敵國(guó)和親绊起。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354