Healthkitから脈拍を取得するapple watchアプリの作り方です。
Swiftで実装しています。 => Githubはコチラ
現在の仕様として、脈拍等のhealth dataは直接取れません。
(Appleさん、早く直接値が取れるようになることを切に願っております)
脈拍値を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 等)
AppGroupsが設定できたら、TargetでApp GroupsとHealthkitを有効化します。
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を2つ配置、サイズ変更等
![スクリーンショット 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)
}
}
2.AppleWatch側で表示
UserDefaultから値を取得して、Switchで条件分けを行いハートを表示します。 ※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