ReactNativeでAppleヘルスケアと連携する
はじめに
株式会社Entaleで長期インターンとしてペアケアの開発に携わっています。業務でReact NativeとAppleヘルスケアを連携する実装に取り組む中で、苦戦した部分があったため、振り返りを兼ねて本記事にまとめました。
やりたいこと
ヘルスケアから基礎体温のデータを取得し、アプリに書き込む(ヘルスケアへの書き込みはしない)
使用するライブラリ
方針
アプリをAppleヘルスケアと連携させるには、以下のモーダルからユーザーに許可してもらうか、設定から許可してもらう必要があります。

このモーダルについては、HKAuthorizationRequestStatusというものが用意されており、これをもとに出し分けることができます。
後述するケースでは、このステータスのみを許可状態の判断基準として使います。
export enum HKAuthorizationRequestStatus {
unknown = 0,
shouldRequest = 1, // リクエストができる状態
unnecessary = 2 // 一度リクエストモーダルを表示しており、リクエストできない状態
}
また、Healthkitの許可状態として、HKAuthorizationStatusが用意されていますが、これを取得できるのは書き込みの権限[1]のみであり、読み取りの権限について取得するための関数が存在しません。
つまり、HKAuthorizationStatusによって読み取りの権限があるかどうかの判断ができないことになります。
export enum HKAuthorizationStatus {
notDetermined = 0, // 許可も拒否もされていない
sharingDenied = 1, // 拒否済み
sharingAuthorized = 2 //許可済み
}
色々なアプリを見たところ、主に以下の二つのケースがあるようです。
許可、拒否に関わらず、モーダルが一度でも表示されたら連携済みとするケース
このケースの実装は単純で、HKAuthorizationRequestStatusがunnecessaryであれば連携済みと判断してしまうということになります。
shouldRequestはまだ一度も許可モーダルを表示していない状態。
unnecessaryは一度許可モーダルを表示している状態で、この状態で再度モーダルを表示することは不可能になります。(再インストールすればshouldRequestに戻ります)
データが取得できたら連携済みとするケース
@kingstinct/react-native-healthkitでは、queryQuantitySamplesという関数が用意されており、これを用いてデータを取得することができます。
内部的にはHKSampleQueryが使われているようです。
この関数では、連携が許可されているがデータがない場合、許可されていない場合のどちらでも空配列が返されます。
このケースでは、許可の状態に関わらず、データが取得できる時のみに連携済みと判断します。
今回実装するケース
検討した結果、ペアケアではモーダル表示前、表示後の画面を作成し、データが取得できない場合はトーストを表示することになりました。
バックグラウンドでデータの連携をすることも可能なようですが、今回はアプリ起動時に連携することにしました。
実装
必要な状態
今回の実装において必要な状態は、モーダルが表示されたかどうかと、データが取得できるかどうかです。
まずはその二つの状態を取得するための関数を作成します。
const isAuthReqStatusUnnecessary = () => {
const status = getRequestStatusForAuthorization([
HKQuantityTypeIdentifier.basalBodyTemperature,
])
return status === HKAuthorizationRequestStatus.unnecessary
}
getRequestStatusForAuthorizationによってHKAuthorizationRequestStatusを取得することができます。
const hasPermission = async () => {
const samples = await queryQuantitySamples(
HKTypeIdentifier.basalBodyTemperature,
{
unit: TemperatureUnit.DegreesCelsius,
limit: 1,
}
)
return samples.length !== 0
}
queryQuantitySamplesで空配列が返されなければ権限があると判断します。
用意するコンポーネントとフックス
今回は起動時に連携をするため、layoutファイル内に配置するHealthcareObserverコンポーネント、連携を設定するページに配置するAppleHealthcare, AppleHalthcareAuthorizedコンポーネントと、上記の関数を格納しておくuseAppleHealthcareフックスを作成します。
サーバーサイドで連携済みかどうかのプロパティを持たせ、それをもとにデータを取得しに行くか判断することも考えましたが、再インストールや、他端末からのログインが発生した際に不整合が発生する可能性があるため、連携済みかどうかはアプリ内で完結させます。
isAuthReqStatusUnnecessaryをもとにコンポーネントを出し分けることを考えると、page.tsxは以下のように実装できます。
export default function Page() {
const { isReady, unnecessary, hasPermission, showModal } = useAppleHealthcare()
return (
{
!isReady ?
<Loading />
:
unnecessary ?
<AppleHealthcareAuthorized hasPermission={hasPermission} />
:
<AppleHealthcare showModal={showModal} />
}
)
}
連携モーダルが表示された後(unnecessary)はAppleHealthcareAuthorizedがレンダリングされます。
このコンポーネント内では、設定に遷移することができるリンクボタンと、hasPermissionを元にトーストを出し分けます。
type Props = {
hasPermission: boolean
}
export const AppleHealthcareAuthorized = (props: Props) => {
if (props.hasPermission) {
showToast("success")
} else {
showToast("failure")
}
return (
<>
// 省略
</>
)
}
データの連携
ここまででユーザーがアプリとヘルスケアを連携する動線が実装できました。
後は起動時にデータを取得して連携するだけです。
export default function HealthcareObserver = () => {
const { hasPermission } = useAppleHealthcare()
useEffect(() => {
if (hasPermission) {
// get & post
}
},[])
return <>{children}</>
}
-
requestAuthorizationなどの関数の返し値に含まれます。https://developer.apple.com/documentation/healthkit/hkhealthstore/1614152-requestauthorization ↩︎
Discussion