【Swift/watchOS/HealthKit】Workout(ワークアウト)の実装方法!HKWorkoutSession

【Swift/watchOS/HealthKit】Workout(ワークアウト)の実装方法!HKWorkoutSession

この記事からわかること

  • Swift/watchOS/iOSHealthKitフレームワーク使い方
  • Workout(ワークアウト)とは?
  • HKWorkoutSession実装方法
  • 開始中断保存するには?

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

環境

Workout(ワークアウト)とは?

公式リファレンス:Workouts

Workout(ワークアウト)」とはウォーキングやランニング、ヨガ、水泳などの運動のアクティビティ情報を計測することができるAppleデバイスの機能です。主にApple Watchで活用することができ、専用の「ワークアウト」アプリを使用することでApple Watchを装着して運動するだけで、特定の運動の記録データ保存し、計測することが可能になります。

iPhoneからでも「ヘルスケア」アプリ>「ブラウザ」タブ>「アクティビティ」>「ワークアウト」>「データを追加する」から特定のワークアウトデータを追加することが可能ですが、これは完全に手動でデータを追加する仕組みになっているのでwatchのように自動計測してくれるわけではないようです。

【Swift/watchOS/HealthKit】Workout Sessionの実装方法!ワークアウトとは

アプリ開発とWorkout

アプリ開発におけるWorkoutはHealthKitフレームワーク内のHKWorkoutSessionという単位で管理されています。HealthKit自体の使い方に関してはデータアクセスへの許可申請やデータの取得方法には癖があるので前知識として以下の記事を参考にしてください。

Apple Watch(watchOS)アプリ自体の開発方法などは以下の記事を参考にしてください。

またAppleがデモアプリを公開してくれているのでこちらも参考にしてください。

WorkoutをwatchOSアプリから操作する

公式リファレンス:Running workout sessions

watchOSアプリからWorkoutを開始・終了・保存など操作する方法をまとめていきます。ただそのためにまず前準備として「info.plist」への値の追加や各項目へのアクセス許可申請などやっておくべきことがあるので先に準備しておきます。

前環境準備はこちらの記事を参考にしてください。ここではwatchOSターゲットの「info.plist」に追加するキーだけ紹介しておきます。

ワークアウトデータへのアクセス許可申請

許可申請するべき項目は以下の通りになります。許可申請の実装については「HealthKit#データへの許可要求」を参考にしてください。ここでは項目のみ記述しておきます。

/// 書き込み許可申請項目
public let writeAllTypes: Set = [
    // ワークアウト
    HKQuantityType.workoutType()
]

/// 読み取り許可申請項目
public let readAllTypes: Set = [
    // 消費エネルギー
    HKQuantityType(.activeEnergyBurned),
    // サイクリングの移動距離
    HKQuantityType(.distanceCycling),
    // ウォーキング・ランニングの移動距離
    HKQuantityType(.distanceWalkingRunning),
    // 車椅子ユーザーの移動距離
    HKQuantityType(.distanceWheelchair),
    // 心拍数
    HKQuantityType(.heartRate),
    // ワークアウト
    HKQuantityType.workoutType()
]

Background Modeの有効

ワークアウトはApple Watchがバックグラウンド状態でも動作する必要があるため「Background Mode」を有効にする必要があります。「Signing & Capabilities」から「Background Modes」を追加し「Workout processing」にチェックを入れておきます。

【Swift/watchOS/HealthKit】Workout Sessionの実装方法!ワークアウトとは

Workoutを管理する主要なクラス

HKWorkoutSession・・・Workoutの開始・終了・一時停止などを全体の操作を管理

HKLiveWorkoutBuilder・・・Workout中に収集されるデータを管理

Workoutを開始する

Watch開発アプリからWorkoutを実行するためには以下のステップで実装します。

  1. HKWorkoutConfiguration設定
  2. HKWorkoutSessionとHKLiveWorkoutBuilderの取得
  3. デリゲートの設定
  4. データソースの設定
  5. Workoutを起動

実装サンプルコードはGitHubで公開しているので参考にしてください。

1.HKWorkoutConfiguration設定

Workoutを開始するためにはHKWorkoutConfigurationで設定を行います。activityTypeアクティビティの種類(ウォーキングやランニングなど)locationType屋外/屋内を指定します。

let configuration = HKWorkoutConfiguration()
configuration.activityType = .running
configuration.locationType = .outdoor

2.HKWorkoutSessionとHKLiveWorkoutBuilderの取得

Workoutを実際に開始するにはまずHKWorkoutSessionをインスタンス化します。引数にHKHealthStoreHKWorkoutConfigurationを渡します。次にassociatedWorkoutBuilderメソッドを呼び出しHKLiveWorkoutBuilderを取得します。

do {
    session = try HKWorkoutSession(healthStore: healthStore, configuration: configuration)
    builder = session.associatedWorkoutBuilder()
} catch {
    // Handle failure here.
    return
}

3.デリゲートの設定

HKWorkoutSessionDelegateHKLiveWorkoutBuilderDelegateからそれぞれイベントを検知することができます。必要であればdelegateの設定をしておきます。

session.delegate = self
builder.delegate = self

4.データソースの設定

リアルタイムのWorkoutデータを取得できるようにHKLiveWorkoutDataSourceを設定します。

builder.dataSource = HKLiveWorkoutDataSource(healthStore: healthStore, workoutConfiguration: configuration)

5.Workoutを起動

準備が整ったのでセッションとビルダーの両方を起動させます。HKWorkoutSessionstartActivityHKLiveWorkoutBuilderbeginCollectionを使用します。Workoutが実行中の際はUIに実行中であることが識別できるような実装をする必要があるようです。

let startDate = Date()
session?.startActivity(with: startDate)
builder?.beginCollection(withStart: startDate) { [weak self] (success, error) in
    guard let self else { return }
    
    guard success else { 
      print("ワークアウト開始失敗:", error)
      return
    }
    print("ワークアウト開始")
}

Workoutを操作する

起動しているWorkoutは中断・再開・終了など操作することが可能です。

中断

session?.pause()

再開

session?.resume()

終了

Workoutを終了する際はセッションとビルダーの両方を終了させる必要があります。HKWorkoutSessionendHKLiveWorkoutBuilderendCollectionを実行し、その中でさらにfinishWorkoutを実行します。

session?.end()
builder?.endCollection(withEnd: Date()) { [weak self] (success, error) in
    guard let self else { return }
    
    guard success else {
        print("ワークアウト終了失敗:", error)
        return
    }
    
    self.builder?.finishWorkout { [weak self]  (workout, error) in
        guard let self else { return }
        
        guard workout != nil else {
            print("ワークアウト終了失敗:", error)
            return
        }

        print("ワークアウト終了")
    }
}

HKWorkoutSessionDelegate

公式リファレンス:HKWorkoutSessionDelegate

HKWorkoutSessionDelegateセッションに関する変化やエラーを検知するデリゲートです。workoutSession(_:didChangeTo:from:date:)workoutSession(_:didFailWithError:)の実装が必須になっています。

extension WorkoutManager: HKWorkoutSessionDelegate {
    /// セッションの状態が変化した際に呼ばれる
    func workoutSession(_ workoutSession: HKWorkoutSession, didChangeTo toState: HKWorkoutSessionState, from fromState: HKWorkoutSessionState, date: Date) {
        self.log.append("セッション状態変化:\(toState)\n")
        self.log.append("セッション状態変化:\(fromState)\n")
    }
    /// セッションがエラーで失敗した際に呼ばれる
    func workoutSession(_ workoutSession: HKWorkoutSession, didFailWithError error: any Error) {
        self.log.append("セッションエラー:\(error)\n")
    }
}

HKWorkoutSessionState

状態の変化はHKWorkoutSessionState型で取得できます。

public enum HKWorkoutSessionState : Int, @unchecked  Sendable {
    // 開始されていない
    case notStarted = 1
    // セッション実行中
    case running = 2
    // 終了
    case ended = 3
    // 一時停止
    case paused = 4
    // 準備中
    case prepared = 5
    // 停止
    case stopped = 6
}

HKLiveWorkoutBuilderDelegate

公式リファレンス:HKLiveWorkoutBuilderDelegate

HKLiveWorkoutBuilderDelegateヘルスケアデータやイベントが追加されたことを検知するデリゲートです。workoutBuilder(_:didCollectDataOf:)workoutBuilderDidCollectEvent(_:)の実装が必須になっています。

extension WorkoutManager: HKLiveWorkoutBuilderDelegate {
    /// ヘルスケアデータが追加された際に呼ばれる
    func workoutBuilder(_ workoutBuilder: HKLiveWorkoutBuilder, didCollectDataOf collectedTypes: Set<HKSampleType>) {
      
    }
    /// イベントが追加された際に呼ばれる
    func workoutBuilderDidCollectEvent(_ workoutBuilder: HKLiveWorkoutBuilder) {
    }
}

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index