Rook SDK
This SDK enables apps to extract and upload data from Apple Health and Health Connect. With this SDK, you will be able to extract and upload data.
Content
Installation
The minimum version of android sdk is 26, the target sdk 34 and the kotlin version >= 1.8.10
The SDK requires Xcode 14.0.1 or higher.
The minimum version of iOS is 13.0
npm
npm install capacitor-rook-sdk
npx cap sync
Configuration
Add your client UUID to be authorized. Follow the example below and add the RookConfig at the top level of your main app. The password refers to the secret key.
import { RookConfig } from 'capacitor-rook-sdk';
import { useEffect, useState } from 'react';
const Home: React.FC = () => {
useEffect(() => {
RookConfig.initRook({
environment: 'sandbox',
clientUUID: 'YOUR-CLIENT-UUID',
password: 'YOUR-SECRET-KEY',
enableBackgroundSync: true,
enableEventsBackgroundSync: true,
})
.then(() => console.log('Initialized'))
.catch((e: any) => console.log('error', e));
}, []);
...
iOS Configuration
Then we need to add Apple Health Kit Framework to our project in order to that please:
- Open your project in Xcode.
- Click on your project file in the Project Navigator.
- Select your target and then click on the "Build Phases" tab.
- Click on the "+" button under the "Link Binary With Libraries" section and select "HealthKit.framework" from the list.
- Select your target and then click on the "Signing Capabilities" tab.
- Click on "Add Capability" and search for "HealthKit"
Additionally add the following to the info.plist
<key>NSHealthShareUsageDescription</key>
<string>This app requires access to your health and fitness data in order to track your workouts and activity levels.</string>
<key>NSHealthUpdateUsageDescription</key>
<string>This app requires permission to write healt data to HealthKit.</string>
Android Configuration
Then we need to configure the Android project. Open the Android project inside Android Studio. We need to modify the AndroidManifest.xml
file to access the Health Connect records.
Add an intent filter inside your activity tag to open the Health Connect app. Your AndroidManifest.xml
file should look like this:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
...
<application
...>
<activity
...>
<intent-filter>
...
</intent-filter>
<!-- For supported versions through Android 13, create an activity to show the rationale
of Health Connect permissions once users click the privacy policy link. -->
<intent-filter>
<action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
</intent-filter>
</activity>
<!-- For versions starting Android 14, create an activity alias to show the rationale
of Health Connect permissions once users click the privacy policy link. -->
<activity-alias
android:name="ViewPermissionUsageActivity"
android:exported="true"
android:permission="android.permission.START_VIEW_PERMISSION_USAGE"
android:targetActivity=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW_PERMISSION_USAGE" />
<category android:name="android.intent.category.HEALTH_PERMISSIONS" />
</intent-filter>
</activity-alias>
</application>
</manifest>
Included permissions for Android
This SDK will use the following permissions. There is no need to declare them in your manifest as the ROOK SDK already declares them in its own manifest:
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_HEALTH"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.health.READ_SLEEP"/>
<uses-permission android:name="android.permission.health.READ_STEPS"/>
<uses-permission android:name="android.permission.health.READ_DISTANCE"/>
<uses-permission android:name="android.permission.health.READ_FLOORS_CLIMBED"/>
<uses-permission android:name="android.permission.health.READ_ELEVATION_GAINED"/>
<uses-permission android:name="android.permission.health.READ_OXYGEN_SATURATION"/>
<uses-permission android:name="android.permission.health.READ_VO2_MAX"/>
<uses-permission android:name="android.permission.health.READ_TOTAL_CALORIES_BURNED"/>
<uses-permission android:name="android.permission.health.READ_ACTIVE_CALORIES_BURNED"/>
<uses-permission android:name="android.permission.health.READ_HEART_RATE"/>
<uses-permission android:name="android.permission.health.READ_RESTING_HEART_RATE"/>
<uses-permission android:name="android.permission.health.READ_HEART_RATE_VARIABILITY"/>
<uses-permission android:name="android.permission.health.READ_EXERCISE"/>
<uses-permission android:name="android.permission.health.READ_SPEED"/>
<uses-permission android:name="android.permission.health.READ_WEIGHT"/>
<uses-permission android:name="android.permission.health.READ_HEIGHT"/>
<uses-permission android:name="android.permission.health.READ_BLOOD_GLUCOSE"/>
<uses-permission android:name="android.permission.health.READ_BLOOD_PRESSURE"/>
<uses-permission android:name="android.permission.health.READ_HYDRATION"/>
<uses-permission android:name="android.permission.health.READ_BODY_TEMPERATURE"/>
<uses-permission android:name="android.permission.health.READ_RESPIRATORY_RATE"/>
<uses-permission android:name="android.permission.health.READ_NUTRITION"/>
<uses-permission android:name="android.permission.health.READ_MENSTRUATION"/>
<uses-permission android:name="android.permission.health.READ_POWER"/>
Google may require you to provide an explanation about the FOREGROUND_SERVICE
/FOREGROUND_SERVICE_HEALTH
permissions. These permissions are used by our Automatic Sync and Background Steps features to extract health data and upload it to ROOK servers. We recommend asking users for permission before enabling these features. Google may also require a video proof of a screen where a user can turn these features on or off.
Request data access for Android
When you are developing with the Health Connect SDK, data access is unrestricted. However, to have data access when your app is launched on the Play Store, you must complete the Developer Declaration Form. More information can be found here.
When you are asked about what data types your app is using, please add the following data types as READ access:
- ActiveCaloriesBurnedRecord
- BloodGlucoseRecord
- BloodPressureRecord
- BodyTemperatureRecord
- DistanceRecord
- ElevationGainedRecord
- ExerciseSessionRecord
- FloorsClimbedRecord
- HeartRateRecord
- HeartRateVariabilityRmssdRecord
- HeightRecord
- HydrationRecord
- MenstruationPeriodRecord
- NutritionRecord
- OxygenSaturationRecord
- PowerRecord
- RespiratoryRateRecord
- RestingHeartRateRecord
- SleepSessionRecord
- SpeedRecord
- StepsCadenceRecord
- StepsRecord
- TotalCaloriesBurnedRecord
- Vo2MaxRecord
- WeightRecord
Obfuscation for Android
If you are using obfuscation consider the following:
In your proguard-rules.pro add the following rule:
-keep class com.google.crypto.** { *; }
In your gradle.properties (Project level) add the following to disable R8 full mode:
android.enableR8.fullMode=false
If you want to enable full mode add the following rules to proguard-rules.pro:
# Keep generic signature of Call, Response (R8 full mode strips signatures from non-kept items).
-keep,allowobfuscation,allowshrinking interface retrofit2.Call
-keep,allowobfuscation,allowshrinking class retrofit2.Response
# With R8 full mode generic signatures are stripped for classes that are not
# kept. Suspend functions are wrapped in continuations where the type argument
# is used.
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
# Crypto
-keep class com.google.crypto.** { *; }
Usage
RookConfig
This object will help you to configure the user ID you want to sync.
const RookConfig = () => {
initRook: (credetials: InitRookProps) => Promise<boolean>;
updateUserId: (userId: UpdateUserIdProps) => Promise<boolean>;
getUserId: () => Promise<UserIdResult>;
clearUserId: () => Promise<boolean>;
syncUserTimeZone: () => Promise<boolean>;
enableAppleHealthSync: () => Promise<boolean>;
disableAppleHealthSync: () => Promise<boolean>;
isAppleHealthSyncEnable: () => Promise<boolean>;
revokeDataSource: (props: RevokeDataSourceProps) => Promise<BoolResult>;
}
initRook
: Configures and Initialize the rook sdk.getUserId
: Return the current user ID.updateUserID
: Change the current user ID.clearUserID
: Clear the current user ID.syncUserTimeZone
: Update the time zone of the user, you should only call this when is strictly necessary.enableAppleHealthSync
: This function enables the automatic upload of the summaries from the previous day of the current device's date. Every time the user opens the app it will try to upload the summaries, before use this function is necessary to add a user id and request permissions.disableAppleHealthSync
: This function disables the automatic upload of the summaries from the previous day of the current device's date.isAppleHealthSyncEnabled
: This function checks if the Apple Health Sync is enabled.revokeDataSource
: This fucntion revokes the data source authorization, using the user id stored locally.
Note: If you delete the user from the Rook services you have to create the user again with updateUserID
and request permissions.
RookPermissions
This object will help you to request permissions to extract data.
const RookPermissions: () => {
requestAllAppleHealthPermissions: () => Promise<boolean>;
requestAllHealthConnectPermissions: () => Promise<boolean>;
requestHealthConnectPermissions: () => Promise<RequestPermissionsStatusResult>;
requestAllPermissions: () => Promise<boolean>;
openIOSSettings: () => Promise<boolean>;
openHealthConnectSettings: () => Promise<boolean>;
healthConnectHasPermissions: () => Promise<boolean>;
requestAndroidBackgroundPermissions: () => Promise<boolean>;
requestAndroidPermissions: () => Promise<RequestPermissionsStatusResult>;
addListener: (eventName: EventNames, callback: (info: any) => void) => Promise<PluginListenerHandle>;
};
requestAllAppleHealthPermissions
: Request all the permissions for apple health only works for iOS.requestAllHealthConnectPermissions
: Request all the permissions for health connect. only works for android @deprecated.requestHealthConnectPermissions
: Request all the permissions for health connect only works for android.requestAllPermissions
: Request all the permissions. @deprecatedopenIOSSettings
: Open iOS system settings. only works for iOS.openHealthConnectSettings
: Open Health Connect settings. only works for android.healthConnectHasPermissions
: check if we have permissions for health connect this function only works for android.requestAndroidBackgroundPermissions
: request permissions for enable synchronization in background for android only works for android @deprecated.requestAndroidPermissions
: request permissions for enable synchronization in background for androidaddListener
: This feature allows you to add a listener to monitor and respond to all events emitted under a specific event name.
type EventNames = "io.tryrook.permissions.android" | "io.tryrook.permissions.healthConnect"
- 'io.tryrook.permissions.android': Event name for android permissions.
- 'io.tryrook.permissions.healthConnect': Event name for Health Connect permissions.
Note: Here is the corrected paragraph: Each method will return a promise indicating whether the permission window was successfully presented. This value does not indicate whether the user actually granted permission. Please keep in mind that Apple Health does not allow checking the status of permissions for types requested to be read. If the user does not allow reading certain data types, either by mistake or on purpose, it will simply appear as if there is no data of the requested type in the HealthKit store. Any further changes must be performed by the user through the Apple Health application.
Health Connect request quota
To maintain optimal system stability and performance, Health Connect imposes rate limits on client connections to the Health Connect API.
It is important to understand that every data type in the ROOK SDK is constructed of multiple health variables, such as heart rate, step count, hydration, etc. When a Sleep Summary is extracted, multiple calls are made to the Health Connect API. While we have focused on optimizing and reducing the number of API calls required for each data type, it is still possible to reach the limit, especially when performing multiple extractions in a short period.
Depending on the sync type you choose, keep the following in mind:
Request quota when syncing data manually
Here is the corrected paragraph:
- Extract summaries once daily: Since summaries collect health data from the previous day, there is no need to extract them more than once per day.
- Use what you already have: If you are extracting Physical Events, you do not need to extract Heart Rate Events (Physical) or Oxygenation Events (Physical) as these are already included in the PhysicalEvent object.
- Only sync the relevant health data for your use case: If you are not interested in individual events and only want to sync the summary of a specific date, use the
sync
functions inRookSummaries
. - If you have already reached the request quota, avoid calling any
sync
function for the next few hours to allow your quota to recover.
Request quota when syncing data automatically
scheduleYesterdaySync
already takes care of practically all issues and limitations of Health Connect. When the quota is reached, all pending syncs are canceled, and a recovery timestamp is created. Pending syncs will not resume until the user reopens the app after a few hours.
Continuous Upload for iOS
The hook useRookConfiguration
helps you to enable or disable continuous data upload. every time a user opens the app, the sdk will try to upload the data from the previous day of the device's current date.
Note: before enable this feature it is necessary to add a user a request permission from apple health
Method | Description |
---|---|
enableAppleHealthSync | This method enables the automatic upload of the summaries from the previous day of the current device's date. Every time the user opens the app it will try to upload the summaries, before use this method is necessary to add a user id and request permissions. |
disableAppleHealthSync | This method disables the automatic upload of the summaries from the previous day of the current device's date. |
Background Upload for iOS
RookAppleHealth
The RookAppleHealth
object helps enable background upload for summaries and events, allowing your app to upload health data while it is in the background. It is recommended to combine continuous upload, background upload, and manual sync for better performance.
const RookAppleHealth = () => {
enableBackGroundUpdates: () => Promise<BoolResult>;
disableBackGroundUpdates: () => Promise<BoolResult>;
isBackGroundUpdatesEnable: () => Promise<BoolResult>;
enableBackGroundEventsUpdates: () => Promise<BoolResult>;
disableBackGroundEventsUpdates: () => Promise<BoolResult>;
isBackGroundEventsUpdatesEnable: () => Promise<BoolResult>;
startListening: () => Promise<BoolResult>;
stopListening: () => Promise<BoolResult>;
addListener(eventName: AppleEventNames, callback: (info: any) => void): Promise<PluginListenerHandle>;
}
enableBackGroundUpdates
: Enables the background updates for summaries.disableBackGroundUpdates
: Disables the background updates for summaries.enableBackGroundEventsUpdates
: Enables the background updates for events.disableBackGroundEventsUpdates
: Disables the background updates for events.startListening
This function attachs a listener in the iOS plugin.stopListening
This function removes all the listeners attached in the iOS plugin.addListener
This feature allows you to add a listener to monitor and respond to all events emitted under a specific event name.
type AppleEventNames = "io.tryrook.background.appleHealth.errors"
- 'io.tryrook.permissions.android': Event name for apple health background errors.
Note: For security, iOS devices encrypt the HealthKit storage when users lock their devices. As a result, apps may not be able to read data from Apple Health when running in the background. Please refer to the official documentation for more information.
Method | Description |
---|---|
enableBackGroundUpdates | This method enables the background upload of the summaries. |
disableBackGroundUpdates | This method disables the background upload of the summaries. |
isBackGroundUpdatesEnable | This method returns the background upload status of the summaries. |
enableBackGroundEventsUpdates | This method enables the background upload of the events. |
disableBackGroundEventsUpdates | This method disables the background upload of the events. |
isBackGroundEventsUpdatesEnable | This method returns the background upload status of the events. |
To configure background upload you need to follow the steps bellow:
- Add health kit to your project and enable background delivery.
- Add Background modes and enable Background fetch.
- In the app delegate of your app add the
setBackListeners
method in didFinishLaunchingWithOptions function.
Example
import UIKit
import Capacitor
import RookSDK
import RookAppleHealth
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
RookBackGroundSync.shared.setBackListeners()
setupNotification()
handleEvents()
return true
}
...
Background Upload for Android
RookHealthConnect
The RookHealthConnect
object helps to automatically sync health data. Use the scheduleYesterdaySync
function during your app's initialization phase. We highly recommend checking and saving whether the user has explicitly given you permission to sync data automatically.
const RookHealthConnect = () => {
checkAvailability: () => Promise<CheckAvailabilityResult>;
scheduleYesterdaySync: (props: ConfigurationDoOnEndProps) => Promise<BoolResult>;
syncTodayAndroidStepsCount: () => Promise<StepsResult>;
enableBackgroundAndroidSteps: () => Promise<BoolResult>;
disableBackgroundAndroidSteps: () => Promise<BoolResult>;
isBackgroundAndroidStepsActive: () => Promise<BoolResult>;
}
checkAvailability
: Check if the Health Connect APK is installed in this device.scheduleAndroidYesterdaySync
: The functionscheduleAndroidYesterdaySync
is an asynchronous function that syncs data of the different summaries and events that Rook has. We have three options for sync:- oldest: After syncing yesterday data, subsequent syncs should start from the 29th day until the 2nd day (The day before yesterday).
- latest: After syncing yesterday data, subsequent syncs should start from the 2nd day (The day before yesterday) day until the 29th.
- nothing: Only sync yesterday data (Default).
syncTodayAndroidStepsCount
: to sync the steps count of the current day, if successful it will return the number of steps taken.
Background steps
enableBackgroundAndroidSteps
: To start tracking steps.disableBackgroundAndroidSteps
: To stop tracking stepsisBackgroundAndroidStepsActive
: check if the service is active
Track and upload steps from Android System in background.
This feature enables automatic extraction and upload of steps without needing to install Health Connect.
Customizing the foreground service notification
The steps manager uses a foreground Service which requires a notification to be permanently displayed.
The notification has the next default values:
Title: Steps service
Content: Tracking your steps…
To use your own resources you need to reference them in the AndroidManifest.xml file:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<meta-data
android:name="io.tryrook.service.notification.STEPS_ICON"
android:resource="@drawable/my_custom_icon"/>
<meta-data
android:name="io.tryrook.service.notification.STEPS_TITLE"
android:resource="@string/my_custom_title"/>
<meta-data
android:name="io.tryrook.service.notification.STEPS_CONTENT"
android:resource="@string/my_custom_content"/>
</application>
</manifest>
Starting on Android 13 (SDK 33) this notification can be dismissed without finishing the service associated with it, then the service will be displayed in the active apps section (This may vary depending on device brand).
This function is resource intensive, don't call it too frequently, as it could have a negative impact in your users experience.
Additional information
Auto start
After a call to enableBackgroundAndroidSteps
if the device is restarted the Foreground service will start after the user unlocks their device for the first time (This may vary depending on device brand). This behavior will be stopped when calling disableBackgroundAndroidSteps
.
Considerations
The steps service is designed to always be active but there are certain scenarios where the service could not behave as intended:
If the user force closes the application from settings, and then restarts their device the service may not be able to restart.
The steps are scheduled to be uploaded every hour from the time
enableBackgroundAndroidSteps
was called, however it's not possible to guarantee the exact execution time as this depends on how the Android System manages the device resources.
RookYesterdaySyncPermissions
scheduleYesterdaySync
Requires 2 types of permissions
- Android
- POST_NOTIFICATIONS
- FOREGROUND_SERVICE
- FOREGROUND_SERVICE_HEALTH
- ACTIVITY_RECOGNITION
- Health Connect
- SLEEP
- PHYSICAL
- BODY
To request or check both types of permissions, use request permissions for Health Connect with RookPermissions().requestAllHealthConnectPermissions()
to access Health Connect data and RookPermissions().requestAndroidBackgroundPermissions()
to access background services.
Customizing the foreground service notification
To sync health data automatically, a Foreground Service is used. This service requires a notification to be displayed until the synchronization finishes.
To use your own resources, you need to reference them in the AndroidManifest.xml file:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<meta-data
android:name="io.tryrook.service.notification.SYNC_ICON"
android:resource="@drawable/my_custom_icon"/>
<meta-data
android:name="io.tryrook.service.notification.SYNC_TITLE"
android:resource="@string/my_custom_title"/>
<meta-data
android:name="io.tryrook.service.notification.SYNC_CONTENT"
android:resource="@string/my_custom_content"/>
</application>
</manifest>
Starting with Android 13 (SDK 33), this notification can be dismissed without finishing the service associated with it. The service will then be displayed in the active apps section (this may vary depending on the device brand).
Launch/Stop conditions
scheduleYesterdaySync
won't start or will stop if any of the following conditions are met:
- The device battery is low.
- The device storage is low.
- The device is not connected to the internet.
- The user hasn't granted Android permissions (POST_NOTIFICATIONS, FOREGROUND_SERVICE, FOREGROUND_SERVICE_HEALTH).
- The device has previously exceeded the Health Connect request quota and the recovery timestamp hasn't been met.
- The user hasn't granted Health Connect permissions (SLEEP, PHYSICAL, BODY).
- The most recent request exceeded the Health Connect request quota.
- The user ID hasn't been configured.
- There is an error initializing the SDK.
RookSummaries
This object will help you to extract and send data from apple health to Rook servers.
const RookSummaries: () => {
shouldSyncFor: (props: ShouldSyncProps) => Promise<BoolResult>;
syncSleepSummary: (props: DateProps) => Promise<BoolResult>;
syncBodySummary: (props: DateProps) => Promise<BoolResult>;
syncPhysicalSummary: (props: DateProps) => Promise<BoolResult>;
reSyncFailedSummaries: () => Promise<BoolResult>;
syncSummaries: () => Promise<BoolResult>;
syncYesterdaySummaries: () => Promise<BoolResult>;
};
shouldSyncFor
: Checks if that specific date are data available. Only works for androidsyncYesterdaySummaries
: Send the sleep, physical and body summaries of the previous day of the current device date.syncSleepSummary
: Send the summary to rook servers.syncBodySummary
: Send the summary to rook servers.syncPhysicalSummary
: Send the summary to rook servers.reSyncFailedSummaries
: In case you try to sync a summary and fail, this function help to try to send again.
RookEvents
This object will help you to extract and send data from apple health to Rook servers.
const RookEvents: () => {
syncEvents: () => Promise<BoolResult>;
syncPhysicalEvents: (props: DateProps) => Promise<BoolResult>;
syncBloodGlucoseEvents: (props: DateProps) => Promise<BoolResult>;
syncBloodPressureEvents: (props: DateProps) => Promise<BoolResult>;
syncBodyMetricsEvents: (props: DateProps) => Promise<BoolResult>;
syncBodyHeartRateEvents: (props: DateProps) => Promise<BoolResult>;
syncPhysicalHeartRateEvents: (props: DateProps) => Promise<BoolResult>;
syncHydrationEvents: (props: DateProps) => Promise<BoolResult>;
syncNutritionEvents: (props: DateProps) => Promise<BoolResult>;
syncBodyOxygenationEvents: (props: DateProps) => Promise<BoolResult>;
syncPhysicalOxygenationEvents: (props: DateProps) => Promise<BoolResult>;
syncTemperatureEvents: (props: DateProps) => Promise<BoolResult>;
syncPendingEvents: () => Promise<BoolResult>;
getTodayStepCount: () => Promise<StepsResult>;
};
ready
: Indicates when the hook is ready to work.syncEvents
: Send all the event types of the previous day of the current date of the device and the events of the current device date.syncBodyHeartRateEvents
: Send body heart rate events for the specified date.syncPhysicalHeartRateEvents
: Send physical heart rate events for the specified date.syncBodyOxygenationEvents
: Send body oxygenation events for the specified date.syncPhysicalOxygenationEvents
: Send physical oxygenation events for the specified date.syncTrainingEvent
: Send training events for the specified date.syncTemperatureEvent
: Send temperature events for the specified date.syncBloodPressureEvent
: Send pressure events for the specified date.syncBloodGlucoseEvent
: Send glucose events for the specified date.syncHealthConnectHydrationEvents
: Send hydration events for the specified date.syncHealthConnectNutritionEvents
: Send nutrition events for the specified date.syncBodyMetricsEvent
: Send body metrics events for the specified date, like change of weight or height.getTodayStepCount
: to sync the steps count of the current day, if successful it will return the number of steps taken.
RookDataSource
This object will help you present a native view with a list of available data sources or obtain an array of objects with properties to create your custom view.
const RookDataSource: () => {
getAvailableDataSources: (props: DataSourceProps) => Promise<ResultDataSource>;
presentDataSourceView: (props: DataSourceProps) => Promise<BoolResult>;
}