初探HealthKit 獲取步數

[iOS] HealthKit 獲取步數

前言

HealthKit 是蘋果在 iOS 8.0 之后推出的健康框架,HealthKit框架提供了一個結構,應用可以使用它來分享健康和健身數據。

設計思路

HealthKit 是用來在應用間以一種有意義的方式共享數據。為了達到這點,框架限制只能使用預先定義好的數據類型和單位。

框架還大量使用了子類化,在相似的類間創建層級關系。通常這些類間都有一些細微但是重要的差別。還有不少很相關的類,需要正確地區別開才能一起工作。例如 HKObject 和 HKObjectType抽象類有很多平行層級的子類。當使用 object 和 object type 時,必須確保使用匹配的子類。

HKObject

HealthKit中所有的對象都是HKObject的子類。
大部分 HKObject 對象子類都是不可變的。每個對象都有下列屬性:

- UUID。每個對象的唯一標示符。
- Source Revision。數據的來源。來源可以是直接把數據存進HealthKit的設備,或者是應用。當一個對象保存進HealthKit中時,HealthKit會自動設置其來源。只有從HealthKit中獲取的數據source屬性才可用。
-  Metadata。一個包含關于該對象額外信息的字典。元數據包含預定義和自定義的鍵。預定義的鍵用來幫助在應用間共享數據。自定義的鍵用來擴展HealthKit對象類型,為對象添加針對應用的數據。
- Device. 在此 sampler 生成的數據存儲硬件設備。
HKSample

HealthKit對象主要分為2類:特征和樣本。特征對象代表一些基本不變的數據。包括用戶的生日、血型和生理性別。你的應用不能保存特征數據。用戶必須通過健康應用來輸入或者修改這些數據。所有的樣本對象都是HKSample的子類。它們都有下列屬性:

- Type。      // 樣本類型。例如,這可能包括一個睡眠分析樣本、一個身高樣本或者一個計步樣本。

- Start date。// 樣本的開始時間。

- End date。  // 樣本的結束時間。如果樣本代表時間中的某一刻,結束時間和開始時間相同。如果樣本代表一段時間內收集的數據,結束時間應該晚于開始時間。
HKObjectType 含有一些樣本類型
  • HKCategorySample 類別樣本。這種樣本代表一些可以被分為有限種類的數據。在iOS8.0中,只有一種類別樣本,睡眠分析。更多信息,參見 HKCategorySample Class Reference

  • HKQuantitySample 數量樣本。這種樣本代表一些可以存儲為數值的數據。數量樣本是HealthKit中最常見的數據類型。這些包括用戶的身高和體重,還有一些其他數據,例如行走的步數,用戶的體溫和脈搏率。更多信息,參見HKQuantitySample Class Reference

  • Correlation 符合數據。這種樣本代表復合數據,包含一個或多個樣本。在iOS8.0中,HealthKit使用 correlation來代表食物和血壓。在創建書屋或者血壓數據時,你應該使用 correlation。更多信息,參見 HKCorrelation Class Reference

  • Workout。Workout 代表某些物理活動,像跑步、游泳,甚至游戲。Workout 通常有類型、時長、距離、和消耗能量這些屬性。你還可以為一個 workout 關聯許多詳細的樣本。不像 correlation,這些樣本是不包含在 workout 里的。但是,它們可以通過 workout 獲取到。更多信息,HKWorkout Class Reference

設置HealthKit

  • 要在Xcode中打開HealthKit功能。
設置權限

打開權限
 // 打開之后,在 Bundle 里會多一個 .entitlements 文件。
  • 調用 isHealthDataAvailable 方法來查看HealthKit在該設備上是否可用。

     if ([HKHealthStore isHealthDataAvailable]) {
     // add code to use HealthKit here...
     }
    
  • 為你的應用實例化一個 HKHealthStore 對象。每個應用只需要一個HealthKit存儲實例。這個存儲實例就是你和HealthKit數據庫交互的主要接口。

     self.healthStore = [[HKHealthStore alloc] init];
    
  • 使用 requestAuthorizationToShareTypes:readTypes:completion:方法來請求獲取HealthKit數據的權限。對每種類型的數據,你都必須請求許可來共享和讀取。

      // 設置共享數據類型
      HKQuantityType *sampleType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
      NSSet *shareSet = [NSSet setWithObjects:sampleType, nil]; 
      
      // 設置寫入數據類型
      HKQuantityType *read = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
      NSSet *readSet = [NSSet setWithObjects:read, nil];
      
      [self.healthStore requestAuthorizationToShareTypes:shareSet readTypes:readSet completion:^(BOOL success, NSError * _Nullable error) {
          if (success) {
              NSLog(@" 請求權限成功");
          } else{
              NSLog(@"error %@", error);
          }
      }];
    

讀取HealthKit數據

有三種主要的方式來訪問數據從HealthKit存儲:

  • 直接方法調用。HealthKit 提供了直接讀取特征數據的方法。這些方法只能用于讀取特征數據。HKHealthStore
  • 查詢
    • 樣本查詢(Sample query )。這是使用最多的查詢。使用樣本查詢來讀取任何類型的樣本數據。當你想要對結果進行排序或者限制返回的樣本總數時,樣本查詢就特別有用。HKSampleQuery

    • 固定對象查詢。用這種查詢來搜索添加進存儲的項。當錨定查詢第一次執行時,會返回存儲中所有匹配的樣本。在接下來的執行中,只會返回上一次執行之后添加的項目。通常,錨定對象查詢會和觀察者查詢一起使用。觀察者查詢告訴你某些項目發生了變化,而錨定對象查詢來決定有哪些(如果有的話)項目被添加進了存儲。HKAnchoredObjectQuery

    • 統計查詢。使用這種查詢來在一系列匹配的樣本中執行統計運算。你可以使用統計查詢來計算樣本的總和、最小值、最大值或平均值。HKStatisticsQuery

    • 統計集合查詢。使用這種查詢來在一系列長度固定的時間間隔中執行多次統計查詢。通常使用這種查詢來生成圖表。查詢提供了一些簡單的方法來計算某些值,例如,每天消耗的總熱量或者每5分鐘行走的步數。統計集合查詢是長時間運行的。查詢可以返回當前的統計集合,也可以監測HealthKit存儲,并對更新做出響應。HKStatisticsCollectionQuery

    • Correlation 查詢。使用這種查詢來在 correlation 查找數據。這種查詢可以為 correlation 中每個樣本類型包含獨立的謂詞。如果你只是想匹配 correlation 類型,那么請使用樣本查詢。 HKCorrelation

    • 來源查詢。使用這種查詢來查找 HealthKit 存儲中的匹配數據的來源(應用和設備)。來源查詢會列出儲存的特定樣本類型的所有來源。HKSourceQuery

    • 活動匯總查詢。 使用這個可以查詢用戶活動的總結信息。每個活動匯總對象包含給定天數的用戶活動總結,你可以查詢一天或者多天。更多信息見,HKActivitySummaryQuery

  • 長時間運行的查詢。 這些查詢隊列繼續運行在一個匿名的后臺線程中,并且無論何時改變了 HealthKit 數據,都能更新其應用。此外,觀察者查詢能注冊到后臺中。因而能使得在更新的時候,HealthKit 能在后臺喚醒你的 app。
    • 觀察者查詢。這是一個長時間運行的查詢,它會檢測HealthKit存儲,并在匹配到的樣本發生變化時通知你。如果當存儲發生變化時你想得到通知,就使用觀察者查詢。你可以讓這些查詢在后臺執行。HKObserverQuery

      每種數據調用的查詢的方法不一樣,類型也要相應的匹配。
      NSDate *startDate, *endDate;
      endDate = [NSDate date];
      NSCalendar *calendar = [NSCalendar currentCalendar];
      NSDate *now = [NSDate date];
      startDate = [calendar startOfDayForDate:now];
      endDate = [calendar dateByAddingUnit:NSCalendarUnitDay value:1 toDate:startDate options:0];
      // NSLog(@"startDate %@, endDate %@", startDate, now);

       HKSampleType *sampleType = [HKSampleType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
      
       //  創建query, 設置 start 和 end 謂詞 Create a predicate to set start/end date bounds of the query
       NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionStrictStartDate];
      
       // Create a sort descriptor for sorting by start date
       NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:HKSampleSortIdentifierStartDate ascending:YES];
       __weak UILabel *numberLabel = self.showView.existStpNumberLabel;
        HKSampleQuery *sampleQuery = [[HKSampleQuery alloc] initWithSampleType:sampleType predicate:predicate limit:HKObjectQueryNoLimit sortDescriptors:@[sortDescriptor] resultsHandler:^(HKSampleQuery * _Nonnull query, NSArray<__kindof HKSample *> * _Nullable results, NSError * _Nullable error) {
      
        if(!error && results){
        
            CGFloat sum = 0.0f;
            for(HKQuantitySample *sample in results)
            {
           
           //
           NSLog(@"%@", sample);
           HKUnit *unit = [HKUnit unitFromString:@"count"];
           CGFloat number = [sample.quantity doubleValueForUnit:unit];
           sum += number;
           
         }
          dispatch_async(dispatch_get_main_queue(), ^{
              numberLabel.text = @(sum).description;
          });
       }
       }];
       [self.healthStore executeQuery:sampleQuery];
      

向 HealthKit Store 中添加樣本

你需要創建一個新的樣本對象,并將它添加到實例化的 Health Store 中去。樣本類型都是相似的類型,只是在主體上可能有一些變化。

  • 在 HealthKit Constants Reference 中找到正確的類型標識符。HealthKit

  • 使用類型標識符創建一個匹配的 HKObjectType 子類。有一些便捷的方法,參見 HKObjectType Class Reference

  • 使用對象類型,創建一個匹配的 HKSample 子類。

  • 使用 saveObject:withCompletion: 方法將對象保存到HealthKit 存儲中。

    對于數量樣本,你必須創建一個 HKQuantity 類的實例。數量的單位必須和類型標識符文檔中描述的可用單位相關。例如,HKQuantityTypeIdentifierHeight 文檔中說明它使用長度單位,因此,你的數量必須使用厘米、米、英尺、英寸或者其他長度單位。HKQuantitySample

    對于類別樣本,它的值必須和類型標識符文檔中描述的枚舉值相關。例如, HKCategoryTypeIdentifierSleepAnalysis 文檔中說明它使用 HKCategoryValueSleepAnalysis 枚舉值。因此你在創建樣本時必須從這個枚舉中傳遞一個值。HKCategorySample

    對于correlation,你必須先創建correlation包含的所有樣本。correlation的類型標識符描述了它可以包含的類型和對象的數量。不要把被包含的對象存進HealthKit。它們是以correlation的一部分存儲的。 HKCorrelation

    workout和其他類型的樣本有些不同。首先,創建 HKWorkoutType 實例并不需要指定類型標識符。所有的workout都是用同樣的類型標識符。第二,對于每個workout你都需要提供一個 HKWorkoutActivityType 值。這個值定義了workout中執行的活動的類型。最后,當workout保存到HealthKit后,你可以給workout關聯額外的樣本。這些樣本提供了workout的詳細信息。HKWorkout

    性別,年齡,
    NSError *error;
    HKBiologicalSexObject *bioSex = [healthStore biologicalSexWithError:&error];
    switch (bioSex.biologicalSex) {
    case HKBiologicalSexNotSet:
    // undefined
    break;
    case HKBiologicalSexFemale:
    // ...
    break;
    case HKBiologicalSexMale:
    // ...
    break;
    }
    
    體重
    // Some weight in gram
    double weightInGram = 83400.f;
    // Create an instance of HKQuantityType and
    // HKQuantity to specify the data type and value
    // you want to update
    NSDate          *now = [NSDate date];
    HKQuantityType  *hkQuantityType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass];
    HKQuantity      *hkQuantity = [HKQuantity quantityWithUnit:[HKUnit gramUnit] doubleValue:weightInGram];
    
    // Create the concrete sample
    HKQuantitySample *weightSample = [HKQuantitySample quantitySampleWithType:hkQuantityType
    quantity:hkQuantity
    startDate:now
    endDate:now];
    
    // Update the weight in the health store
    [healthStore saveObject:weightSample withCompletion:^(BOOL success, NSError *error) {
    // ..
    }];
    
    // 獲取步數
    NSDate *eDate = [NSDate date];
    NSDate *sDate = [NSDate dateWithTimeInterval:-300 sinceDate:eDate];
    HKQuantity *stepQuantityConsumed = [HKQuantity quantityWithUnit:[HKUnit countUnit] doubleValue:self.showView.addStepNumberTextField.text.doubleValue];
    HKQuantityType *stepConsumedType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
    
    NSString *strName = [[UIDevice currentDevice] name];
    NSString *strModel = [[UIDevice currentDevice] model];
    NSString *strSysVersion = [[UIDevice currentDevice] systemVersion];
    NSString *localeIdentifier = [[NSLocale currentLocale] localeIdentifier]; 
    
    HKDevice *device = [[HKDevice alloc] initWithName:strName manufacturer:@"Apple" model:strModel hardwareVersion:strModel firmwareVersion:strModel softwareVersion:strSysVersion localIdentifier:localeIdentifier UDIDeviceIdentifier:localeIdentifier];
    HKQuantitySample *stepConsumedSample = [HKQuantitySample quantitySampleWithType:stepConsumedType quantity:stepQuantityConsumed startDate:sDate endDate:eDate device:device metadata:nil];
    
    [self.healthStore saveObject:stepConsumedSample withCompletion:^(BOOL success, NSError * _Nullable error) {
    dispatch_async(dispatch_get_main_queue(), ^{
       
       if (success) {
           [self.view endEditing:YES];
           UIAlertView *doneAlertView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"添加成功" delegate:nil cancelButtonTitle:@"確定" otherButtonTitles:nil, nil];
           [doneAlertView show];
           //刷新數據  重新獲取步數
           [self getTodyStepNumberForHealthKit];
       }else {
           NSLog(@"The error was: %@.", error);
           UIAlertView *doneAlertView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"添加失敗" delegate:nil cancelButtonTitle:@"確定" otherButtonTitles:nil, nil];
           [doneAlertView show];
           return ;
       }
     });
    }];
    

單位換算

  • HealthKit使用 HKUnit 和 HKQuantity 類來支持單位。HKUnit 提供了單一單位的表示。它支持大部分的公制和英制單位,當然還包括基本單位和符合單位。基本單位代表單一的度量,例如米、磅或者秒。復合單位使用數學運算連接一個或多個基本單位,例如m/s或者lb/ft2。
  • HKUnit 提供了便捷方法來創建HealthKit支持的所有基本單位。它還提供了構建復合單位需要的數學運算。最后,你還可以通過直接使用恰當的格式化的單位字符串來創建復合單位。

  • HKQuantity 類存儲了給定單位的值。之后你可以用任何兼容的單位來取值。這樣,你的應用就可以很輕松的完成單位換算。
    在某些場合,你可以使用格式化器來本地化數量。iOS8提供了提供了新的格式化器來處理長度(NSLengthFormatter)、質量(NSMassFormatter)和能量(NSEnergyFormatter)。對于其他的數量,你需要自己來換算單位和本地化數據

demo

demo鏈接

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容