LoginSignup
91
90

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-01-19

Healthkitから脈拍を取得するapple watchアプリの作り方です。
Swiftで実装しています。 => Githubはコチラ

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

そのため
スクリーンショット 2015-01-17 20.36.23.png

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

0.準備

・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

AppGroupsが設定できたら、TargetでApp GroupsとHealthkitを有効化します。
health Target > Capabilities > App Groups と HealthKit を ON 
App Groups:のgroup.jp.techfundにチェックを入れる
※health WatchKit Extensionも同様に有効化すること

Healthxs.004.jpg

・HealthKit Framework読み込み
プロジェクト > General > Linked Frameworks and Librariesに「+」 > HealthKitを検索 > Add
スクリーンショット 2015-01-19 14.13.27.png

・Interface.StoryboardにLabelを2つ配置、サイズ変更等
スクリーンショット 2015-01-19 17.34.38.png

1.iPhone側でHealthkitの値を取得

デモ用に書き込みボタンを用意し、読み込み時にはUser Defaultへ平均値を書き込む処理を入れています。

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)
    }
}

2.AppleWatch側で表示

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

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()
    }
}

参考

HealthKitへの書き込み/読み込みは下記を参考にさせていただきました。
001 体重データのHealthStoreへの読み書き

表示部分は下記を参考にさせていただきました。
Preparing for the Apple Watch: a Prototype Heartbeat App

Github

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

91
90
1

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
91
90