iOS 14 / watchOS 7 HealthKit 変更点

iOS 14 / watchOS 7 から HealthKit に症状を記録するデータタイプや、歩行に関するより詳細なデータタイプが追加されるなどの変更がありました。

ECG 関連のアップデートが中心な感じですが、2021.1 の watchOS 7.3 アップデートで日本でも ECG が使えるようになったのでまとめてみました。

# 心拍に関するデータタイプ HKElectrocardiogramType の追加

wathOS 7.3 (iOS 14.4) より、日本でも Apple Watch で心電図（ECG）が使えるようになりました。アメリカので二年遅れてついに日本で使えるようになりました。Apple Watch S4,5,6 で使用可能です。

[https://www.apple.com/jp/newsroom/2021/01/ecg-app-and-irregular-rhythm-notification-coming-to-apple-watch/](https://www.apple.com/jp/newsroom/2021/01/ecg-app-and-irregular-rhythm-notification-coming-to-apple-watch/)

不規則な心拍（心房細動（AFib）の兆候がある不規則な心拍リズム）の通知を受け取ることができるようになり、心電図 Apple Watch アプリを使うと、心房細動、洞調律、低心拍数、高心拍数、判定不能のいずれかに分類することができるようになっています。

## HKElectrocardiogramType

この有無が記録されている HKElectrocardiogram をチェックすることが iOS14/watchOS7 SDK からできるようになりました。

```swift
let healthStore = HKHealthStore()
let healthTypes = Set([
    HKCategoryType.electrocardiogramType()
])

healthStore.requestAuthorization(toShare: nil, read: healthTypes)
    { (success, error) in
        // ...
    }
```

[https://developer.apple.com/documentation/healthkit/hkelectrocardiogram?changes=latest_major](https://developer.apple.com/documentation/healthkit/hkelectrocardiogram?changes=latest_major)

HKElectrocardiogram を取得するには HKSampleQuery で取得します。

```swift
let ecgType = HKObjectType.electrocardiogramType()

let ecgQuery = HKSampleQuery(sampleType: ecgType,
                             predicate: nil,
                             limit: HKObjectQueryNoLimit,
                             sortDescriptors: nil) { (query, samples, error) in
    if let error = error {
        fatalError(error.localizedDescription)
    }
    
    guard let ecgSamples = samples as? [HKElectrocardiogram] else {
        fatalError(String(describing: samples))
    }
    
    for sample in ecgSamples {
        print(sample)
    }
}

healthStore.execute(ecgQuery)
```

HKElectrocardiogramQuery に HKElectrocardiogram オブジェクトを渡して詳細な測定値を取得できます。

```swift
let voltageQuery = HKElectrocardiogramQuery(ecgSample) { (query, result) in
    switch(result) {
    
    case .measurement(let measurement):
        if let voltageQuantity = measurement.quantity(for: .appleWatchSimilarToLeadI) {
            print(voltageQuantity)
        }
    
    case .done:
        print("done")

    case .error(let error):
        fatalError(error.localizedDescription)

    }
}

healthStore.execute(voltageQuery)
```

```swift
-4.39537 mcV
1.91129 mcV
8.0672 mcV
14.0773 mcV
... 省略
```

# HealthKit で症状を記録することができるデータタイプの追加

iOS 13.6 / watchOS 7 以降から症状についての記録のためのデータタイプが追加されています。

iOS 14 でもさらに追加され、「寒気」「鼻水が出る」「咳」「腰の痛み」「物忘れ」「頭痛」「胸の痛み」「ニキビ」など、ほか含め合計 39 種類の症状が発生したことを記録する HealthKit 対応のログアプリなどが作れるようになりました。

全て HKCategoryTypeIdentifier のデータタイプになっているので、他の HKCategoryType のサンプルのように取り扱うことができるようになっています。

[https://developer.apple.com/documentation/healthkit/data_types/symptom_type_identifiers](https://developer.apple.com/documentation/healthkit/data_types/symptom_type_identifiers)

# 歩行速度や歩幅、階段の昇降速度のサンプルデータ型が追加

歩くことに関するいくつかのサンプルデータが追加されました。全て HKQuantityType 型のサンプルデータになります。

6 分間歩行の推定データが取得できるようになりました。

```swift
static let sixMinuteWalkTestDistance: HKQuantityTypeIdentifier
```

6 分間歩行とは何か、近畿中央呼吸器センターさんのホームページに記載されている内容を引用します。

> 6 分間歩行試験とは、6 分間平地を歩いていただくことによって、肺や心臓の病気が日常生活の労作にどの程度障害を及ぼしているのか調べるための検査です。[https://kcmc.hosp.go.jp/shinryo/hokou.html](https://kcmc.hosp.go.jp/shinryo/hokou.html)

歩行の速度や歩幅を示すデータ、さらに歩幅が左右で違う割合だとか、歩いている時に両足が地面についている瞬間の割合だとか、歩くことに関する複雑なデータが取得できるようになりました。

```swift
static let walkingSpeed: HKQuantityTypeIdentifier
static let walkingStepLength: HKQuantityTypeIdentifier
static let walkingAsymmetryPercentage: HKQuantityTypeIdentifier
static let walkingDoubleSupportPercentage: HKQuantityTypeIdentifier
```

階段登り下りの速度のデータが取得できるようになりました。

```swift
static let stairAscentSpeed: HKQuantityTypeIdentifier
static let stairDescentSpeed: HKQuantityTypeIdentifier
```

# 「手洗いした」データタイプが追加

watchOS 7 / Apple Watch S4,5,6 で、手洗いをマイクとモーションセンサーを使って自動で検知するようになりました。そのログデータを取り出すことができるようになります。

```swift
static let handwashingEvent: HKCategoryTypeIdentifier
```

HKSampleQuery で他のデータと同じようにサンプルを取り出すことができます。

SwiftUI で昨日今日の handwashingEvent を取得する例。他の HKCategoryType のデータタイプも同様の書き方で取得することができます。

```swift
import SwiftUI
import HealthKit

var healthStore: HKHealthStore!

struct ContentView: View {
    
    var body: some View {
        Button(action: {
            checkHealthKit()
        }, label: {
            Text("Button")
        })
    }
    
    func checkHealthKit() {
        healthStore = HKHealthStore()
        let healthTypes = Set([
            HKCategoryType.categoryType(forIdentifier: .handwashingEvent)!
        ])

        healthStore.requestAuthorization(toShare: nil, read: healthTypes)
            { (success, error) in
                fetchHandWashing()
            }
    }
    
    func fetchHandWashing() {
        let now = Date()
        let calendar = Calendar.current
        let yesterday = calendar.date(byAdding: .day, value: -1, to: calendar.startOfDay(for: now))
        let predicate = HKQuery.predicateForSamples(withStart: yesterday, end: now, options: [])
        
        let sortDescriptor = [NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: true)]
        let washingEvent = HKCategoryType.categoryType(forIdentifier: .handwashingEvent)!
        let query = HKSampleQuery(sampleType: washingEvent,
                                        predicate: predicate,
                                        limit: HKObjectQueryNoLimit,
                                        sortDescriptors: sortDescriptor) {
            (query, results, error) in
            
            guard error == nil else { print("error"); return }
            
            let format = DateFormatter()
            format.dateFormat = "yyyy-MM-dd HH:mm:ss"
            format.timeZone   = TimeZone(identifier: "Asia/Tokyo")
            print("\(format.string(from: yesterday!)) - \(format.string(from: now)) result 🥳:")
            
            if let tmpResults = results as? [HKCategorySample] {
                tmpResults.forEach { (sample) in
                    print(sample)
                }
            }
        }
        healthStore.execute(query)
    }
}
```

# ヘッドホンから有害とされる音量を受けた時の記録のデータタイプの追加

ヘッドホンからの音、うるさすぎるよ！っていうものです。

```swift
static let headphoneAudioExposureEvent: HKCategoryTypeIdentifier
```

これに合わせて、環境音がうるさすぎるの時のデータタイプ名はカテゴリを正しく表記する名称に変更になりました。

```swift
~~static let audioExposureEvent: HKCategoryTypeIdentifier~~
↓
static let environmentalAudioExposureEvent: HKCategoryTypeIdentifier
```

# ワークアウトタイプの追加

```swift
HKWorkoutActivityTypeCardioDance
HKWorkoutActivityTypeSocialDance
HKWorkoutActivityTypePickleball
HKWorkoutActivityTypeCooldown
```

Cardio Dance は YouTube とかで検索するといっぱい出てきますね。

Social Dance はフォークダンスなどパートナーとか何人かでやるダンス。

Pickle Ball っていう新しいアメリカで流行り始めている？スポーツが追加されたようです。歴史的には 50 年ぐらいあるらしい。 [https://www.playpickleball.com](https://www.playpickleball.com/)

Cooldown は運動前後のクールダウン的な運動やストレッチを明示的にするための定数ですね。Apple Fitness+ サービス（日本では提供されていませんが）のメニューに対応した定数が今後も増えそうな気がします。

# (iOS 14.3 / watchOS 7.3 以降)妊娠出産に関するヘルスケアデータタイプの追加

妊娠出産関連のカテゴリタイプが追加されたので、それに対応するデータタイプが追加されています。

```swift
static let contraceptive: HKCategoryTypeIdentifier // 避妊薬
static let lactation: HKCategoryTypeIdentifier // 授乳
static let pregnancy: HKCategoryTypeIdentifier // 妊娠
```

# (iOS 14.3/watchOS 7.2 以降) HKSample.hashUndeterminedDuration の追加

```swift
var hasUndeterminedDuration: Bool { get }
```

サンプルの endDate プロパティが distantFuture の場合 true になります。

distantFuture とは「遠い未来の日付」で、イベントの終了待ちをするまでのテンポラリ値として入れておくなどの使い方をしたりします。

```swift
print(Date.distantFuture)
// 4001-01-01 00:00:00 +0000
```

# おわり

HealthKit を解説している拙著「[HealthKit Book for Beginners](https://booth.pm/ja/items/1304218)」にも本記事を追加しましてアップデート（v3.0）しましたので、購入していただいた方は PDF 版再ダウンロードおねがいしますー。（製本版は v1.0 のままです）