はじめに
iOSでは、iOSや各アプリから"ヘルスケア"にいろいろなヘルスケアデータが書き込まれます。
iOSアプリはヘルスケアデータをHealthkit経由で読み書きが可能で、またデータの更新を監視することができます。
そして更新の監視はアプリが終了している状態でも有効にすることができる、というのがこの記事の主旨です。
アプリの状態 | ヘルスケアデータの更新検知 |
---|---|
フォアグラウンドで実行されている | 可能 |
実行されているがフォアグラウンドにない | 可能 |
アプリが終了している | 可能 ←ここ |
↓のサンプルでは、アプリが終了している状態でヘルスケアへのデータ書き込みを検知して、アプリからローカル通知を送信してます。
※最初左右にスワイプしてるのはヘルスケア以外にアプリ起動してないアピールです
サンプル |
---|
実装方法
- Healthkitで任意の種類のヘルスケアデータに対して
HKObserverQuery
を実行する -
enableBackgroundDelivery(for:frequency:withCompletion:)
を実行する
let objectType = HKSampleType.quantityType(forIdentifier: .bodyMass)!
let query = HKObserverQuery(sampleType: objectType, predicate: nil, updateHandler: { query, completionHandler, error in
// 更新検知
})
HKHealthStore().execute(query)
// バックグランドでのヘルスケアデータの更新検知を有効にする
HKHealthStore().enableBackgroundDelivery(for: objectType, frequency: .immediate, withCompletion: { success, error in
})
- 検知できる更新タイミングは
enableBackgroundDelivery(for:frequency:withCompletion:)
の引数として渡す HKUpdateFrequency で指定した期間に最大1回- 指定できる期間は
immediate
hourly
daily
weekly
、ただし特定のヘルスケアデータ(歩数など更新頻度が非常に高いものなど)はhourly
以上の頻度でしか検知できない
- 指定できる期間は
詳しくは公式ドキュメント参照。
サンプル
以下、体重データの更新を検知してローカル通知を送るサンプルです。
サンプルの動作環境: Xcode11.2.1
iOS13.2
プロジェクト設定
- ヘルスケアからデータの読み出しを許可するため、Info.plistに
Privacy - Health Share Usage Description
をセットする - project -> target -> Signing&Capabilities で以下を設定する
-
HealthKit
を有効にする -
PushNotifications
を有効にする(このサンプルでローカル通知を送るため)
サンプルコード
AppDelegate.swift
import UIKit
import UserNotifications
import HealthKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// プッシュ通知を有効にする
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge], completionHandler: { _, _ in
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
})
// ヘルスケアから体重データの読み込みを許可
let objectType = HKSampleType.quantityType(forIdentifier: .bodyMass)!
HKHealthStore().requestAuthorization(toShare: nil, read: Set([objectType]), completion: { success, error in
})
// ヘルスケアデータの更新を検知するクエリ
let query = HKObserverQuery(sampleType: objectType, predicate: nil, updateHandler: {
query, completionHandler, error in
if error != nil {
return
}
// ヘルスケアデータの更新を検知したらローカル通知を送る
let content = UNMutableNotificationContent()
content.body = "体重データが更新されました"
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false)
let request = UNNotificationRequest(identifier: "detection-test", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request)
completionHandler()
})
HKHealthStore().execute(query)
// バックグランドでのヘルスケアデータの更新検知を有効にする
HKHealthStore().enableBackgroundDelivery(for: objectType, frequency: .immediate, withCompletion: { success, error in
})
return true
}
}