25
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ヘルスケアAdvent Calendar 2018

Day 10

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

Last updated at Posted at 2018-12-09

米国では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です。
  2. ビルドターゲットをWatch Appにする

    スクリーンショット 2018-12-09 6.20.59.png

  3. シミュレータを指定する

    スクリーンショット 2018-12-09 6.24.58.png

  4. 実行する

すると、指定した iPhone と Watch のシミュレータが起動して、Watchの方には黒い画面が表示されます。
これで、WatchAppの開発ができるようになりました:tada:
Simulator Screen Shot - Apple Watch Series 4 - 44mm - 2018-12-09 at 06.30.33.png

HealthStoreDataにアクセスできるようにする

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

  1. iOSアプリ側ターゲットのCapabilitiesでHealthKitの設定を有効にする
    スクリーンショット 2018-12-09 7.02.41.png

  2. アプリのInfo.plistに、なぜアプリがHealthDataを書込・読込したいかを記載する

    • 今回はサンプルなので適当にhogeとかでいいです
    • HealthDataのアクセス権限にはWriteとReadがあります
      • Write権限は、書込権限と、このアプリの書込データの読込権限が付与されます
      • Read権限は、全てのアプリの書込データを読み取る権限が付与されます
    <key>NSHealthShareUsageDescription</key>
    <string>[読み込む理由をここに記載する]</string>
    <key>NSHealthUpdateUsageDescription</key>
    <string>[書き込む理由をここに記載する]</string>
    
  3. iOS側に適当にボタンを配置して、ボタンが押された時のコードを以下のように書く

    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 }
        }
    }
    
  4. 実行ターゲットをiOS側にして、実行する

  5. iOS側のSimulatorで設置したボタンをタップする

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

ちなみに、Watch側で HKHealthStore の requestAuthorization をコールすると、iPhone側で設定しろって表示されます。
Simulator Screen Shot - Apple Watch Series 4 - 44mm - 2018-12-09 at 05.26.48.png

水分摂取量データをWatchで表示する

  1. Watch extensionターゲットのCapabilitiesのHealthKitの設定を有効にする
    スクリーンショット 2018-12-09 9.56.58.png

  2. InterfaceController.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)
        }
    }
    
  3. Interface.storyboardにLabelを追加する

  4. intakeLabelと3.で追加したUIパーツを繋げる
    スクリーンショット 2018-12-09 22.38.48.png

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

水分摂取量データをWatchから書き込む

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

  1. InterfaceController.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()
        }
    }
    
  2. Interface.storyboardにButtonを追加する

  3. tapped Action と 2.で追加したUIパーツを繋げる
    スクリーンショット 2018-12-09 23.10.35.png

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

終わりに

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

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

25
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?