[Apple Watch] 30分で作るヘルスケアWatchOS App

米国ではWatch OS 5.1.2よりECG（心電計）が使えるようになったみたいですね。
早く、日本でも使えるようになってほしいものです。(薬機法とか色々あるので、大変ですが。。。)

ちなみに、ECGが大きくインパクトを与える事例の一つとして、
血栓の原因となりうる心房細動と呼ばれる症状を早期発見し、脳梗塞などの予防が期待されるそうです。
ウェアラブルデバイスの進化により、様々な病気の予防や早期発見に利用されています。

この記事では、ウェアラブルデバイスの代表格であるApple Watchのアプリを、HealthKitを絡めて作ってみたいと思います。
WatchAppとHealthKitの雰囲気をまとめてキャッチアップしていきましょう。

# 作ってみる
今回は**できるだけ**、iOS側のコードを書かずに、WatchOS側だけでHealthStoreの[水分摂取量]を操作するアプリを作ってみましょう。
なお、`Xcode 10.1`を前提にしています。

## Watch Simulatorを起動する
兎にも角にも、まずはWatch SimulatorをXcodeから起動してみます。

1. プロジェクトの作成する
    - Project template では `iOS App with WatchKit App` を選択してください
    - Project optionsではNotificationもComplicationもTestもしないので、全てのチェックを外してOKです。
1. ビルドターゲットをWatch Appにする

    ![スクリーンショット 2018-12-09 6.20.59.png](https://qiita-image-store.s3.amazonaws.com/0/46792/2bf4005e-5225-0a3f-5733-81c5d5e61350.png)
1. シミュレータを指定する

    ![スクリーンショット 2018-12-09 6.24.58.png](https://qiita-image-store.s3.amazonaws.com/0/46792/4eab5182-11a4-9e1a-ada1-44708e4a5bb6.png)
1. 実行する

すると、指定した iPhone と Watch のシミュレータが起動して、Watchの方には黒い画面が表示されます。
これで、WatchAppの開発ができるようになりました:tada: 
![Simulator Screen Shot - Apple Watch Series 4 - 44mm - 2018-12-09 at 06.30.33.png](https://qiita-image-store.s3.amazonaws.com/0/46792/b19a8750-1a94-94cf-743d-bf3657d09a7b.png)

## HealthStoreDataにアクセスできるようにする
それでは次にHealthKitを利用して、HealthStoreDataにアクセスできるようにします。
まずはiOS側の設定です。

1. iOSアプリ側ターゲットのCapabilitiesでHealthKitの設定を有効にする
    ![スクリーンショット 2018-12-09 7.02.41.png](https://qiita-image-store.s3.amazonaws.com/0/46792/1ebaa246-177e-31fe-8aa2-3c960829f85d.png)
1. アプリの`Info.plist`に、なぜアプリがHealthDataを書込・読込したいかを記載する
    - 今回はサンプルなので適当に`hoge`とかでいいです
    - HealthDataのアクセス権限にはWriteとReadがあります
        - Write権限は、書込権限と、このアプリの書込データの読込権限が付与されます
        - Read権限は、全てのアプリの書込データを読み取る権限が付与されます
    
    ```xml
    <key>NSHealthShareUsageDescription</key>
    <string>[読み込む理由をここに記載する]</string>
    <key>NSHealthUpdateUsageDescription</key>
    <string>[書き込む理由をここに記載する]</string>
    ```

1. iOS側に適当にボタンを配置して、ボタンが押された時のコードを以下のように書く

    ```swift
    import UIKit
    import HealthKit

    class ViewController: UIViewController {
        let store = HKHealthStore()

        @IBAction func tapped() {
            // 水分摂取量データにアクセスできるよう、リクエストする
            let sharedTypes: Set<HKSampleType> = [HKSampleType.quantityType(forIdentifier: .dietaryWater)!]
            store.requestAuthorization(toShare: sharedTypes, read: sharedTypes) { _, _ in }
        }
    }
    ``` 

1. 実行ターゲットをiOS側にして、実行する
1. iOS側のSimulatorで設置したボタンをタップする

Health Accessの設定画面が表示されましたね。アクセスを全て許可にしたら、水分摂取量データの読取と書込ができるようになります。

ちなみに、Watch側で HKHealthStore の `requestAuthorization` をコールすると、iPhone側で設定しろって表示されます。
![Simulator Screen Shot - Apple Watch Series 4 - 44mm - 2018-12-09 at 05.26.48.png](https://qiita-image-store.s3.amazonaws.com/0/46792/fd9aa103-c758-5147-6a12-8ab2b3d7444a.png)

## 水分摂取量データをWatchで表示する
1. Watch extensionターゲットのCapabilitiesのHealthKitの設定を有効にする
    ![スクリーンショット 2018-12-09 9.56.58.png](https://qiita-image-store.s3.amazonaws.com/0/46792/027f120d-f4c8-2118-9f27-f2c83b8da072.png)
1. `InterfaceController.swift`に以下のコードを書く

    ```swift
    import WatchKit
    import Foundation
    import HealthKit

    class InterfaceController: WKInterfaceController {
        let store = HKHealthStore()
        // 水摂取量を表すデータタイプ
        let waterType = HKSampleType.quantityType(forIdentifier: .dietaryWater)!
        // 今回は単位をミリリッターに固定
        let unit = HKUnit.literUnit(with: .milli)
        // 水摂取量。更新されたら、Labelを更新する
        var waterIntake: Double = 0 {
            didSet {
                intakeLabel.setText("\(waterIntake) ml")
            }
        }
        
        @IBOutlet weak var intakeLabel: WKInterfaceLabel!

        // 画面が表示される前に実行される
        override func willActivate() {
            loadWaterIntake()
            super.willActivate()
        }

        // HealthStoreから今日の水摂取量データを読込み、waterIntake propertyを更新する
        func loadWaterIntake() {
            let calendar = Calendar(identifier: .gregorian)
            let today = calendar.dateComponents([.year, .month, .day], from: Date())
            let start = calendar.date(from: today)!
            let predicate = HKQuery.predicateForSamples(withStart: start, end: start.addingTimeInterval(60*60*24))
            let query = HKSampleQuery(sampleType: waterType, predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: nil) { _, samples, _ in
                let quantities: [HKQuantitySample]? = samples?.compactMap { $0 as? HKQuantitySample }
                self.waterIntake = quantities?.reduce(0) { $0 + $1.quantity.doubleValue(for: self.unit) } ?? 0
            }
            store.execute(query)
        }
    }
    ```

1. `Interface.storyboard`にLabelを追加する
1. `intakeLabel`と3.で追加したUIパーツを繋げる
    ![スクリーンショット 2018-12-09 22.38.48.png](https://qiita-image-store.s3.amazonaws.com/0/46792/274b84c0-cd41-db4b-1eb6-0acc1bcbf26e.png)

これで、読取ることができるようになりました:tada:
起動してみると、おそらく`0.0 ml`と表示されますので、iOSのシミュレータのHealth Appから水分の値を追加し、再度、Watch Appを起動してみてください。
すると、追加した値が表示されると思います。
（この辺の更新タイミングがまちまちで、追加後すぐにはwatch側で取得することができないことが多かったです。）

## 水分摂取量データをWatchから書き込む
最後に水分摂取量データをWatchから書き込んでみましょう。
Watchの入力インターフェースはかなり貧弱なので、ボタンをタップすると水摂取量を200mlの追加します。（コップ一杯が約200mlらしい）
この辺の値はiOSアプリ側でユーザが設定できるようにしたりすると便利かもしれません。

1. `InterfaceController.swift`に以下のコードを追加する

    ```swift
    @IBAction func tapped() {
        addWaterIntake()
    }
    
    // HealthStoreに水分を200ml摂取したことを書き込み、waterIntakeの値を更新する
    func addWaterIntake() {
        let now = Date()
        let waterIntake = HKQuantitySample(type: waterType, quantity: HKQuantity(unit: unit, doubleValue: 200), start: now, end: now)
        store.save(waterIntake) { _, _ in
            self.loadWaterIntake()
        }
    }
    ```

1. `Interface.storyboard`にButtonを追加する
1. `tapped` Action と 2.で追加したUIパーツを繋げる
    ![スクリーンショット 2018-12-09 23.10.35.png](https://qiita-image-store.s3.amazonaws.com/0/46792/7c6e8114-63bc-3366-c3d2-adfdc5830824.png)

これで、Watchから水摂取量データの追加ができるようになりました:tada:
Watch Appを起動し、ボタンを押すと、200ml追加されます。

# 終わりに
どうでしたでしょうか？ 意外と簡単にできたかと思います。
エラー処理を全部すっ飛ばしてますが、本番コードではきちんと処理してくださいね。
本当はComplicationの表示までやりたかったんですが、ちょっと疲れたので今回はここまで。

HealthKitってどんなデータ扱えるの？って人は以下の記事も参照ください。

- [HealthKitデータ一覧（iOS11まで）](https://qiita.com/sato-shin/items/d7a0099750a0b9eddfd1)