HealthKitアプリSwiftコードの紹介(4) – HealthKitデータの管理

HealthKit

HealthKitデータの管理と同期を行うためのクラス

このコードは、HealthKitデータの管理と同期を行うためのクラス HealthKitStore とそれに関連するプロトコル HealthKitStoreProtocol を定義しています。このクラスは、HealthKitフレームワークを使用してユーザーのヘルスデータを読み取り、アップロードするための機能を提供します。

import Foundation
import HealthKit
import UIKit
import Combine

protocol HealthKitStoreProtocol {
    func authorizeHealthKit(completion: @escaping () -> Void)
    func startObservingDataChanges(queue: DispatchQueue?, isBackgroundTask: Bool, completion: @escaping () -> Void)
    func updateCharacteristicIdentifiers()
    func getMostRecentHealthKitSample<T>(for sampleType: HKSampleType,
                                    completion: @escaping ([T], Date, Error?) -> Void) where T: HKSample
}

class HealthKitStore: HealthKitStoreProtocol {
    static let firstStartDateInterval: Double = -90*24*3600
    static let typeEcgVoltage = "HKQuantityTypeIdentifierECGVoltage"
    static let typeElectrocardiogramType = HKSampleType.electrocardiogramType().identifier
    
    private var userDefault: UserDefaultUtilsProtocol
    private var localDatabase: LocalDatabaseProtocol
    private var dataSyncService: DataSyncServiceProtocol

    let healthKitStore:HKHealthStore = HKHealthStore()
    private var blockIdentifier: [String: Bool] = [:]
    private var isHealkitLoadingData: Bool = false
    private var group = DispatchGroup()
    private var uploadDatas: [UploadFileDataModel] = []
    private var sendingIdentifier: [String: Date] = [:]
    private var activeQueries: [HKQuery] = []
    private var queue: DispatchQueue = DispatchQueue(label: "bgTask")
    private var stores = Set<AnyCancellable>()
    static let healthKitCharacteristicType = [
        HKObjectType.characteristicType(forIdentifier: HKCharacteristicTypeIdentifier.biologicalSex)!,
        HKObjectType.characteristicType(forIdentifier: HKCharacteristicTypeIdentifier.wheelchairUse)!,
    ]
    
    public static var healthKitActivityType: [HKSampleType] {
        var list = [
        //省略
        ]
        if #available(iOS 16, *) {
            list.append(HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.atrialFibrillationBurden)!)
            list.append(HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRateRecoveryOneMinute)!)
        }
        return list
    }
    
    static let healthKitEventType = [
        HKCategoryType(.appleWalkingSteadinessEvent),
        HKCategoryType(.lowHeartRateEvent),
        HKCategoryType(.highHeartRateEvent),
        HKCategoryType(.irregularHeartRhythmEvent)
    ]
    
    static let healthKitCategoryType = [
        HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis)!
    ]
    
    public static var mapHealthKitActivityUnit: [HKSampleType: HKUnit] {
        var list = [
     //省略
        ]
        if #available(iOS 16, *) {
            list[HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.atrialFibrillationBurden)!] = HKUnit.percent()
            list[HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRateRecoveryOneMinute)!] = HKUnit(from: "count/min")
        }
        return list
    }
    
    init(userDefault: UserDefaultUtilsProtocol,
         localDatabase: LocalDatabaseProtocol,
         dataSyncService: DataSyncServiceProtocol) {
        self.userDefault = userDefault
        self.localDatabase = localDatabase
        self.dataSyncService = dataSyncService
    }
    
    func authorizeHealthKit(completion: @escaping () -> Void) {
    //省略
    }
    
    func updateCharacteristicIdentifiers() {
    //省略
    }
    
    public func startObservingDataChanges(queue: DispatchQueue?, isBackgroundTask: Bool, completion: @escaping () -> Void) {
    //省略        
    }
    
    public func getMostRecentHealthKitSample<T>(for sampleType: HKSampleType,
                                                completion: @escaping ([T], Date, Error?) -> Void) where T: HKSample {
    //省略
    }
    
    private func queryECGSample(ecgSample: HKElectrocardiogram, ecgId: String) {
    //省略
    }
    
    private func updateEndTime(identifier: String, endDate: Date) {
    //省略
    }
    
    private func storeDataToDatabase<T>(model: T) where T: HealthKitModelProtocol {
    //省略
    }
    
    private func storeDataToDatabase<T>(type: String, models: [T]) where T: BaseUploadDataModel {
    //省略
    }
}

主な機能としては以下の通りです:

  1. HealthKitデータの読み取りと同期化: authorizeHealthKit メソッドにより、ユーザーにHealthKitデータへのアクセス許可を求め、startObservingDataChanges メソッドにより、ユーザーのデータ変更を監視し、変更があった場合にそれを同期化します。
  2. 特性識別子の更新: updateCharacteristicIdentifiers メソッドは、ユーザーの特性情報(例: 生物学的性別、車椅子の利用状況など)を更新し、アップロードデータに追加します。
  3. HealthKitデータのクエリと処理: getMostRecentHealthKitSample メソッドは、指定されたサンプルタイプに関連する最新のHealthKitデータを取得し、そのデータを処理します。
  4. データのローカルおよびリモートへの保存: storeDataToDatabase メソッドは、データをローカルデータベースに保存し、DataSyncServiceProtocol を介してデータをリモートサーバーにアップロードします。
  5. バックグラウンドでのタスク管理: バックグラウンドでのタスク実行に関連する処理も含まれており、DispatchQueue を使用して非同期処理を制御します。

healthKitActivityTypeで活動タイプの定義

    public static var healthKitActivityType: [HKSampleType] {
        var list = [
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)!,
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.distanceWalkingRunning)!,
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.pushCount)!,
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.distanceWheelchair)!,
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.activeEnergyBurned)!,
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.basalEnergyBurned)!,
            //mobility
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.appleWalkingSteadiness)!,
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.walkingSpeed)!,
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.walkingStepLength)!,
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.walkingAsymmetryPercentage)!,

            //heart rate
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)!,
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.restingHeartRate)!,
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRateVariabilitySDNN)!,
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.walkingHeartRateAverage)!,

            //blood pressure
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.oxygenSaturation)!,
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bloodPressureSystolic)!,
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bloodPressureDiastolic)!,
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.respiratoryRate)!,

            HKObjectType.electrocardiogramType()
        ]
        if #available(iOS 16, *) {
            list.append(HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.atrialFibrillationBurden)!)
            list.append(HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRateRecoveryOneMinute)!)
        }
        return list
    }

healthKitActivityTypeは、HealthKitフレームワークで使用されるさまざまな活動タイプを表すHKSampleTypeの配列です。この配列に含まれる各HKSampleTypeは、特定の健康活動やフィットネスデータを表します。

以下は、healthKitActivityTypeで定義される主な活動タイプのいくつかです:

  • ステップ数 (HKQuantityTypeIdentifier.stepCount)
  • 歩行およびランニングの距離 (HKQuantityTypeIdentifier.distanceWalkingRunning)
  • プッシュ回数 (HKQuantityTypeIdentifier.pushCount)
  • 車椅子での移動距離 (HKQuantityTypeIdentifier.distanceWheelchair)
  • 消費したアクティブエネルギー量 (HKQuantityTypeIdentifier.activeEnergyBurned)
  • 休息時の心拍数 (HKQuantityTypeIdentifier.restingHeartRate)
  • その他多くの健康活動やフィットネスに関連するデータ

これらの活動タイプは、ユーザーがiPhoneやApple Watchなどのデバイスを使用して行ったさまざまな活動や運動に関するデータを収集し、HealthKitフレームワークを介してアプリケーションに提供します。

HKUnitとは?

HKUnitは、HealthKitフレームワークで使用される単位を表すクラスです。HealthKitは、ヘルスデータを取得、保存、管理するためのフレームワークであり、さまざまなヘルスデータ(身長、体重、心拍数など)の単位を表すためにHKUnitが使用されます。

例えば、心拍数の単位は「count/min」(1分あたりのカウント数)であり、これはHKUnit(from: "count/min")として表現されます。また、体重の単位は「kg」(キログラム)であり、これはHKUnit.gramUnit(with: .kilo)として表現されます。

HKUnitクラスは、さまざまな単位のインスタンスを作成するための便利なメソッドも提供しています。これにより、ヘルスデータを取り扱うアプリケーションで正確な単位を指定することができます。

mapHealthKitActivityUnitの定義

    public static var mapHealthKitActivityUnit: [HKSampleType: HKUnit] {
        var list = [
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)! : HKUnit.count(),
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.distanceWalkingRunning)! : HKUnit.meter(),
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.pushCount)! : HKUnit.count(),
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.distanceWheelchair)!: HKUnit.meter(),
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.appleWalkingSteadiness)!: HKUnit.percent(),
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.walkingSpeed)!: HKUnit(from: "m/s"),
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.walkingStepLength)!: HKUnit.meter(),
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.walkingAsymmetryPercentage)!: HKUnit.percent(),
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)!: HKUnit(from: "count/min"),
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.restingHeartRate)!: HKUnit(from: "count/min"),
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRateVariabilitySDNN)!: HKUnit.minute(),
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.walkingHeartRateAverage)!: HKUnit(from: "count/min"),
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.oxygenSaturation)!: HKUnit.percent(),
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bloodPressureSystolic)!: HKUnit.millimeterOfMercury(),
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bloodPressureDiastolic)!: HKUnit.millimeterOfMercury(),
            HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.respiratoryRate)!: HKUnit(from: "count/min")
        ]
        if #available(iOS 16, *) {
            list[HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.atrialFibrillationBurden)!] = HKUnit.percent()
            list[HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRateRecoveryOneMinute)!] = HKUnit(from: "count/min")
        }
        return list
    }

このコードは、HKSampleTypeごとに対応するHKUnitをマッピングする静的なメソッド mapHealthKitActivityUnit を定義しています。このメソッドは、特定の健康データのタイプ(例:歩数、心拍数、血圧など)に対して、適切なHKUnitを関連付けるための辞書を生成します。

具体的には、次のような対応関係が定義されています:

  • HKQuantityTypeIdentifier.stepCountには、HKUnit.count()(個数)が関連付けられています。
  • HKQuantityTypeIdentifier.distanceWalkingRunningには、HKUnit.meter()(メートル)が関連付けられています。
  • HKQuantityTypeIdentifier.heartRateには、HKUnit(from: "count/min")(1分あたりのカウント数)が関連付けられています。

このように、各健康データのタイプに対して適切な単位が定義され、健康データを正しく取り扱うための基盤が整えられています。また、#available(iOS 16, *)を使用して、iOS 16以降で利用可能な特定の健康データの追加に対応しています。

関連記事

カテゴリー

アーカイブ