React NativeアプリにおけるApple HealthKitとGoogle Fitの統合
React NativeでApple HealthKitとGoogle Fit(Health Connect)を統合するための完全ガイド。セキュアなパーミッション要求、歩数/心拍数データの読み取り、ワークアウトデータの書き込みを行う統合APIの構築方法を学びます。
このページの内容
急成長を続けるヘルステック分野において、ユーザーはApple HealthやGoogle Fitといった主要な健康エコシステムとシームレスに連携するアプリを実現することを求めています。React Nativeの開発者にとって、これは非常に異なる2つのネイティブプラットフォームに対応した、シンプルで elegante なソリューションをどのように構築するかという独特の課題です。本チュートリアルでは、ユーザーの健康データをセキュアに読み書きするための堅牢で統一されたAPIの構築について詳しく説明します。
ここでは、以下のことができるReact Nativeアプリケーションを構築します:
- healthデータへのアクセスについてユーザーの許可を求める。
- 1日の歩数和直近の心拍数サンプルを読み取る。
- 完了したワークアウトセッションをネイティブヘルスストアに書き込む。
- プラットフォーム固有のロジックを統一されたサービスに抽象化する。
本ガイドは、React Nativeの基本的な理解を持つ開発者を対象としています。ネイティブプロジェクトの構成を完全に制御するために、ベアReact Nativeセットアップで作業します。
開発者にとってなぜ重要か:
- ユーザー体験の向上:OSレベルのヘルスストアと連携することで、ユーザーはすべての健康データを1箇所で確認でき、シームレスな体験を提供できます。
- データ豊富なアプリケーション:価値のある健康データを活用して、パーソナライズされたインサイト、コーチング、機能を提供します。
- アプリの定着率向上:ユーザーの毎日の健康ルーティンに組み込まれたアプリは、より長く使い続けられやすくなります。
問題の理解
Apple HealthKit(iOS)と、Google Fitの後継であるHealth Connect(Android)は強力ですが、根本的に異なるAPIです。単純な実装では、プラットフォーム固有のコードがアプリ全体に散在する面倒なものになります。
- プラットフォームの分岐:iOSでは、HealthKit用の成熟したライブラリである
react-native-healthを使用します。Androidでは、Google Fit APIから、よりモダンでプライバシー重視のHealth Connect APIへと状況が変化しています。これにはreact-native-health-connectを使用します。 - パーミッションレッド:各プラットフォームには、それぞれ固有のパーミッション要求ワークフローがあり、適切に処理する必要があります。
- データ構造:HealthKitとHealth Connectでは、データの構造化方法和クエリが異なります。
私たちのアプローチは、「ヘルスサービス」抽象化レイヤーを作成することです。このレイヤーは、アプリケーションの残りの部分に対して、getStepCount()、saveWorkout()などのシンプルで統一されたメソッドのセットを公開します。内部的には、OSを検出して適切なネイティブライブラリを呼び出します。
前提条件
- Node.jsとWatchman:Node.js(LTSバージョン)とWatchmanがインストールされていることを確認してください。
- React Native環境:iOSとAndroidの両方(XcodeとAndroid Studio)の開発環境が適切に構成されていること。React Native環境セットアップガイドを参照してください。
- 物理デバイス:物理的なiPhoneとAndroidデバイスへのアクセスを強く推奨します。シミュレーターでは健康データが制限されているか、まったくない場合があります。
- React Nativeバージョン:
0.71以上を推奨します。
ステップ1:プロジェクトセットアップとライブラリのインストール
まず、新しいReact Nativeプロジェクトを作成します。
npx react-native@latest init HealthApp
cd HealthApp
次に、各プラットフォームに必要なライブラリをインストールします。
# For Apple HealthKit on iOS
npm install react-native-health
# For Health Connect on Android
npm install react-native-health-connect
iOSでは、 podsもインストールする必要があります。
cd ios && pod install && cd ..
ステップ2:iOSネイティブ構成(react-native-health)
何をするか
HealthKitを有効にし、ユーザーの健康データへのアクセスを要求する理由をユーザーに通知するために、ネイティブiOSプロジェクトを構成する必要があります。
実装
-
XcodeでHealthKitを有効にする:
/ios/HealthApp.xcworkspaceをXcodeで開きます。- ナビゲーターで
HealthAppプロジェクトを選択し、HealthAppターゲットをタップします。 - 「Signing & Capabilities」タブに移動します。
+ Capabilityをクリックし、HealthKitをダブルクリックします。これにより、機能が追加され、.entitlementsファイルが作成されます。
-
Info.plistに使用DESCRIPTIONを追加する:- Xcodeで
Info.plistファイルを開きます。 +アイコンをクリックして2つの新しいキーを追加します:Privacy - Health Share Usage Description:ユーザーにとってわかりやすいメッセージを設定します(例:「パーソナライズされたインサイトを提供するために健康データを読み取る必要があります。」)Privacy - Health Update Usage Description:「ワークアウトデータをApple Healthでアクティビティを追跡するために書き込む必要があります」を設定します。
- Xcodeで
仕組み
HealthKit機能を有効にすることで、iOSはアプリがHealthKitストアとやり取りする意图であることを認識します。Info.plistエントリはユーザーのプライバシーにとって重要です。iOSはアプリが初めてパーミッションを要求的时候にこれらの文字列をユーザーに表示します。これらがない場合、アプリがクラッシュします。
ステップ3:Androidネイティブ構成(react-native-health-connect)
何をするか
Androidでは、AndroidManifest.xmlでアプリがHealth Connectを使用するという意图を宣言し、要求する正確なパーミissionsを指定する必要があります。
実装
-
AndroidManifest.xmlを構成する:/android/app/src/main/AndroidManifest.xmlを開きます。<manifest>タグ内に必要なパーミissionsを追加します。
xml <!-- Required to specify which Health Connect permissions the app can request --> <uses-permission android:name="android.permission.health.READ_STEPS" /> <uses-permission android:name="android.permission.health.READ_HEART_RATE" /> <uses-permission android:name="android.permission.health.WRITE_EXERCISE" /> <uses-permission android:name="android.permission.health.READ_EXERCISE" /><application>タグ内に、Health Connectパーミission理由画面を処理するintentフィルターを追加します。
xml <activity android:name=".MainActivity" android:exported="true"> <!-- ... existing intent-filter ... --> <intent-filter> <action android:name="android.health.connect.action.ACTION_HEALTH_CONNECT_SETTINGS" /> </intent-filter> </activity>
仕組み
<uses-permission>タグは、Android OSとGoogle Playストアに、アプリがアクセスする可能性のある機密データについて通知します。intent-filterにより、アプリはHealth Connectパーミission管理画面に正しくディープリンクでき、これはユーザーフローの必須 частиです。
ステップ4:統一されたHealth APIサービスの作成
ここでプラットフォームの違いを抽象化します。
何をするか
Reactコンポーネントに対して一貫した関数のセットを公開する単一のJavaScriptファイルsrc/services/HealthService.jsを作成します。このサービスは内部的にPlatform.OSに基づいて適切なライブラリを呼び出します。
実装
新しいディレクトリsrc/servicesとファイルHealthService.jsを作成します。
// src/services/HealthService.js
import { Platform } from 'react-native';
import AppleHealthKit from 'react-native-health';
import {
initialize,
requestPermission,
readRecords,
writeRecords,
} from 'react-native-health-connect';
// Define permissions for both platforms
const permissions = {
ios: {
permissions: {
read: [
AppleHealthKit.Constants.Permissions.Steps,
AppleHealthKit.Constants.Permissions.HeartRate,
AppleHealthKit.Constants.Permissions.Workout,
],
write: [AppleHealthKit.Constants.Permissions.Workout],
},
},
android: {
permissions: [
{ accessType: 'read', recordType: 'Steps' },
{ accessType: 'read', recordType: 'HeartRate' },
{ accessType: 'write', recordType: 'ExerciseSession' },
{ accessType: 'read', recordType: 'ExerciseSession' },
],
},
};
const requestPermissions = async () => {
if (Platform.OS === 'ios') {
return new Promise((resolve, reject) => {
AppleHealthKit.initHealthKit(permissions.ios, (error, results) => {
if (error) {
console.log('[ERROR] Failed to grant permissions!', error);
return reject(error);
}
resolve(results);
});
});
} else {
try {
await initialize();
const granted = await requestPermission(permissions.android);
return granted;
} catch (error) {
console.log('[ERROR] Failed to grant permissions!', error);
throw error;
}
}
};
const getDailyStepCount = async () => {
const today = new Date();
if (Platform.OS === 'ios') {
return new Promise((resolve, reject) => {
const options = { date: today.toISOString() };
AppleHealthKit.getStepCount(options, (err, result) => {
if (err) {
return reject(err);
}
resolve(result.value);
});
});
} else {
const today = new Date();
today.setHours(0, 0, 0, 0);
const records = await readRecords('Steps', {
timeRangeFilter: {
operator: 'between',
startTime: today.toISOString(),
endTime: new Date().toISOString(),
},
});
return records.reduce((sum, record) => sum + record.count, 0);
}
};
// Other methods (getHeartRate, saveWorkout) will go here...
export default {
requestPermissions,
getDailyStepCount,
// ... export other methods
};
仕組み
requestPermissions関数はPlatform.OSを使用して、AppleHealthKit.initHealthKitまたはreact-native-health-connectのrequestPermissionのどちらを呼び出すかを決定します。両方のプラットフォームのパーミissionsを構造化された方法で定義しました。getDailyStepCount関数は同様に、各ライブラリに対してオプションとレスポンス処理を調整します。
ステップ5:データの読み取りと書き込み
HealthService.jsに心拍数和ワークアウト用の関数を追加しましょう。
何をするか
サービスにgetHeartRateSamplesとsaveWorkoutを追加し снова、プラットフォームの違いを処理します。
実装
src/services/HealthService.jsに次の関数を追加します。
// ... inside src/services/HealthService.js
const getHeartRateSamples = async () => {
const now = new Date();
const yesterday = new Date();
yesterday.setDate(now.getDate() - 1);
if (Platform.OS === 'ios') {
return new Promise((resolve, reject) => {
const options = {
startDate: yesterday.toISOString(),
endDate: now.toISOString(),
};
AppleHealthKit.getHeartRateSamples(options, (err, results) => {
if (err) {
return reject(err);
}
resolve(results);
});
});
} else {
const records = await readRecords('HeartRate', {
timeRangeFilter: {
operator: 'between',
startTime: yesterday.toISOString(),
endTime: now.toISOString(),
},
});
return records.map(record => ({
value: record.samples[0].beatsPerMinute,
endDate: record.time,
}));
}
};
const saveWorkout = async () => {
const startTime = new Date();
startTime.setHours(startTime.getHours() - 1); // 1-hour workout
const endTime = new Date();
if (Platform.OS === 'ios') {
return new Promise((resolve, reject) => {
const options = {
type: AppleHealthKit.Constants.Activities.Running,
startDate: startTime.toISOString(),
endDate: endTime.toISOString(),
energyBurned: 300, // in calories
totalDistance: 5, // in kilometers
};
AppleHealthKit.saveWorkout(options, (err, result) => {
if (err) {
return reject(err);
}
resolve(result);
});
});
} else {
const records = await writeRecords([
{
recordType: 'ExerciseSession',
startTime: startTime.toISOString(),
endTime: endTime.toISOString(),
exerciseType: 'Running',
title: 'Morning Run',
},
]);
return records;
}
};
export default {
requestPermissions,
getDailyStepCount,
getHeartRateSamples,
saveWorkout,
};
すべてをまとめる
では、新しく作成したサービスをReactコンポーネントで使用しましょう。
実装
App.tsx(またはApp.js)の中身を次の内容に置き換えます。
// App.tsx
import React, { useState, useEffect } from 'react';
import {
SafeAreaView,
ScrollView,
StatusBar,
StyleSheet,
Text,
Button,
View,
} from 'react-native';
import HealthService from './src/services/HealthService';
const App = () => {
const [hasPermissions, setHasPermissions] = useState(false);
const [steps, setSteps] = useState(0);
const [heartRate, setHeartRate] = useState(0);
useEffect(() => {
// Check for permissions on component mount
HealthService.requestPermissions().then(setHasPermissions).catch(console.error);
}, []);
const fetchHealthData = () => {
if (!hasPermissions) {
alert('Permissions not granted!');
return;
}
HealthService.getDailyStepCount()
.then(setSteps)
.catch(err => console.error('Steps Error:', err));
HealthService.getHeartRateSamples()
.then(samples => {
if (samples.length > 0) {
setHeartRate(samples[samples.length - 1].value);
}
})
.catch(err => console.error('Heart Rate Error:', err));
};
const handleSaveWorkout = () => {
HealthService.saveWorkout()
.then(() => alert('Workout saved successfully!'))
.catch(err => console.error('Workout Save Error:', err));
};
return (
<SafeAreaView style={styles.container}>
<StatusBar barStyle={'dark-content'} />
<ScrollView contentInsetAdjustmentBehavior="automatic">
<View style={styles.section}>
<Text style={styles.title}>React Native Health Demo</Text>
<Text>Permissions Granted: {hasPermissions ? '✅' : '❌'}</Text>
</View>
<View style={styles.section}>
<Button title="Fetch Health Data" onPress={fetchHealthData} />
<Text style={styles.dataText}>Steps Today: {steps}</Text>
<Text style={styles.dataText}>Latest Heart Rate: {heartRate} BPM</Text>
</View>
<View style={styles.section}>
<Button title="Save a Test Workout" onPress={handleSaveWorkout} />
</View>
</ScrollView>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: { flex: 1 },
section: {
padding: 24,
borderBottomWidth: 1,
borderBottomColor: '#ccc',
},
title: {
fontSize: 24,
fontWeight: '600',
marginBottom: 8,
},
dataText: {
fontSize: 18,
marginTop: 8,
},
});
export default App;
物理デバイスでアプリを実行しましょう。
npx react-native run-ios --device="Your iPhone Name"
npx react-native run-android --deviceId="Your Device ID"
健康パーミissionsを求められます。許可を与えた後、データを取得してテストワークアウトを保存できます!
セキュリティのベストプラクティス
健康データを取り扱うことは、大きな責任を伴います。
- 最小限の範囲を要求する:アプリの機能に本当に必要なパーミissionsだけを要求してください。全権を要求するアプリにはユーザーが警戒します。
- 理由を説明する:パーミissionsプロンプトを表示する前に、モーダルまたは画面を使用して、ユーザーのデータを必要とする理由とそれを使って何をするかを説明してください。
- データのプライバシー:明確なプライバシーポリシーなしに、ユーザーの明示的な同意なしにHealthKitまたはHealth Connectデータをサーバーに送信しないでください。可能な限り、データを匿名化または集計してください。
- 拒否を適切に処理する:ユーザーはパーミissionsを拒否できます。アプリはクラッシュしてはなりません。代わりに、特定の機能が利用できないことを説明するメッセージを表示し、後からパーミissionsを有効にするための設定へのショートカットを提供してください。
- HIPAA:アプリが米国で臨床的文脈で使用されるとみなされる場合、HIPAA(医療保険の相互運用性と説明責任に関する法律)の規制を認識する必要があります。この法律は保護された健康情報のプライバシーとセキュリティを管轄しています。
結論
Apple HealthKitとGoogleのHealth Connectの両方に連携するクロスプラットフォームReact Nativeアプリを正常に構築しました。統一されたAPIサービスを作成することで、アプリケーションロジックをクリーンで保守可能な状態に保ち、プラットフォーム固有のコードを1箇所に隔離しました。このアーキテクチャにより、将来新しい健康指標のサポートを簡単に追加できます。
次のステップは、より多くの数据类型を探索し、このデータで美しい可視化を作成し、ユーザーの健康ジャーニーにおいて本当の価値を提供する機能を構築することです。
リソース
- react-native-health(iOS):GitHub Repository
- react-native-health-connect(Android):npm Package
- Apple HealthKit Documentation:Developer Docs
- Google Health Connect Documentation:Developer Docs
クイックリファレンス
| 側面 | 主要情報 |
|---|---|
| トピック | 開発 |
| 関連性 | 患者の教育と意識向上 |
| アクション | 医療提供者との相談 |
| 頻度 | ケアチーム推奨の通り |
| 証拠 | 現在の臨床ガイドラインに基づく |
よくある質問
Q: これが自分に該当するかはどうすればわかりますか? A: 個人的な病史と家族歴について医療提供者と相談し、個人的なリスク因子を評価し、適切な次のステップを決定してください。
Q: 医師に何を聞くべきですか? A: 좋은質問には次のようなものがあります:「私の結果はどういう意味ですか?」「私のオプションは何ですか?」「どんな生活習慣の改善が役立ちますか?」「いつフォローアップすべきですか?」
Q: 有所帮助生活习惯の変更はありますか? A: ほとんどの健康状態は、基本的な措置から Benefits :バランスの 取れた栄養、定期的な身体活動(週150分以上)、質の良い睡眠(7〜9時間)、ストレス管理、タバコの回避。
医療免责声明
本文書は教育目的のみを目的としており、医療上のアドバイスを構成するものではありません。健康、治療、またはケアに関する決定を行う前は、必ず資格のある医療提供者と相談してください。個人が異なる情况にあり、1人に適切なことが別の人に適切であるとは限りません。
救急車を要する緊急事態の場合は、直ちに911(またはお住まいの地域の緊急電話番号)に電話してください。