RookSDK
If you’re building a health or fitness application of any kind, you likely understand the power of health user data. Integrating it with your application will benefit you regardless of your use case, whether it’s mindfulness and wellness, digital health, or remote care.
Apple Health does not have an API, and since no data is stored in the cloud, you need to retrieve it locally from your users' devices. This can be done by implementing Apple HealthKit and its data retrieval methods into your iOS app.
ROOK SDK allows developers to ask their users to access and share their health data through Apple HealthKit.
ROOK SDK for iOS enables fetching health data from Apple Health and synchronizing it with the ROOK server. It also allows registering a new user and storing this information locally.
The SDK provides access to Apple Health data, but only after the user's explicit consent. Users can select detailed data sharing settings, including which data types will be read.
Demo app
To help your implementation here is a demo app that shows you how to configure and use the sdk: https://github.com/RookeriesDevelopment/rook_demo_app_ios_rook_sdk
Features
The features listed bellow are available to fetch and synchronize:
- Sleep summaries
- Physical summaries
- Body summaries
- Heart rate events
- Oxygenation events
- Activity events
- Temperature Events
- Blood Glucose Events
- Blood Pressure Events
- Time zone of the device
- Variable extraction
- Background active extraction
Integrating the iOS framework
Installation
The SDK requires Xcode 14.0.1 or higher. To run your app using the Rook SDK on a connected device with iOS 13.0 or later you need Xcode 14.2 or higher.
To add a package dependency to your Xcode project, select File > Swift Packages > Add Package Dependency and enter the repository URL rook SDK
Rook SDK supports installation with CocoaPods
Here's how to install Rook using CocoaPods:
- Create a Podfile if you don't already have one. From the root of your project directory, run the following command:
pod init
- To your Podfile, add the Rook pod
pod "RookSDK"
- Install the pods, then open your .xcworkspace file to see the project in Xcode:
pod install
Configuration
To configure Rook SDK, you need to follow this steps:
- Import the apple health sdk
import RookSDK
- Add your credentials.
- This method should be called at the beginning of your app's launch process.
func application(_ application: UIApplication
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
RookConnectConfigurationManager.shared.setConfiguration(
clientUUID: "YOUR-CLIENT-UUID",
secretKey: "YOUR-SECRET-KEY",
enableBackgroundSync: true,
enableEventsBackgroundSync: true)
RookConnectConfigurationManager.shared.setEnvironment(.sandbox)
RookConnectConfigurationManager.shared.initRook()
return true
}
- Add the HealthKit framework to your Xcode project:
- 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.
- Then declare the privacy permissions used by this SDK. You will need to include the NSHealthShareUsageDescription and NSHealthUpdateUsageDescription keys in your app's Info.plist file. These keys provide a description of why your app needs to access HealthKit data and will be displayed to the user in the permission request dialog.
<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 health data to HealthKit.</string>
RookConnectConfigurationManager Use this class to configure and init the sdk. This class conforms the singleton pattern, to access this class use the shared property.
Method | Description |
---|---|
func setConfiguration(clientUUID: String, secretKey: String, enableBackgroundSync: Bool, enableEventsBackgroundSync: Bool) | Sets the configuration of the sdk. |
func setEnvironment(_ environment: RookEnvironment) | Configures the rook sdk environment. |
func initRook() | Initializes the rook sdk |
func updateUserId(_ id: String, completion: @escaping (Result<Bool, Error>) -> Void) | It will try to register the user in the rook server and it will be stored, if the registration was successful, after that the sdk upload the current time zone of the device. |
func getUserId(completion: @escaping (Result<String, Error>) -> Void) | Returns the user id stored locally. |
func clearUser(completion: @escaping (Result<Bool, Error>) -> Void) | Deletes the user stored locally. |
func removeUserFromRook(completion: @escaping (Result<Bool, Error>) -> Void) | Removes the authorization od the user to upload data from apple health and deletes the user id stored locally. |
func syncUserTimeZone(completion: @escaping (Result<Bool, Error>) -> Void) | Uploads the current time zone of the device a user has to added before use this method. |
func enableSync() | This method enables the automatic upload of the missing summaries from previous days. 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 all permissions. |
func disableSync() | This method disables the automatic upload of the summaries from the previous days. |
func isSyncEnable() -> Bool | Returns a boolean indicating if the automatic upload is enable. |
func setConsoleLogAvailable(_ value: Bool) | Enables or disables console logs for transmission responses when uploading events or summaries. |
The parameters enableBackgroundSync and enableEventsBackgroundSync enable background synchronization for summaries and events, respectively. To disable synchronization for either, set the corresponding parameter to false.
Get user authorization
Before you can retrieve any data, your app needs to be authorized to access Apple Health data by your users. To get authorization, use the authorize method, and the corresponding dialog will be shown on top of your app.
The SDK provides the RookConnectPermissionsManager class to request user permission. It contains the following methods:
Method | Description |
---|---|
+ requestAllPermissions(completion: @escaping (Result<Bool, Error>) -> Void) | Sends a request for all the health permissions and displays a view to grand access |
+ requestSleepPermissions(completion: @escaping (Result<Bool, Error>) -> Void) | Sends a request for the sleep data types permissions and displays a view to grand access. |
+ requestUserInfoPermissions(completion: @escaping (Result<Bool, Error>) -> Void) | Sends a request for the user information permissions. |
+ requestPhysicalPermissions(completion: @escaping (Result<Bool, Error>) -> Void) | Sends a request for the physical data types permissions and displays a view to grand access |
+ requestBodyPermissions(completion: @escaping (Result<Bool, Error>) -> Void) | Sends a request for the body data type permissions and displays a view to grand access. |
The callback of each method returns a boolean: true
if the permission window was successfully presented, or false
with an optional error if the window was not presented properly. 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 data type reading, 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.
User
Before synchronize summaries or events a user have to be added, otherwise the sdk will return an error.
The class UserManager
contains the below methods related to the user access.
Method | Description |
---|---|
func updateUserId(_ id: String, completion: @escaping (Result<Bool, Error>) -> Void) | It will try to register the user in the rook server and it will be stored, if the registration was successful, after that the sdk upload the current time zone of the device. |
func getUserId(completion: @escaping (Result<String, Error>) -> Void) | Return the user id stored locally. |
func clearUser(completion: @escaping (Result<Bool, Error>) -> Void) | Deletes the user stored locally. |
func removeUserFromRook(completion: @escaping (Result<Bool, Error>) -> Void) | Removes the authorization od the user to upload data from apple health and deletes the user id stored locally. |
public func syncUserTimeZone(completion: @escaping (Result<Bool, Error>) -> Void) | Uploads the current time zone of the device a user has to added before use this method. |
func revokeDataSource(dataSource: DataSourceRevoke, completion: @escaping (Result<Bool, Error>) -> Void) | Revoke the authorization of the data source given. |
Async await is supported.
Method | Description |
---|---|
public func updateUserId(_ id: String) async throws -> Bool | It will try to register the user in the rook server and it will be stored, if the registration was successful, after that the sdk upload the current time zone of the device. |
public func getUserId() async throws -> String | Return the user id stored locally. |
public func clearUser() async throws -> Bool | Deletes the user stored locally. |
public func removeUserFromRook() async throws -> Bool | Removes the authorization od the user to upload data from apple health and deletes the user id stored locally. |
public func syncUserTimeZone() async throws -> Bool | Uploads the current time zone of the device a user has to added before use this method. |
func revokeDataSource(dataSource: DataSourceRevoke) async throws -> Bool | Revoke the authorization of the data source given. |
Any call to upbdateUserId
with a different userId will override the previous userID and reset the sync status, if
you are using BackgroundSync
all health data will synchronize
again the next time the app is launched.
Continuous Upload
The class RookConnectConfigurationManager
contains two methods 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 |
---|---|
func enableSync() | This method enables the automatic upload, it will check the last time data was synced. If there is new data to update or pending data from previous days, it will upload this information., before use this method is necessary to add a user id and request permissions. |
func disableSync() | This method disables the automatic upload of the summaries. |
Background Upload
RookBackGroundSync
The class RookBackGroundSync
contains methods to enable background uploads for summaries, 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.
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 |
---|---|
func setBackListeners() | This method has to be added in app delegate to enable back ground upload. |
func enableBackGroundForSummaries() | This method enables the background upload of the summaries. |
func disableBackGroundForSummaries() | This method disables the background upload of the summaries. |
func isBackGroundForEventsEnable() -> Bool | This method returns the status of background upload for summaries. |
func enableBackGroundForEvents() | This method enables the background upload of the events. |
func disableBackGroundForEvents() | This method disables the background upload of the events. |
func isBackGroundForEventsEnable() -> Bool | This method returns the status of background upload for 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 RookSDK
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
RookBackGroundSync.shared.setBackListeners()
}
Background notifications error
When an error occurs in a back ground proccess, it can be listen by adding an observer, as shown in the example below:
import RookSDK
func onAppear() {
NotificationCenter.default.addObserver(
self,
selector: #selector(handleErrorBackground),
name: NSNotification.Name.init(EventNames.errorBackGround),
object: nil)
}
@objc private func handleErrorBackground(_ notification: Notification) {
if let data: [AnyHashable : Any] = notification.userInfo {
debugPrint(data)
}
}
Manual Sync Data
RookSummaryManager
This class contains the methods to synchronize summaries of the user
Method | Description |
---|---|
func syncSummaries(completion: @escaping () -> Void) | Uploads the sleep, physical and body summaries of previous days that has no been uploaded. |
func syncSleepSummary(form date: Date, completion: @escaping (Result<Bool, Error>) -> Void) | Synchronizes the sleep summary from the given day date. |
func syncPhysicalSummary(form date: Date, completion: @escaping (Result<Bool, Error>) -> Void) | Synchronizes the physical summary from the given day date. |
func syncBodySummary(from date: Date, completion: @escaping (Result<Bool, Error>) -> Void) | Synchronizes the body summary from the given day date. |
Async await implementation is also supported
Method | Description |
---|---|
public func syncSummaries() async | Uploads the sleep, physical and body summaries of previous days that has no been uploaded. |
public func syncSleepSummary(from date: Date) async throws -> Bool | Synchronizes the sleep summary from the given day date. |
public func syncPhysicalSummary(from date: Date) async throws -> Bool | Synchronizes the physical summary from the given day date. |
public func syncBodySummary(from date: Date) async throws -> Bool | Synchronizes the body summary from the given day date. |
RookEventsManager
Method | Description |
---|---|
func syncEvents(completion: @escaping () -> Void) | Uploads all the events of previous days that has no been uploaded. |
func syncBodyHeartRateEvent(date: Date, completion: @escaping (Result<Bool, Error>) -> Void) | Synchronized all the body heart rate events from the given day date. |
func syncPhysicalHeartRateEvent(date: Date, completion: @escaping (Result<Bool, Error>) -> Void) | Synchronized all the physical heart rate events from the given day date. |
func syncBodyOxygenationEvent(date: Date, completion: @escaping (Result<Bool, Error>) -> Void) | Synchronized all the body oxygenation events from the given day date. |
func syncPhysicalOxygenationEvent(date: Date, completion: @escaping (Result<Bool, Error>) -> Void) | Synchronized all the physical oxygenation events from the given day date. |
func syncTrainingEvent(date: Date, completion: @escaping (Result<Bool, Error>) -> Void) | Synchronized all the trainings events from the given day date. |
func syncTemperatureEvents(date: Date, completion: @escaping (Result<Bool, Error>) -> Void) | Synchronized all the temperature events from the given day date. |
func syncBloodPressureEvents(date: Date, completion: @escaping (Result<Bool, Error>) -> Void) | Synchronized all the blood pressure events from the given day date. |
func syncBloodGlucoseEvents(date: Date, completion: @escaping (Result<Bool, Error>) -> Void) | Synchronized all the blood glucose events from the given day date. |
getTodayStepCount(completion: @escaping (Result<Int, Error>) -> Void) | "Upload the current step count retrieved from Apple Health as a steps event, and return the current count or an error in the completion response. |
Async await implementation is also supported
Method | Description |
---|---|
public func syncEvents() async | Uploads all the events of previous days that has no been uploaded. |
public func syncBodyHeartRateEvent(date: Date) async throws -> Bool | Synchronized all the body heart rate events from the given day date. |
public func syncPhysicalHeartRateEvent(date: Date) async throws -> Bool | Synchronized all the physical heart rate events from the given day date. |
public func syncBodyOxygenationEvent(date: Date) async throws -> Bool | Synchronized all the body oxygenation events from the given day date. |
public func syncPhysicalOxygenationEvent(date: Date) async throws -> Bool | Synchronized all the physical oxygenation events from the given day date. |
public func syncTrainingEvent(date: Date) async throws -> Bool | Synchronized all the trainings events from the given day date. |
public func syncTemperatureEvents(date: Date) async throws -> Bool | Synchronized all the temperature events from the given day date. |
public func syncBloodPressureEvents(date: Date) async throws -> Bool | Synchronized all the blood pressure events from the given day date. |
public func syncBloodGlucoseEvents(date: Date) async throws -> Bool | Synchronized all the blood glucose events from the given day date. |
public func syncBodyMetricsEvents(date: Date) async throws -> Bool | Synchronized all the body metrics events from the given day date. |
public func getTodayStepCount() async throws -> Int | "Upload the current step count retrieved from Apple Health as a steps event, returns the current count or throws an error. |
We highly advise against retrieving more than 14 days of EPOCH data and 30 days of Daily data for more than one data type. Due to the expected volume of data, the retrieval process will require extensive resources and may affect the user experience.
Data Sources
ROOK SDK includes the class DataSourcesManager
, which allows the presentation of a connection page view where users can directly connect other data sources with ROOK. If you prefer to implement your own view, this class also contains a method to retrieve the available data sources for connection.
Method | Description |
---|---|
func getAvailableDataSources(redirectURL: String?, completion: @escaping (Result<[RookDataSource], Error>) -> Void) | Retrieves and array of object representing the available data sources. |
func presentDataSourceView(redirectURL: String?, completion: @escaping (Result<Bool, Error>) -> Void) | Presents a view that contains the available data sources. |