WWDC Quick Look 💓 By SwiftGGTeam
Explore wellbeing APIs in HealthKit

Explore wellbeing APIs in HealthKit

元の動画を見る

ハイライト

Apple は昨年 Health app にメンタルヘルス機能を追加。ユーザーは感情状態(State of Mind)を記録でき、GAD-7 不安評価と PHQ-9 うつ評価の 2 つの標準化アンケートも完了できます。今年これらのデータ型が HealthKit API として開発者に正式公開されました。

主要内容

昨年 iOS 17 の Health app にメンタルヘルスモジュールが追加され、ユーザーは iPhone と Apple Watch で感情を記録し、不安・うつのアンケートを完了できます。しかしこのデータは Health app 内に閉じ込められ、サードパーティ app からはアクセスできませんでした。フィットネス app が「運動量」と「気分」の関係を比較したい、カレンダー app が「どの会議が最も不安を招くか」を知りたい——HealthKit に対応データ型がなかったため不可能でした。

今年 Apple は 3 つのメンタルヘルスデータ型を HealthKit API として公開。State of Mind、GAD-7(7 問不安リスク評価)、PHQ-9(9 問うつリスク評価)です。State of Mind が最も中核的で汎用的な新タイプ。4 属性を持ちます。kind は「瞬間的な感情」と「1 日の気分」を区別。valence は -1 から 1 の連続値でネガティブからポジティブを表現。labels は具体的情緒タグ(「不安」「解放感」「情熱」など)。associations は感情の源泉(「仕事」「家族」「アイデンティティ」など)。Apple は感情科学の専門家と協力してこのモデルを設計。核心原則は、ユーザーが能動的に立ち止まり自分の感情を振り返る行為自体に臨床的メリットがあることです。

詳細

権限リクエスト

State of Mind データの書き込み・読み取り前に、標準 HealthKit 認可フローで権限をリクエスト(05:37):

import HealthKitUI

func healthDataAccessRequest(
    store: HKHealthStore,
    shareTypes: Set<HKSampleType>,
    readTypes: Set<HKObjectType>? = nil,
    trigger: some Equatable,
    completion: @escaping (Result<Bool, any Error>) -> Void
) -> some View

キーポイント:

  • HealthKitUI フレームワーク提供の認可ビュー修飾子を使用
  • shareTypes は書き込みたいデータ型の集合
  • readTypes は読み取りたいデータ型の集合
  • すべてのヘルスデータはプライベートで安全。認可でユーザーが共有データを制御

State of Mind サンプルの作成

Session ではカレンダー app の Demo で実用法を示します。ユーザーがカレンダーイベントをタップ後、emoji で感情を選択し、app が emoji を State of Mind サンプルにマッピングして HealthKit に保存。まず emoji からデータへのマッピング(06:26):

enum EmojiType: CaseIterable {
    case angry
    case sad
    case indifferent
    case satisfied
    case happy

    var emoji: String {
        switch self {
        case .angry: return "😡"
        case .sad: return "😢"
        case .indifferent: return "😐"
        case .satisfied: return "😌"
        case .happy: return "😊"
        }
    }
}

キーポイント:

  • 5 つの enum 値が 5 段階の感情に対応
  • 各 case が具体 emoji 文字にマッピング
  • この enum は後で valencelabel プロパティを拡張

次に HKStateOfMind サンプルを作成(06:32):

func createSample(for event: EventModel, emojiType: EmojiType) ->
HKStateOfMind {
    let kind: HKStateOfMind.Kind = .momentaryEmotion
    let valence: Double = emojiType.valence
    let label = emojiType.label
    let association = event.association
    return HKStateOfMind(date: event.endDate,
                         kind: kind,
                         valence: valence,
                         labels: [label],
                         associations: [association])
}

キーポイント:

  • kind.momentaryEmotion。ユーザーはイベント終了後の瞬間的感情を記録。1 日全体の気分なら .dailyMood
  • valence は emojiType プロパティから取得。-1.0 から 1.0 の Double
  • labels は配列。複数の感情タグを指定可能
  • associations も配列。カレンダーイベントのカレンダーカテゴリから推論(Office カレンダーは Work association)
  • date はイベント終了時刻

保存は 1 行(07:21):

func save(sample: HKSample, healthStore: HKHealthStore) async {
    do {
        try await healthStore.save(sample)
    }
    catch {
        // Handle error here.
    }
}

State of Mind データのクエリ

Apple は 4 種類の新 HealthKit 述語を提供。Kind(感情/気分)、Valence(正負の程度)、Label(具体的情緒タグ)、Association(感情の源泉)でクエリ。Demo では 2 機能を実装。「カレンダー品質」指標の計算と「最も意味のあるイベント」の特定。

日付と association でクエリ(10:49):

let datePredicate: NSPredicate = { ... }
let associationsPredicate = NSCompoundPredicate(
    orPredicateWithSubpredicates: associations.map {
        HKQuery.predicateForStatesOfMind(with: $0)
    }
)
let compoundPredicate = NSCompoundPredicate(
    andPredicateWithSubpredicates: [datePredicate, associationsPredicate]
)
let stateOfMindPredicate = HKSamplePredicate.stateOfMind(compoundPredicate)

let descriptor = HKSampleQueryDescriptor(predicates: [stateOfMindPredicate],
                                         sortDescriptors: [])
var results: [HKStateOfMind] = []
do {
    results = try await descriptor.result(for: healthStore)
} catch {
    // Handle error here.
}

キーポイント:

  • HKQuery.predicateForStatesOfMind(with:) で association 別述語を作成
  • 複数 association は NSCompoundPredicate(orPredicateWithSubpredicates:) で OR 結合
  • 日付と association は AND 結合
  • HKSamplePredicate.stateOfMind() が NSCompoundPredicate を Swift Predicate にラップ
  • HKSampleQueryDescriptor は非同期クエリの標準パターン

平均 valence パーセンテージを計算(10:54):

let adjustedValenceResults = results.map { $0.valence + 1.0 }
let totalAdjustedValence = adjustedValenceResults.reduce(0.0, +)
let averageAdjustedValence = totalAdjustedValence / Double(results.count)
let adjustedValenceAsPercent = Int(100.0 * (averageAdjustedValence / 2.0))

キーポイント:

  • valence の元の範囲は -1.0 から 1.0。1.0 加算で 0.0 から 2.0 に
  • 平均後 2.0 で割り 100 倍し、0% から 100% の指標に
  • このパーセンテージが Demo の「Calendar Quality」指標

Label と Association の組み合わせクエリ

Demo ではより詳細なクエリも展示。Label と Association を同時にフィルタし、最も嬉しい仕事イベントを特定(11:33):

let label: HKStateOfMind.Label = .happy
let datePredicate = HKQuery.predicateForSamples(withStart: dateInterval.start,
                                                end: dateInterval.end)
let associationPredicate = HKQuery.predicateForStatesOfMind(with: association)
let labelPredicate = HKQuery.predicateForStatesOfMind(with: label)
let compoundPredicate = NSCompoundPredicate(
    andPredicateWithSubpredicates: [datePredicate, associationPredicate, labelPredicate]
)
let stateOfMindPredicate = HKSamplePredicate.stateOfMind(compoundPredicate)
let descriptor = HKAnchoredObjectQueryDescriptor(predicates: [stateOfMindPredicate],
                                                 anchor: nil)
let results = descriptor.results(for: healthStore)
let samples: [HKStateOfMind] = try await results.reduce([]) { $1.addedSamples }

キーポイント:

  • HKQuery.predicateForStatesOfMind(with:) は association と label 両方の述語に使用
  • 3 条件を AND 結合。日付範囲、特定 association、特定 label
  • HKSampleQueryDescriptor ではなく HKAnchoredObjectQueryDescriptor で増分クエリ
  • reduceaddedSamples を取り出し [HKStateOfMind] 配列を取得

valence 最高のサンプルを見つけた後、最も近いカレンダーイベントにマッチ(11:45):

let happiestSample = samples.max { $0.valence < $1.valence }
let happiestEvent: EventModel? = findClosestEvent(startDate: happiestSample?.startDate,
                                                  endDate: happiestSample?.endDate)

キーポイント:

  • max(by:) で valence 値が最高のサンプルを特定
  • 時間範囲で最も近いカレンダーイベントにマッチし「最も意味のあるイベント」機能を実現

GAD-7 と PHQ-9

GAD-7(7 問不安評価)と PHQ-9(9 問うつ評価)は Pfizer 開発の標準化臨床アンケートで、世界中の医師・臨床従事者が広く使用。開発者は今年両評価結果の読み書きが可能。Apple は Pfizer の標準フローに従うことを強調——簡略版を自作しない。臨床的有効性がこれらツールの核心価値。

重要ポイント

  • 何を作るか:時間管理・カレンダー系 app に State of Mind 記録を統合。ユーザーがカレンダーイベント完了後、emoji で感情を選択し、HKStateOfMind サンプルとして HealthKit に保存。なぜ価値があるか:数十行で app に感情次元を追加。「今日何をしたか」が「今日どう感じたか」に。どう始めるか:emoji picker UI を実装、valence と label にマッピングする EmojiType enum を作成、HKStateOfMind イニシャライザでサンプル作成。

  • 何を作るか:フィットネス app で State of Mind データをクエリし、運動データとクロス分析。例:「週間運動量」と「平均 valence」のトレンド比較。なぜ価値があるか:ユーザーは運動と気分の関連に気づかないことが多い。クロス分析で行動パターンを発見。どう始めるかHKSampleQueryDescriptor で指定期間の State of Mind サンプルをクエリ、平均 valence を計算、HKWorkout データと重ねて表示。

  • 何を作るか:メンタルヘルス app に GAD-7 / PHQ-9 アンケートを統合。app 内で標準化評価を完了し、結果を HealthKit に保存。なぜ価値があるか:両アンケートに臨床的有効性。HealthKit 書き込み後 Apple Health のメンタルヘルスチャートと連動。どう始めるか:Pfizer 標準フローを厳守。Apple 開発者ドキュメントの詳細を参照。

  • 何を作るか:4 種類の新述語(Kind / Valence / Label / Association)でパーソナライズ洞察を提供。例:Association 述語で「仕事」関連感情サンプルをクエリし、最も不安を招く時間帯を特定。なぜ価値があるか:集約感情データは生サンプルより価値が高い。「平日は通常不安を感じる」ような洞察が必要。どう始めるかHKQuery.predicateForStatesOfMind(with:) で association フィルタ、HKSamplePredicate.stateOfMind() でクエリ構築。

関連セッション

コメント

GitHub Issues · utterances