質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Swift 2

Swift 2は、Apple社が独自に開発を行っている言語「Swift」のアップグレード版です。iOSやOS X、さらにLinuxにも対応可能です。また、throws-catchベースのエラーハンドリングが追加されています。

Q&A

解決済

1回答

4193閲覧

ヘルスケアの心拍数の値を取得したい(追記: queryの初期化の設定方法)

nyarome

総合スコア18

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Swift 2

Swift 2は、Apple社が独自に開発を行っている言語「Swift」のアップグレード版です。iOSやOS X、さらにLinuxにも対応可能です。また、throws-catchベースのエラーハンドリングが追加されています。

0グッド

1クリップ

投稿2016/12/22 02:09

編集2016/12/22 02:37

###前提・実現したいこと
xcode8.2.1
swift3
私は現在iPhoneを使ってヘルスケアに格納されている心拍数の値を取得するアプリを考えております。

そのアルゴリズムは
1.アップルウォッチで心拍数の計測ヘルスケアに格納

2.ヘルスキットを用いて、iPnoneでヘルスケアにアクセスし心拍数の値を取得

というものになっております。

    1. という方法で実装することで利用者はデベロッパー登録をしないでiPhoneに心拍数の値を取得することができます。

コードを完全に書き終わりいざ実機でビルドを行なってみると、デバックコンソール内でエラーが発生してしましました。
このエラーを解決したいです。二つあるのですが、どちらか片方でも本当に助かります。
長くなっておりますが何卒よろしくお願いいたします。

[追記]
Start observe の方のエラーの件で
queryの初期化が必要だということがわかりました。
queryの初期化の設定をご存知でしたらご教授お願いいたいます。

###発生している問題・エラーメッセージ

デバックコンソール内 ===='Start observe'ボタンを押した時==== socket connected! 2016-12-22 09:58:53.865464 healthKitTest1[1028:252927] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'This query has already been executed and cannot be executed again.' *** First throw call stack: (0x1bdc0df7 0x1b023077 0x1bdc0d3d 0x29b7bb39 0x29b569a3 0xafd54 0xadd0c 0xbf0d57 0xbff011 0xbfeba7 0x1b61b937 0x1b61b490) libc++abi.dylib: terminating with uncaught exception of type NSException ===='Start'ボタンを押し、そのあと'データ読み込み'ボタンを押した時==== my heartrate is nil (さらに self.getHeartRateAverage = (result!.averageQuantity()?.description)! のところでブレイクポイントが自動に入る)

###該当のソースコード

import UIKit import HealthKit class ViewController: UIViewController { let readDataButton = UIButton() let setTimerButton = UIButton() var setTimeFlag = 0 var startTime = Date() var endTime = Date() let formatter = DateFormatter() var getHeartRateAverage = String() var getHeartRateMax = String() var getHeartRateMin = String() let observeButton = UIButton() var observeSwitch: Int = 0 var getLatestHeartRate = String() override func viewDidLoad() { super.viewDidLoad() // データ取得ボタン readDataButton.setTitle("データ取得", for: .normal) readDataButton.addTarget(self, action: #selector(ViewController.didTapReadData), for: .touchUpInside) readDataButton.sizeToFit() readDataButton.center = CGPoint(x: view.center.x, y: view.center.y + 50) view.addSubview(readDataButton) // タイマーセット setTimerButton.setTitle("Start", for: .normal) setTimerButton.addTarget(self, action: #selector(ViewController.setTime), for: .touchUpInside) setTimerButton.sizeToFit() setTimerButton.center = CGPoint(x: view.center.x, y: view.center.y - 50) view.addSubview(setTimerButton) // 時間の表示の型 formatter.dateFormat = "yyyy/MM/dd HH:mm:ss" // HKHKObserverQuery起動、停止ボタンの作成 observeButton.setTitle("Start observe", for: .normal) observeButton.addTarget(self, action: #selector(ViewController.didTapObserveData), for: .touchUpInside) observeButton.sizeToFit() observeButton.center = CGPoint(x: view.center.x, y: view.center.y - 100) view.addSubview(observeButton) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } // 開始時間と終了時間の設定 func setTime(){ if setTimeFlag == 0 { setTimeFlag = 1 setTimerButton.setTitle("Stop", for: .normal) startTime = NSDate() as Date let startStr = formatter.string(from: startTime) print("startTime : \(startStr)") } else { setTimeFlag = 0 setTimerButton.setTitle("Start", for: .normal) endTime = NSDate() as Date let endStr = formatter.string(from: endTime) print("endTime : \(endStr)") } } // HealthStoreから心拍数データを取得します。 func didTapReadData(){ readHealthData() } private func readHealthData(){ // データの種類 let type2 = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)! // データ取得の準備 let healthStore = HKHealthStore() // 取得するデータの条件 let predicate: NSPredicate = HKQuery.predicateForSamples(withStart: startTime, end: endTime, options: HKQueryOptions.strictStartDate) // 取得するための条件の設定 let startOptions: HKStatisticsOptions = ([HKStatisticsOptions.discreteAverage, HKStatisticsOptions.discreteMax, HKStatisticsOptions.discreteMin]) // データ抽出クエリ let query: HKStatisticsQuery = HKStatisticsQuery(quantityType: type2, quantitySamplePredicate: predicate, options: startOptions, completionHandler: { (query, result, error) in DispatchQueue.global().async { print("my heartrate is \(result!.averageQuantity())") self.getHeartRateAverage = (result!.averageQuantity()?.description)! self.getHeartRateMax = (result!.maximumQuantity()?.description)! self.getHeartRateMin = (result!.minimumQuantity()?.description)! } }) print("\(getHeartRateAverage)") // HealthStoreへのアクセス権限確認 let authorizedStatus2 = healthStore.authorizationStatus(for: type2) // 権限があれば、実行。権限がなければ、権限確認画面へ。 if authorizedStatus2 == .sharingAuthorized{ healthStore.execute(query) }else{ healthStore.requestAuthorization(toShare: [type2], read: [type2]) { success, error in if error != nil { return } if success { // 引数に指定されたクエリーを実行します healthStore.execute(query) } } } func didTapObserveData() { // データの種類 let type2 = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)! // データ取得の準備 let healthStore = HKHealthStore() // HealthStoreへのアクセス権限確認 // 権限があれば、実行。権限がなければ、権限確認画面へ。 healthStore.requestAuthorization(toShare: nil, read: [type2], completion: { (Bool, Error) -> Void in }) // 最新の心拍数を取得するクエリの作成 // 検索条件 let predicate: NSPredicate? = nil // 取得件数 let limit = 0 // 並び替え条件 let sortDescriptors: [NSSortDescriptor]? = nil // クエリ作成 let sampleQuery = HKSampleQuery(sampleType: type2, predicate: predicate, limit: limit, sortDescriptors: sortDescriptors) { (query, results, error) in // 実行後 if error == nil { DispatchQueue.global().async { // 最新の心拍数を格納 self.getLatestHeartRate = results!.description } } } // 心拍数が更新されるたびに呼び出されるクエリの作成 let observeQuery = HKObserverQuery(sampleType: type2, predicate: nil, updateHandler: { (query, complitionHandler, error) in // 実行後 if error == nil { DispatchQueue.global().async { healthStore.execute(sampleQuery) healthStore.stop(sampleQuery) } } }) if observeSwitch == 0 { observeSwitch = 1 healthStore.execute(observeQuery) observeButton.setTitle("Stop observe", for: .normal) } else { observeSwitch = 0 healthStore.stop(observeQuery) observeButton.setTitle("Start observe", for: .normal) } } }

###補足情報(言語/FW/ツール等のバージョンなど)
アプローチの仕方が2通りあります。
1つ目は指定した時間内の心拍数の最大最小平均値を取り出す処理を実装したくて
まず'Start'ボタンを2回(2回目は名前が'Stop'に変わる)押して測定開始時間、測定終了時間を保存
次にデータ読み込みボタンを押してHKStatisticsQueryを用いて最大最小平均値を取得する
2つ目はリアルタイムで心拍数を取り出す処理をしたくて
最初'Start observe'ボタンを押して、HKObserverQueryを用いてHealthStoreの値の変更を検知し、
その時sampleQueryを用いて新しい心拍数の値を取得する
というものがあります。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

自己解決

試行錯誤してこのようにすれば上手くいきました。
どうしてこうなったのかはよくわかりません。申し訳ありません。
"並び替え条件"の後の
"心拍数が更新されるたびに呼び出されるクエリの作成"の中に
"クエリ作成"の部分を挿入することで上手くいきました。

// 並び替え条件 let sortDescriptors: [NSSortDescriptor]? = nil // 心拍数が更新されるたびに呼び出されるクエリの作成 let observeQuery = HKObserverQuery(sampleType: type2, predicate: nil, updateHandler: { ( query, completionHandler, error) in // 実行後 if error == nil { // クエリ作成 let sampleQuery = HKSampleQuery(sampleType: type2, predicate: predicate, limit: limit, sortDescriptors: sortDescriptors) { (query, results, error) in // 実行後 if error == nil { DispatchQueue.global().async { // 最新の心拍数を格納 self.getLatestHeartRate = results!.description } } } DispatchQueue.global().async { healthStore.execute(sampleQuery) } } completionHandler() })

投稿2017/03/10 04:15

nyarome

総合スコア18

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問