【HealthKit】Workout関連
公式ドキュメントを参考に
HKWorkoutSession
は、運動時のモニタリングをしたいときに使用できるセッションである。
HKWorkoutSession
を使用して、Apple Watchでユーザーのアクティビティをトラッキングします。セッションが実行されている間、システムは指定されたアクティビティ用にウォッチのセンサーを調整する。
Apple Watchでワークアウトをトラッキングするには、ワークアウトセッションを設定、開始、保存する必要があります。
Workoutのデータ取得・書き込みに必要なデータ型を指定して、権限をリクエストする必要がある。
// The quantity type to write to the health store.
let typesToShare: Set = [
HKQuantityType.workoutType()
]
// The quantity types to read from the health store.
let typesToRead: Set = [
HKQuantityType.quantityType(forIdentifier: .heartRate)!,
HKQuantityType.quantityType(forIdentifier: .activeEnergyBurned)!,
HKQuantityType.quantityType(forIdentifier: .distanceWalkingRunning)!
]
// Request authorization for those quantity types.
healthStore.requestAuthorization(toShare: typesToShare, read: typesToRead) { (success, error) in
// Handle errors here.
}
WorkoutSessionがアクティブなアプリはバックグラウンドで実行できるため、WatchKit App Extensionにバックグラウンドモード機能を追加する必要がある。
WorkoutSessionには、ワークアウト処理のバックグラウンドモードを設定する必要がある。
音楽をバックグラウンドで再生する場合もチェックする必要がある。
HKWorkoutConfiguration()
を生成して、運動の種類、屋内or屋外かを設定する。
let configuration = HKWorkoutConfiguration()
configuration.activityType = .running
configuration.locationType = .outdoor
セッションが実行されている間、Apple Watchは活動のエネルギー消費サンプルを自動的にHealthKitストアに保存します。HealthKitは、ランニング、ウォーキング、サイクリング、階段登りの活動に対して最適化されたカロリー計算を提供します。さらに、ランニング、ウォーキング、サイクリングの活動に関しては、屋内と屋外の場所によって計算が異なります。
HKWorkoutConfiguration()
を使用して、設定を使用してワークアウト・セッションを作成し、セッションからHKLiveWorkoutBuilder
を取得します。
do {
session = try HKWorkoutSession(healthStore: healthStore, configuration: configuration)
builder = session.associatedWorkoutBuilder()
} catch {
// Handle failure here.
return
}
HKWorkoutSession
は例外処理を投げる可能性があるため、do catch
が必要。
HKLiveWorkoutDataSource
オブジェクトを作成し、workout builderに割り当てます。
builder.dataSource = HKLiveWorkoutDataSource(healthStore: healthStore,
workoutConfiguration: configuration)
ワークアウトセッションとライブデータソースに同じ設定オブジェクトを使用します。セッションが実行されている間、Apple Watchは自動的にワークアウトに関するデータを収集し、サンプルをHealthKitに保存します。例えば、屋外ランニングセッションは、activeEnergyBurned、basalEnergyBurned、heartRate、distanceWalkingRunningのサンプルを収集し、保存します。
sessionとbuilderのデリゲートを設定することが可能です。
session.delegate = self
builder.delegate = self
HKWorkoutSessionDelegate
は、セッションの状態が変化したとき、イベントが発生したとき、またはセッションがエラーで失敗したときに更新を受け取ります。
HKLiveWorkoutBuilderDelegate
は、Apple Watchまたはアプリがビルダーに新しいサンプルやイベントを追加したときに更新を受け取ります。
セッションとビルダーの違い
両者の違いがイメージわかなかったので、まとめてみました。
セッション(Session):
- ワークアウト全体の期間と状態を管理。
- ワークアウトの開始、停止、一時停止、再開を制御。
- イベントやエラーの通知を担当。
ビルダー(Builder):
- セッション中に収集されるデータの記録と管理。
- リアルタイムでのデータサンプルとイベントの収集。
デフォルトでは、ワークアウトセッションは自動的にすべてのイベントをビルダーに転送するので、両方のデリゲートが同じイベントセットを受け取るはずです。ただし、ビルダーに設定されるイベントを制御したい場合は、ビルダーのshouldCollectWorkoutEventsプロパティをfalseに設定できます。
「ビルダーに設定されるイベントを制御したい場合は」はどのような状況なのだろうか。
セッションとビルダーを開始します。
session.startActivity(with: Date())
builder.beginCollection(withStart: Date()) { (success, error) in
guard success else {
// Handle errors.
}
// Indicate that the session has started.
}
セッションが実行されている間、Apple Watchはワークアウトの設定に基づいてサンプルとイベントを自動的に収集し、追加します。
ビルダーが新しいサンプルまたはイベントを受信するたびに、アプリのユーザー インターフェイスを更新する必要があります。新しいサンプルを使用するには、HKLiveWorkoutBuilderDelegate
のworkoutBuilder(_:didCollectDataOf:)
メソッドを実装します。
func workoutBuilder(_ workoutBuilder: HKLiveWorkoutBuilder, didCollectDataOf collectedTypes: Set<HKSampleType>) {
for type in collectedTypes {
guard let quantityType = type as? HKQuantityType else {
return // Nothing to do.
}
// Calculate statistics for the type.
let statistics = workoutBuilder.statistics(for: quantityType)
let label = labelForQuantityType(quantityType)
DispatchQueue.main.async() {
// Update the user interface.
}
}
}
新しいイベントに応答するには、HKLiveWorkoutBuilderDelegate
のworkoutBuilderDidCollectEvent(_:)
メソッドを実装します。
func workoutBuilderDidCollectEvent(_ workoutBuilder: HKLiveWorkoutBuilder) {
let lastEvent = workoutBuilder.workoutEvents.last
DispatchQueue.main.async() {
// Update the user interface here.
}
}
HKWorkoutEventType.lap
イベントが発生したら、表示されるラップ数を増やすことができます。
バックグラウンドでの実行
- ワークアウトセッションはバックグラウンドで実行され続ける。
- バックグラウンドで音声や触覚フィードバックを提供できる。
- 過剰なCPU使用の場合、watchOSはアプリを一時停止することがある。
- XcodeやInstrumentsを使ってCPU使用量をテストする必要がある。
companion app の連携が可能です。
- iOSでセッションを開始して、watchOSにそれを反映できます。
- ただ両者でのセッションの管理は厳重に行う必要があります。
ユーザがワークアウトを終了したら、セッションを終了し、ビルダの endCollection(withEnd:completion:)
メソッドと finishWorkout(completion:)
メソッドを呼び出します。
session.end()
builder.endCollection(withEnd: Date()) { (success, error) in
guard success else {
// Handle errors.
}
builder.finishWorkout { (workout, error) in
guard workout != nil else {
// Handle errors.
}
DispatchQueue.main.async() {
// Update the user interface.
}
}
}
クラッシュからの回復
ワークアウトセッション中にアプリがクラッシュした場合、システムはアプリの再起動時にhandleActiveWorkoutRecovery()
メソッドを呼び出します。このメソッドの実装では、HealthKitストアのrecoverActiveWorkoutSession(completion:)
メソッドを呼び出します。HealthKitは以前のワークアウトセッションの復元を試み、新しいセッションオブジェクトかエラーをcompletionブロックに返します。