swiftでヘルスケアのデータから心拍数の平均値を取得する
解決済
回答 1
投稿
- 評価
- クリップ 1
- VIEW 2,059
前提・実現したいこと
xcode8.1 swift3
iPhoneからヘルスケアにアクセスし、心拍数の平均値を取得するアプリを考えております。
取得するときにHealthKitを用いております。
その最中に値が読み取れていないエラーが発生しました。
発生している問題・エラーメッセージ
my heartrate is nil
(「HealthStoreから心拍数データを取得します」の「日付処理」、「データ抽出のクエリ」のところです)
該当のソースコード
import UIKit
import HealthKit
class ViewController: UIViewController {
let button = UIButton()
let readDataButton = UIButton()
var testarray: [String] = []
let dataLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
view.backgroundColor = UIColor.lightGray
// データ追加ボタン
button.setTitle("データ追加", for: .normal)
button.addTarget(self, action: #selector(ViewController.didTapAddData), for: .touchUpInside)
button.sizeToFit()
button.center = view.center
view.addSubview(button)
// データ取得ボタン
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)
// 取得したデータの表示
dataLabel.text = ""
dataLabel.textAlignment = .center
dataLabel.frame = CGRect(x: 0, y: 0, width: 150, height: 30)
dataLabel.center = CGPoint(x: view.center.x, y: view.center.y + 100)
view.addSubview(dataLabel)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func didTapAddData(){
saveHealthData()
}
/// 心拍数データを保存します
private func saveHealthData(){
/* 保存するデータの準備 */
// 心拍数
let heatRate = "60"
// 心拍数の単位
let unit2 = HKUnit(from: "count/min")
// 心拍数情報のタイプ
let type2 = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)!
// healthKitデータ保存領域のインスタンス化
let healthStore = HKHealthStore()
let helthData2 = HKQuantity(unit: unit2, doubleValue: Double(heatRate)!)
// 時間設定
let now: NSDate = NSDate()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy/MM/dd HH:mm:ss"
let dateStr = formatter.string(from: now as Date)
print("current time : \(dateStr)")
let calendar = Calendar(identifier: .gregorian)
let inAfterMintes = calendar.date(byAdding: .minute, value: 30, to: now as Date)!
let dateStrAfterMinutes = formatter.string(from: inAfterMintes)
print("30 minutes after time : \(dateStrAfterMinutes)")
let sample2 = HKQuantitySample(type: type2, quantity: helthData2, start: now as Date, end: inAfterMintes)
let authorizedStatus2 = healthStore.authorizationStatus(for: type2)
// HealthKitデータ保存領域へのアクセス権限があればデータ保存。なければ、アクセス権限の確認画面へ移動
if authorizedStatus2 == .sharingAuthorized{
healthStore.save(sample2, withCompletion: { (success, error) in
if error != nil {
// NSLog(error!.description);
return
}
if success {
healthStore.save(sample2, withCompletion:{ (success, error) in
print("保存成功")
})
}
})
}else{
healthStore.requestAuthorization(toShare: [type2], read: [type2], completion: { (success, error) in
if error != nil {
// NSLog(error!.description);
return
}
// アクセス権限を得たらデータを保存
if success {
healthStore.save(sample2, withCompletion:{ (success, error) in
print("保存成功")
})
}
})
}
}
func didTapReadData(){
readHealthData()
}
/// HealthStoreから心拍数データを取得します。
private func readHealthData(){
// データの種類
let type2 = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)!
// データ取得
let healthStore = HKHealthStore()
// 日付処理
let now: NSDate = NSDate()
let calendar = Calendar(identifier: .gregorian)
let inAfterMintes = calendar.date(byAdding: .minute, value: 30, to: now as Date)!
let predicate: NSPredicate = HKQuery.predicateForSamples(withStart: now as Date, end: inAfterMintes, options: HKQueryOptions.strictStartDate)
let startOptions: HKStatisticsOptions = HKStatisticsOptions.discreteAverage
// データ抽出クエリ
let query: HKStatisticsQuery = HKStatisticsQuery(quantityType: type2, quantitySamplePredicate: predicate, options: startOptions, completionHandler: {
(query, result, error) in DispatchQueue.global().async {
print("my heartrate is \(result!.averageQuantity())")
}
})
healthStore.execute(query)
let query2 = HKSampleQuery(sampleType: type2, predicate: nil, limit: 0, sortDescriptors: nil) { (query, results, error) in
// データ抽出処理完了後の処理
if error != nil{
print("エラー")
}
if let samples2 = results as? [HKQuantitySample]{
DispatchQueue.main.async{
self.dataLabel.text = samples2.first?.quantity != nil ? "\(samples2.first!.quantity)" : ""
}
}
}
// HealthStoreへのアクセス権限確認
let authorizedStatus2 = healthStore.authorizationStatus(for: type2)
// 権限があれば、実行。権限がなければ、権限確認画面へ。
if authorizedStatus2 == .sharingAuthorized{
healthStore.execute(query2)
}else{
healthStore.requestAuthorization(toShare: [type2], read: [type2]) {
success, error in
if error != nil {
// print(error!.description);
return
}
if success {
// 引数に指定されたクエリーを実行します
healthStore.execute(query2)
}
}
}
}
}
試したこと
読み込む時間を保存したものと同じにしましたが、nilが表示されました。
補足情報(言語/FW/ツール等のバージョンなど)
より詳細な情報
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
0
ヘルスケアへのアクセスが禁止になっているのではないでしょうか?設定で確認してみて下さい。
あと、readHealthData()の処理の流れもおかしいですね。
初めて実行したとき、アクセス許可の確認前にquery
を実行しています。
healthStore.execute(query)
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.35%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2016/12/20 02:55
あれから試行錯誤した結果、上手くいきました
原因は
//時間設定 と //日付処理 で
let now: NSDate = NSDate()
を再定義しており、このせいで保存と読み込みとの間にわずかな時間差が生じ、
出力ができなかったと思われます。
解決策として
//時間設定 の
nowとinAfterMinutesの値をを新しい変数に代入させ、
その新しい変数を //日付処理の部分で実装しました。