Swift と HealthKit で栄養情報をためていく

こんにちは、大阪のお好み焼屋です。お好み焼は最高ですが、Swiftも最高ですね。そんなわけで先日リリースした、[お好み焼の栄養成分をヘルスケア（Health.app）へ送るApp、その名もレコノミー](https://itunes.apple.com/jp/app/reconome/id1039641444)に絡めて、HealthKitをSwiftで利用する話です。

## HealthKit

iOS8で新たに加わったHealthKit。いまいち盛り上がっている感じもありませんが、とりあえずデータをためておけば後でなにかいいことがあるかも・・・というわけで、iOS9で加えられた`HKCorrelationTypeIdentifierFood`をつかって複数の栄養素をまとめて「メニュー単品」としてHealth.appへ送ることにします。ここではサンプルとしてうち自慢のイカ玉をつかいます。

[サンプルコード](https://github.com/haruhikoM/YukiNoIkatama)と参考スクリーンショット

![Simulator Screen Shot Dec 19, 2015, 23.38.18.png](https://qiita-image-store.s3.amazonaws.com/0/32738/f6f9c546-ef86-f88d-1ed9-dffabffee85c.png)



### 主な登場Class

| HealthKit ||
|:------------|:------------|
|      HKUnit|単位|
|      HKQuantity|量|
|      HKQuantityType|量のタイプ|
|      HKQuantitySample|モノ|

「モノ」というのがよくわからないですが、とりあえずの目標がこれだと思ってください。

### HKUnit, HKQuantity

順番が前後しますが、まずは`HKQunatity`の初期化メソッドです

```swift:
init(unit unit: HKUnit!, doubleValue value: Double)
```

Unitは単位で、Quantityは量のことですね。`unit`単位に`doubleValue`の値を与えてやることで量になります。100.0という数（`Double`）に単位グラム（`HKUnit`）を与えてやることで、「100グラム」という量になるわけです。

unitの初期化はいろいろなやり方が用意されています

```swift:
HKUnit.percentUnit()
HKUnit(fromString: "cm")
HKUnit.gramUnitWithMetricPrefix(.Kilo)
```

ひとつめはいわゆるConvenience Constructor的なやつで、ここでは「％」ですが、他にも結構な数が用意されています。

2つめは単位を文字列で表す方法（ここではセンチメートル）。”m/s^2”（加速度｜$m/s^2$）なんかも可能。

3つめは読んだ通り「メートル法のグラム単位をキロで」。これでキログラムになります。

```swift:
let kcalUnit = HKUnit.kilocalorieUnit()
let quantity = HKQuantity(unit: kcalUnit, doubleValue: 347)
```
### HKQuantityType, HKQuantitySample

次に量のタイプです。カロリーと一口にいっても、摂取したカロリーなのか（`HKQuantityTypeIdentifierDietaryEnergyConsumed`）、運動で消費したカロリーなのか（`HKQuantityTypeIdentifierActiveEnergyBurned`）、はたまた基礎エネルギーの消費なのか（`HKQuantityTypeIdentifierBasalEnergyBurned`）を、`HKQuantityType`でしっかり指定してやります。

これでようやく必要な要素がそろってHKQuantitySampleのインスタンス化ができるようになりました。

```swift:
let type = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDietaryEnergyConsumed)!
let date = NSDate()
let ikatamaEnergySample = HKQuantitySample(type: type, quantity: quantity, startDate: date, endDate: date)
```

これでイカ玉のカロリーが完成です。あとは同じ手順で他の必要な栄養素を**単位に気をつけつつ**インスタンス化していきます。

たとえばプロテイン（蛋白質）はこんな具合

```swift:
let gramUnit = HKUnit.gramUnit()
let proteinQuantity = HKQuantity(unit: gramUnit, doubleValue: 18.9)
let proteinType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDietaryProtein)!
let ikatamaProteinSample = HKQuantitySample(type: proteinType, quantity: proteinQuantity, startDate: date, endDate: date)
```

### HKCorrelation

コードを見ていただければ一目瞭然だとおもいます。

```swift:
var itemSet = Set<HKQuantitySample>() 
itemSet.insert(ikatamaEnergySample)
//// 必要な栄養のサンプルを`insert(_)`していく
let foodType = HKCorrelationType.correlationTypeForIdentifier(HKCorrelationTypeIdentifierFood)!
let ikatama = HKCorrelation(type: foodType, startDate: date, endDate: date, objects: itemSet, metadata: metadata)
```

### HKHealthStore

あとはストアのアベイラビリティをチェックして、ストアへのアクセスをリクエストしてやります。フレームワークが面倒をみてくれます。

```swift:
guard HKHealthStore.isHealthDataAvailable() else { return }
let healthStore = HKHealthStore()

let typeSet: Set<HKSampleType> = [energyType, proteinType]
healthStore.requestAuthorizationToShareTypes(typeSet, readTypes: nil) { success, error -> Void in
  // if request is approved
  healthStore.saveObject(ikatama) { success, error in
    if success { print("DONE!!") }
  }
}
```

無事送れています。

![Simulator Screen Shot Dec 20, 2015, 17.40.31.png](https://qiita-image-store.s3.amazonaws.com/0/32738/fbdfd8ce-84ee-da7f-ba9e-80e75ad8f515.png)


## 最後に

あわてて書いたのであまりSwiftっぽい話はできませんでしたが、[レコノミー](https://itunes.apple.com/jp/app/reconome/id1039641444)内では`enum`を使いまくったりして、とても楽しく書くことができています。また機会があればenum+protocolの話も書ければと思っています。ありがとうございました。