Healthkitから脈拍を取得するapple watchアプリ

Healthkitから脈拍を取得するapple watchアプリの作り方です。
Swiftで実装しています。 => <a href="https://github.com/TECHFUND/health">Githubはコチラ</a>

現在の仕様として、脈拍等のhealth dataは直接取れません。
（Appleさん、早く直接値が取れるようになることを切に願っております）


そのため
![スクリーンショット 2015-01-17 20.36.23.png](https://qiita-image-store.s3.amazonaws.com/0/44837/949c68cc-f188-b804-349d-226c7efb0fcc.png)

脈拍値をAppleWatchのセンサーで取得（Apple公式アプリを想定）→iPhone上のHealthKitに値を保存→HealthKitの値を取得し表示する
という仕様になります。ただし、センサーで値取得〜HealthKitに保存部分は
実機が無いとできないため、今回はデモ値をアプリからHealthKitに書き込んで実装しています。

<h3>0.準備</h3>
・WatchKit ExtensionをAdd Target
※Xcode6.2beta以上である必要があります
Target > Add Target > メニュー内のApple WatchにあるWatchKit Appを選択してNext > Finish

・App Group登録
iPhone側からAppleWatchにデータを渡すためにUserDefaultを使用するので、AppGroup登録を行う必要があります。
```iOS Dev Center(Web) > Certificates > Identifiers > App Groups > App Groups Description / Identifierを登録（例　techfund / group.jp.techfund　等）```
![スクリーンショット 2015-01-19 13.51.11.png](https://qiita-image-store.s3.amazonaws.com/0/44837/3e27dc23-26c9-55e0-b1c5-2657886a8f11.png)

AppGroupsが設定できたら、TargetでApp GroupsとHealthkitを有効化します。
```health Target > Capabilities > App Groups と HealthKit を ON　
App Groups:のgroup.jp.techfundにチェックを入れる
※health WatchKit Extensionも同様に有効化すること```
![Healthxs.004.jpg](https://qiita-image-store.s3.amazonaws.com/0/44837/01cc8a0c-1df9-4554-fbcb-4877fa7519cb.jpeg)

・HealthKit Framework読み込み
プロジェクト > General > Linked Frameworks and Librariesに「+」 > HealthKitを検索 > Add
![スクリーンショット 2015-01-19 14.13.27.png](https://qiita-image-store.s3.amazonaws.com/0/44837/76a6b078-2f45-2989-a889-1b295ea5ae2e.png)

・Interface.StoryboardにLabelを２つ配置、サイズ変更等
![スクリーンショット 2015-01-19 17.34.38.png](https://qiita-image-store.s3.amazonaws.com/0/44837/829a4cd4-7ba7-8d1f-29a1-89fff7c66962.png)


<h3>1.iPhone側でHealthkitの値を取得</h3>
デモ用に書き込みボタンを用意し、読み込み時にはUser Defaultへ平均値を書き込む処理を入れています。

```lang:ViewController.swift
import UIKit
import HealthKit

class ViewController: UIViewController, UITextFieldDelegate  {
    
    var myHealthStore : HKHealthStore!
    var myReadHeartField: UITextField!
    var myWriteHeartField: UITextField!
    var myReadButton: UIButton!
    var myWriteButton: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // HealthStoreの生成
        myHealthStore = HKHealthStore()
        
        // Write用
        myWriteHeartField = UITextField(frame: CGRectMake(0,0,300,30))
        myWriteHeartField.text = "80"
        myWriteHeartField.delegate = self
        myWriteHeartField.borderStyle = UITextBorderStyle.RoundedRect
        myWriteHeartField.layer.position = CGPoint(x:self.view.bounds.width/2,y:50);
        self.view.addSubview(myWriteHeartField)
        
        // Read用
        myReadHeartField = UITextField(frame: CGRectMake(0,0,300,30))
        myReadHeartField.text = ""
        myReadHeartField.delegate = self
        myReadHeartField.borderStyle = UITextBorderStyle.RoundedRect
        myReadHeartField.layer.position = CGPoint(x:self.view.bounds.width/2,y:100);
        self.view.addSubview(myReadHeartField)
        
        // 読み込みボタン
        myReadButton = UIButton()
        myReadButton.frame = CGRectMake(0,0,200,40)
        myReadButton.backgroundColor = UIColor.redColor();
        myReadButton.layer.masksToBounds = true
        myReadButton.setTitle("脈拍データ読み込み", forState: UIControlState.Normal)
        myReadButton.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
        myReadButton.setTitleColor(UIColor.blackColor(), forState: UIControlState.Highlighted)
        myReadButton.layer.cornerRadius = 20.0
        myReadButton.layer.position = CGPoint(x: self.view.frame.width/2, y:200)
        myReadButton.tag = 1
        myReadButton.addTarget(self, action: "onClickMyButton:", forControlEvents: .TouchUpInside)
        self.view.addSubview(myReadButton);
        
        // 書き込みボタン
        myWriteButton = UIButton()
        myWriteButton.frame = CGRectMake(0,0,200,40)
        myWriteButton.backgroundColor = UIColor.blueColor();
        myWriteButton.layer.masksToBounds = true
        myWriteButton.setTitle("脈拍データ書き込み", forState: UIControlState.Normal)
        myWriteButton.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
        myWriteButton.setTitleColor(UIColor.blackColor(), forState: UIControlState.Highlighted)
        myWriteButton.layer.cornerRadius = 20.0
        myWriteButton.layer.position = CGPoint(x: self.view.frame.width/2, y:250)
        myWriteButton.tag = 2
        myWriteButton.addTarget(self, action: "onClickMyButton:", forControlEvents: .TouchUpInside)
        self.view.addSubview(myWriteButton);
        
    }
    
    // ボタンイベント
    func onClickMyButton(sender: UIButton){
        if(sender.tag == 1){
            readData()
            
        }
        else if(sender.tag == 2){
            let myHeartStr: NSString = myWriteHeartField.text
            writeData(myHeartStr.doubleValue)
            
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    
    private func requestAuthorization() {
        // 書き込みを許可する型.
        let typeOfWrite = [HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)]
        let typeOfWrites = NSSet(array: typeOfWrite)
        
        // 読み込みを許可する型.
        let typeOfRead = [HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)]
        let typeOfReads = NSSet(array: typeOfRead)
        
        //  HealthStoreへのアクセス承認をおこなう.
        self.myHealthStore.requestAuthorizationToShareTypes(typeOfWrites, readTypes: typeOfReads, completion: {
            (success: Bool, error: NSError!) in
            if success {
                println("Success!")
            } else {
                println("Error!")
            }
        })
    }
    
    // 心拍数を取得
    private func readData() {
        var error: NSError?
        
        let typeOfHeart = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)
        
        // 日付処理
        let calendar: NSCalendar! = NSCalendar.currentCalendar()
        let now: NSDate = NSDate()
        let startDate: NSDate = calendar.startOfDayForDate(now)
        let endDate: NSDate = calendar.dateByAddingUnit(NSCalendarUnit.CalendarUnitDay, value: 1, toDate: startDate, options: nil)!
        let predicate: NSPredicate = HKQuery.predicateForSamplesWithStartDate(startDate, endDate: endDate, options: HKQueryOptions.StrictStartDate)
        
        let statsOptions: HKStatisticsOptions = (HKStatisticsOptions.DiscreteMin | HKStatisticsOptions.DiscreteMax)
        
        let query: HKStatisticsQuery = HKStatisticsQuery(quantityType: typeOfHeart,
            quantitySamplePredicate: predicate, options: statsOptions, completionHandler: {
                (query: HKStatisticsQuery!, result: HKStatistics!, error: NSError!) in
                dispatch_async(dispatch_get_main_queue(),{
                    self.myReadHeartField.text = "最小:\(result.minimumQuantity()) 最大:\(result.maximumQuantity())"
                    
                    // 読み込み時にデータをuser defaultに保存
                    var myHeartStr = result.averageQuantity()
                    var def = NSUserDefaults(suiteName: "group.jp.techfund")
                    def?.setObject(myHeartStr, forKey: "heartRate")
                })
        })
        
        
        
        self.myHealthStore.executeQuery(query)
    }
    
    // 心拍数データを保存
    private func writeData(heart:Double){
        
        // Save the user's heart rate into HealthKit.
        let heartRateUnit: HKUnit = HKUnit.countUnit().unitDividedByUnit(HKUnit.minuteUnit())
        let heartRateQuantity: HKQuantity = HKQuantity(unit: heartRateUnit, doubleValue: heart)
        
        var heartRate : HKQuantityType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)
        let nowDate: NSDate = NSDate()
        
        let heartRateSample: HKQuantitySample = HKQuantitySample(type: heartRate
            , quantity: heartRateQuantity, startDate: nowDate, endDate: nowDate)
        
        let completion: ((Bool, NSError!) -> Void) = {
            (success, error) -> Void in
            
            if !success {
                println("An error occured saving the Heart Rate \(heartRateSample). In your app, try to handle this gracefully. The error was: \(error).")
                abort()
            }
        }
        self.myHealthStore!.saveObject(heartRateSample, withCompletion: completion)
    }
}
```

<h3>2.AppleWatch側で表示</h3>
UserDefaultから値を取得して、Switchで条件分けを行いハートを表示します。
※Swiftでは絵文字を変数に入れることができるため、赤・青・黄色のハートの絵文字を入れています

```lang:InterfaceController.swift
import WatchKit
import Foundation

class InterfaceController: WKInterfaceController {

    @IBOutlet weak var label1: WKInterfaceLabel!
    @IBOutlet weak var label2: WKInterfaceLabel!
    
    override func awakeWithContext(context: AnyObject?) {
        super.awakeWithContext(context)
        
    }

    override func willActivate() {
        super.willActivate()
        
        // heartRateの値を取得
        var def = NSUserDefaults(suiteName: "group.jp.techfund")
        let heartRate = def?.objectForKey("heartRate") as String
        var heartRateInt :Int! = heartRate.toInt()
        
        switch heartRateInt {
        case 1...59:
            label1.setText("?")    // 少ない
        case 60...90:
            label1.setText("?")    // 正常
        default:
            label1.setText("?")    // 多い
        }
        
        label2.setText(heartRate)
    }

    override func didDeactivate() {
        super.didDeactivate()
    }
}
```

<h3>参考</h3>
HealthKitへの書き込み/読み込みは下記を参考にさせていただきました。
<a href="https://sites.google.com/a/gclue.jp/swift-docs/ni-yinki100-ios/healthkit/001-ti-zhongdetanohealthstoreheno-dumi-shuki" target="_blank">001 体重データのHealthStoreへの読み書き</a>

表示部分は下記を参考にさせていただきました。
<a href="http://www.raywenderlich.com/87650/preparing-apple-watch-prototype-heartbeat-app" target="_blank">Preparing for the Apple Watch: a Prototype Heartbeat App</a>


<h3>Github</h3>
下記よりダウンロードできます。
https://github.com/TECHFUND/health