22
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

DeNAAdvent Calendar 2020

Day 13

[iOS] Healthkitを使う前に知っておきたいハマりどころ

Last updated at Posted at 2020-12-12

Healthkitとは

iOSでは、iOSやサードパーティアプリから ヘルスケア にいろいろなヘルスケアデータが書き込まれます。
iOSアプリはヘルスケアデータをHealthkit経由で読み書きが可能で、またデータの更新を監視することができます。

それでは直近2年ぐらいで踏み抜いたHealthkit関連のTipsをどうぞ

Healthkitのハマりどころ

1. iPadをサポートしてないアプリでもiPadで機能が動いてないとAppStore申請時にリジェクトされる

原因

  • 「ヘルスケア」はiPadに入ってないのでiPadのサポートを切ることが多い
  • しかしAppleの人はiPadのiPhone互換モードで動作確認する

解決策

↓のメソッドでヘルスケアが利用可能な端末か識別して制御しておく

HKHealthStore
class func isHealthDataAvailable() -> Bool

2. AppStoreの申請情報の書き方でリジェクトされる

ヘルスケアデータのデータソースとしてAppleのヘルスケア以外も対応してるアプリで↓のような文章を書くとAppStoreレビューでリジェクトされることがある。

リジェクトされる例
このアプリはFitbitから歩数データを連携できます!

解決策

Appleのヘルスケアも対応していることを併記する

リジェクトされない例
このアプリはAppleのヘルスケアやFitbitから歩数データを連携できます!

3. パーミッション要求しても許可画面が表示されないことがある

↓のメソッドでヘルスケアのパーミッション要求をすると許可画面が表示されるはずが、

HKHealthStore
func requestAuthorization(toShare typesToShare: Set<HKSampleType>?, 
                     read typesToRead: Set<HKObjectType>?, 
               completion: @escaping (Bool, Error?) -> Void)

completion に↓のようなErrorが返ってきて表示されない。

Error Domain=com.apple.healthd.SQLite Code=1 "Transaction block failed without an error." UserInfo={NSLocalizedDescription=Transaction block failed without an error.}

原因

  • 数年前から開発者フォーラム に投稿されているが明確な原因はわからない
  • iPhoneSE第一世代/iOS13.2.1で実際に発生していた
  • アプリのアンインストール/再インストールを繰り返しているとまれに発生する

解決策

  • 端末を再起動すると発生しなくなった(事象ベースで根本的に解決してませんすいません)
  • パーミッション要求のエラーハンドリングでユーザに端末の再起動を促す

4. アクセス許可したあとにタップ操作ができなくなることがある

原因

ヘルスケアのパーミッション要求し、ユーザが許可画面で許可/不許可を設定して画面がクローズした後もHealthkitが表示した透明のViewを持つUIWindowが最前面に残り続けることがある。

解決策

アプリのメインWindowとHealthkitが表示したWindowの前面になるようWindowレベルを設定する。
詳しくはこちらをどうぞ。

5. データの読み出しが不許可になっていることはわからない/データが微妙に取得できない

書き込みのパーミッションは↓で取得できるが、読み出しの許可状態はわからない

HKHealthStore
func authorizationStatus(for type: HKObjectType) -> HKAuthorizationStatus

理由

  • ドキュメント に書かれているとおり、機密性の高い健康情報の漏洩を防ぐためにアプリはユーザーがデータの読み出しを許可したかどうかを判断できないようになっている
  • 取得処理を実行した場合はデータがないように見える
    • 自身のアプリが書き込んだデータは読み出しが不許可でも取得できる

6. データが取得できない

原因

7. 取得したデータの値が異常なことがある

歩数など1日で歩けないような数値が入ってくることがある

原因①

データを手入力できるアプリを経由してヘルスケアに書き込まれている

解決策①

Appleの端末で自動計測されたデータだけに絞る(サードパーティアプリからヘルスケアに連携されたデータは含まない)

こんな感じで限定することができる

一定期間の日次歩数合計をApple端末の自動計測だけに絞って取得するサンプル
let healthStore = HKHealthStore()

let stepCountType = HKQuantityType.quantityType(forIdentifier: .stepCount)!
let now = Date()
let start = now.addingTimeInterval(-1 * 60 * 60 * 24 * 30)
let end = now
let anchorDate = Calendar.current.startOfDay(for: now)
var intervalComponents = DateComponents()
intervalComponents.day = 1

let datePredicate = HKQuery.predicateForSamples(withStart: start, end: end)
let statisticsCollectionQuery = HKStatisticsCollectionQuery(quantityType: stepCountType,
                                        quantitySamplePredicate: datePredicate,
                                        options: [.cumulativeSum, .separateBySource],
                                        anchorDate: anchorDate,
                                        intervalComponents: intervalComponents)

statisticsCollectionQuery.initialResultsHandler = { query, results, error in
    guard let sources = results?.sources().filter({ $0.bundleIdentifier.lowercased().hasPrefix("com.apple.health") }) else {
        return
    }
    
    let sourcesPredicate = HKQuery.predicateForObjects(from: Set(sources))
    let predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [datePredicate, sourcesPredicate])
    
    let statisticsCollectionQuery2 = HKStatisticsCollectionQuery(quantityType: stepCountType,
                                            quantitySamplePredicate: predicate,
                                            options: [.cumulativeSum, .separateBySource],
                                            anchorDate: anchorDate,
                                            intervalComponents: intervalComponents)
    
    statisticsCollectionQuery2.initialResultsHandler = { query, results, error in
        results?.statistics().forEach {
            print($0.sumQuantity()) // 日次の歩数合計
        }
        
    }
    
    healthStore.execute(statisticsCollectionQuery2)
}
healthStore.execute(statisticsCollectionQuery)

HKStatisticsCollectionQuery でソースを絞るやり方はもっといい方法があるかもしれません

原因②

ヘルスケアでデータが手入力されている

※ヘルスケアの入力画面
sample.image

解決策②

ヘルスケアに手入力されたデータはクエリで除外する

HKSampleQueryHKStatisticsQuery などに↓を追加するとユーザが手入力したデータを除外できる

let predicate = NSPredicate(format: "metadata.%K != YES", HKMetadataKeyWasUserEntered)

8. iCloudバックアップ/iTunesバックアップからの復元時、ヘルスケアからアプリへの読み出し/書き込み許可はリセットされる

  • UserDefaultsやKeychainなどのデータやプッシュ通知許可などは復元されるが、ヘルスケアのパーミッションは復元されない
  • ユーザが機種変更したときなどヘルスケアのパーミッションだけがリセットされた状態になる

設定アプリ→[ヘルスケア]→[データアクセスとデバイス] で確認するとAPPが消えている(例)
sample.image

おわり

Appleもすごいヘルスケアに力を入れていて扱えるデータの種類もどんどん増えているので、これからアプリを作ってみたい人のお役に立てば幸いです!

また DeNA 公式 Twitter アカウント @DeNAxTech では色々な勉強会での登壇資料も発信してます。ぜひフォローして下さい!

22
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?