shell (cURL) Android (Java) Kotlin iOS (Swift) Python JavaScript (jQuery) Dart

SDK Integration

IMPORTANT NOTE

SDK support & versioning

At Thryve, we have committed ourselves to provide a more robust, efficient and easy to integrate system, as well as coming to a point where we can provide quicker and longer support on our releases.

From SDK Version 4.2.0 onwards, when released, all modules in each of the SDK's ( Android and iOS ) will be published and updated using a Unified Versioning Scheme based on semantic versioning.

But, What does that mean for our integration?

In the short term, it means easily understanding the version of the SDK you're using, it's features, any new changes, any known issues, and ensuring module inter-compatibility.

When should we upgrade to a new SDK Version?

Ideally, we would love to be able to provide you with our most recent integrations and improvements, but we understand that changes in a running environment take time and therefore we want to be able to help you adapt to those > changes. Stay tuned to know more about the process we're building to ensure you can get the most out of our system.

The SDK allows your users to connect 3rd party health data sources like Apple Health, Fitbit or Withings with your app through Thryve, as well as native data retrieval and data collection through specific SDK modules.

Integrating the Thryve SDK within your Android and iOS apps (→ see sections for iOS or Android respectively) is a plug & play process and will allow you to establish a data source connection with only two requests:

  1. Generate a Thryve token (and store it with your user)
  2. Bring up the data sources connection for this user

If a successful connection is established, your users’ data will start to flow to Thryves data warehouse, where you can retrieve it through our wearable API at any time.

This standard process can be flexibly adapted and expanded to match exactly your needs – from individual styling (→ see custom data source connection screen) to the integration of on-device data retrieval of native data sources (→ see HealthKit, GoogleFit Native and SHealth modules) and on-device health data generation (→ see GoogleFit Native).

Get access to Thryve's SDK and integrate 500+ wearables with your appGet started

SDKs and Modules

Thryve’s SDKs for Android and iOS app consist of one CoreSDK and several optional modules, you only need to integrate the functionalities you want to use. SDK modules are separated libraries that can coexist in your application environment.

Thryve Core SDK

The CoreSDK brings everything you need to allow your users to connect all OAuth and Direct Access data sources with your app via Thryve. The CoreSDK contains all necessary routines to create Thryve access tokens, to display Thryve’s or your own data source connection page and to allow your users to establish a data source connection.

Thryve Commons module

The Thryve Commons module is required to use any of the additional SDK modules. This module provides database and server synchronization logic needed for the additional modules.

Native data source modules

The SDK modules for Apple Health, Google Fit (native) and Samsung Health allow your users to connect and share data from native data sources with you. The corresponding SDK modules allow to establish a data source connection with native sources and to retrieve data.

BGM module (experimental)

The BGM module allows for data access of selected connected blood glucose monitors from i-Sense, B.Braun and Roche via Bluetooth. The module allows your users to find, connect and sync data of these devices within your app.

iOS integration

Experimental modules: BGMModule

In a quest to provide the most comprehensive access to connected devices, we are continuously exploring new ways of device access. To get new devices into your hands as fast as possible, we release new features on an ongoing basis. Please note, that as of now, the BGMModule is in testing and enhancement phase and only released as a "experimental" version - please let us know in case of any peculiarities!

The iOS SDKs and modules for native apps are designed as iOS frameworks. As previously described, the CoreSDK provides all necessary methods to allow your users to establish a data sources connection with OAuth and Direct Access data sources. The different modules are extensions to the SDK’s functionality with additional data retrieval routines. It is required to instantiate the CoreConnector when using any of the additional modules. The integration of the Apple Health Module is required to access data through Apple HealthKit. The CoreSDK and all modules have separate constructors to set up the corresponding object instance needed to call the desired method.

Integrating the iOS framework

To integrate the Thryve SDK with your iOS app, please download the latest SDK and required modules and follow these steps:

Add framework file

Right-click your Xcode project's root and add a New Group. This is where the Framework will be added. When the group is created, drag and drop the dedicated framework file from Finder into the newly created group. A dialog will open, please select Copy items if needed, Create folder references and select the targets you want to add the Framework onto.

Following the import, the framework will appear in the left navigation hierarchy of the project.

When the framework is added to the project, link it with the target. Select the project root > target > General:

Next, add the framework in the Embedded Binaries and Linked Frameworks and Libraries sections by clicking on the +-sign. Select the thryve_CoreSDK.framework in the appearing dialog.

If you have followed the import correctly, the screen should look like this:

Enable BitCode

Select the project root > target > Build Settings. Search for Enable Bitcode and select Yes.

Import framework

Now you should be able to import the dedicated reference in all classes.

Constructor

To use the library’s functionality, create a CoreConnector-object with the below described constructor. There are two different types of constructors. Depending on the usage of the partnerUserId-String, the getAccessToken()-method will create different results.

import CoreSDK

let connector = CoreConnector(
  appId: "FEh9HQNaa87cwdbB",
  appSecret: "NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM",
  partnerUserId: "FVMW6fp9wnUxKnfekrQduZ96Xt6gemVk",
  language: "en"
)
Code Description
CoreConnnector(appId: String, appSecret: String) Creates the CoreConnector-object when using the following correct parameters.
Parameters:

appId: The application code you received upon signing the cooperation as a String
appSecret: Your application secret you received upon signing the cooperation as a String

CoreConnnector(appId: String, appSecret: String, partnerUserId: String) Creates the CoreConnector-object in the same way as the above constructor, but with a partnerUserId.
Additional Parameter:

partnerUserID: An optional customerID, which allows Thryve to persistently relate a token to a user. If provided, Thryve will return the last valid accessToken previously created for this user instead of generating a new one e.g. FVMW6fp9wnUxKnfekrQduZ96Xt6gemVk

Note: partnerUserID is only allowed to consist of alphanumeric characters.

CoreConnnector(appId: String, appSecret: String, language: String) Creates the CoreConnector-object in the same way as the above constructor, but with a user related language.
Additional Parameter:

language: Optionally determines the language setting for the corresponding user. Will be set as a string of the country code, e.g.: en. We currently support five languages: English, German, Spanish, French and Italian. If not set, the default language partner will be used.

Generate a Thryve user token (partnerUserID)

All data stored of any given user is linked to a pseudonymous Thryve user token (partnerUserID). This token will be generated on backend side when calling the getAccessToken method for your user.

An accessToken is required for each user to connect data sources, upload data or retrieve data. You will get the accessToken when calling the method getAccessToken for your user. This method will return the unique accessToken.

If you want to define the Thryve user token (partnerUserID), you need to construct the connector with partnerUserID and call the getAccessToken method with the given partnerUserID. If an accessToken is returned, you defined your user's partnerUserID successfully.

connector.getAccessToken(completionHandler: { (token, error) in
  //do code
})
Code Description
getAccessToken(completionHandler: @escaping (_ accessToken: String?, _ error: Error?) -> Void) Retrieves a Thryve access token, which can be specified to correspond to a specific user (Token to access the individual health information of this user).

This method runs asynchronously and, when finished, returns the result or error through the completion handler. Please check the code section on the right for an example response.

Name Type Description
completionHandler @escaping (_ accessToken: String?, _ error: Error?) -> Void Callback for when the operation finishes with access token or error.

Display data source connection screen

Having received the accessToken for your user, the following method allows your user to connect their 3rd party devices via the data source connection screen. To give you a head-start we provide a ready-to-use WebView for the data source connection screen that you can display in your app (see below) – providing a seamless experience for your users.

The Thryve data sources selection screen uses the web view of the OS. To implement the pre-built data source connection screen, create the child of CoreWebViewController class and CoreWebViewDelegate class. To display the list of available data sources, please use loadDataSourceConnection. An example implementation is displayed on the right.

class ViewController: CoreWebViewController, CoreWebViewDelegate {

    var connector: CoreConnector?

    var connection: Bool = true

    var instantRevoke: Bool = false

    var source: Tracker?

    override func viewDidLoad() {
        super.viewDidLoad()
        coreWebViewDelegate = self
        loadDataSourceConnection()
    }

    func webView(_ webView: WKWebView, source: Tracker?, connected: Bool, error: Error?) {}

}
Code Description
connector: CoreConnector? A CoreWebViewDelegate property. A CoreConnector instance.
loadDataSourceConnection() Generates and shows the data source URL inside the web view for the user related to the last retrieved accessToken.
• hint: call this method directly inside the viewDidLoad()

iOS extensions

The following chapter describes additional functionalities of the CoreSDK and modules. You can download the SDK framework files and the sample project in the above chapter.

CoreSDK: Custom data source connection screen (direct connection)

If you want your users to connect their data source through your own data source connection screen or other GUI, you can use the SDKs direct connection functionality.

You can directly connect or revoke a tracker using the protocol CoreWebViewDelegate, without the need to implement the WKNavigationDelegate logic on your own,. As the user provides data within a web view, the corresponding response for connecting or revoking a tracker will happen asynchronously.

To implement a custom data source connection screen, the same method as mentioned in the paragraph “Display data source connection screen” needs to be implemented. The difference to the previously described method is that the loadDirectConnection is now used for direct connection to or direct revoke from a specified source. An example implementation is displayed on the right side of the documentation.

Use the data source ID to display the authorization-page of the corresponding data source, e.g., use ID 1 when connecting Fitbit. For the correct data source IDs, please refer to the data sources overview.

class ViewController: CoreWebViewController, CoreWebViewDelegate {

    var connector: CoreConnector?

    var connection: Bool = true

    var instantRevoke: Bool = false

    var source: Tracker?

    override func viewDidLoad() {
        super.viewDidLoad()
        coreWebViewDelegate = self
        loadDirectConnection()
    }

    func webView(_ webView: WKWebView, source: Tracker?, connected: Bool, error: Error?) {
        print(source?.id ?? 0)
        print(connected)
        print(error ?? "")
    }

}
Code Description
connection: Bool (For direct connection) A CoreWebViewDelegate property. Set (true) for direct connection or (false) for direct revoke.
instantRevoke: Bool (For direct connection) A CoreWebViewDelegate property. Set (true) for quick instant revoke skipping the revoke page or (false) for displaying the revoke page.
source: Tracker? (For direct connection) A CoreWebViewDelegate property. A direct connection/revoke source
loadDirectConnection() (For direct connection) Performs the direct connection/revoke to the selected delegate property source inside the web view for the user related to the last retrieved accessToken (valid for 1h).
• hint: call this method directly inside the viewDidLoad()
webView(_ webView: WKWebView, source: Tracker?, connected: Bool, error: Error?) (For direct connection) A CoreWebViewDelegate callback function showing the status of directly connected/revoked data source.
webView: the CoreConnectorWebView object implementing WKNavigationDelegate for navigation handling.
source: (optional) selected source which was initially set in source
connected: the connection status of the source
error: error notifiying if the connection was performed with troubles. Please ensure that the delegate property source was set and check the error first in order to implement your own logic depending on conecction status connected boolean value.

If you want to build your own WKWebView logic, the SDK allows to build the URLs for direct connection and revoke of each data source with the CoreConnector methods.

class WebViewController: UIViewController, WKNavigationDelegate {
    @IBOutlet weak var webView: WKWebView!

    let connector = AppDelegate.connector

    override func viewDidLoad() {
        super.viewDidLoad()
        webView.navigationDelegate = self
        connector.directConnection(to: Tracker(.fitbit)) { (url, error) in
            if let url = url {
                DispatchQueue.main.async { [self] in
                    webView.load(URLRequest(url: url))
                }
            }
        }
    }

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        if let url = webView.url {
            connector.connected(url: url) { (connected, error) in
                print(connected)
                print(error)
            }
        }
    }
}

When calling directConnection and no error occurs, the direct connection URL is built. Use this URL to build an URLRequest object and load it asynchronously in the WebView instance on the main queue. An example implementation is displayed on the right side of the documentation.

class WebViewController: UIViewController, WKNavigationDelegate {
    @IBOutlet weak var webView: WKWebView!

    let connector = AppDelegate.connector

    override func viewDidLoad() {
        super.viewDidLoad()
        webView.navigationDelegate = self
        connector.directRevoke(from: Tracker(.fitbit), instant: true) { (url, error) in
            if let url = url {
                DispatchQueue.main.async { [self] in
                    webView.load(URLRequest(url: url))
                }
            }
        }
    }

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        if let url = webView.url {
            connector.connected(url: url) { (connected, error) in
                print(connected)
                print(error)
            }
        }
    }
}

When calling directRevoke and no errors occurs, the direct revoke URL is built. Use this URL to build an URLRequest object and load it asynchronously in a custom WebView instance on the main dispatch queue. An example implementation is shown on the right side of the documentation.

Code Description
directConnection(to tracker: Tracker, completionHandler: @escaping (URL?, Error?) -> Void) Awaits data source as a parameter and provides a completion handler with direct connection URL or Error
directRevoke(from tracker: Tracker, instant: Bool = false, completionHandler: @escaping (URL?, Error?) -> Void) Awaits data source as a parameter and provides a completion handler with direct revoke URL or Error
connected(url: URL, completionHandler: (Bool, Error?) -> Void) A result callback will report successful connection or successful revoke based on the boolean value. The result callback can also return Error as well in case the revoke or connection is unsuccessful

CoreSDK: PartnerDataUpload

The PartnerDataUpload methods allows to upload custom data from the app to the Thryve data warehouse which is not data recorded by any data source. This might be a users demographic data like birthdate and gender or certain indications needed for health status calculation or other analytics of Thryve’s interpretation layer.

Data can be uploaded as DailyDynamicValue, or, for slowly or never changing values as ConstantValues. You can access the data uploaded through the SDK like any other data via our API.

Please refer to the sections Accessing the API and the Interpretation Layer for more information.

Health status/Interpretation layer data types

The following values are required for health status and interpretation layer analytics and need to be uploaded via the PartnerDataUpload:

DailyDynamicValue

let day = Date()
let payload = CustomerValue.CustomerPayload(value: "10", of: DataType(1000, .long), on: day)
let data = CustomerValue(data: [payload])

connector.uploadDailyDynamicValue(data: data) {
  (success, e) in
  debugPrint(e)
}
Code Description
uploadDailyDynamicValue(data: CustomerValue, completionHandler: @escaping (Bool, Error?) -> ()) Uploads DailyDynamicValue for given day of date and type. Value is passed as String. The completion handler reports about the status of the successfully implemented interface or will return an error otherwise.
Name Type Description
completionHandler @escaping (Bool, Error?) -> () Callback returns response status and error

ConstantValue

let payload = CustomerValue.CustomerPayload(value: "5", of: DataType(9050, .long))
let data = CustomerValue(data: [payload])

connector.uploadConstantValue(data: data) {
  (succes, e) in debugPrint(e)
}
Code Description
uploadConstantValue(data: CustomerValue, completionHandler: @escaping (Bool, Error?) -> ()) Uploads ConstantValue for given type. Value is passed as String. The completion handler reports about the status of the successfully implemented interface or will return an error otherwise.
Name Type Description
completionHandler @escaping (Bool, Error?) -> () Callback returns response status and error otherwise

UserInformation

let user = User(height: 173, weight: 174.2, birthdate: "1990-01-18", gender: Gender.male)
connector.uploadUserInformation((data: user) {
  (succes, e) in debugPrint(e)
}
Code Description
uploadUserInformation(data: User, completionHandler: @escaping (Bool, Error?) -> ()) Uploads UserInformation for the given User object. The completion handler reports about the status of the successfully implemented interface or will return an error otherwise.
Name Type Description
completionHandler @escaping (Bool, Error?) -> () Callback returns response status and error

CoreSDK: Get UserInformation

You can retrieve user information directly with the CoreSDK, without the need to call the Wearable API. The implementation makes use of an UserInformation-object that will be wrapped inside a list. Please refer to the right code section for an example.

self.viewModel.connector.getUserInformation(onSuccess: { (userInfoArray, error) in
  // Data inside 'userInfoArray'
})
Code Description
getUserInformation(completionHandler: @escaping ([UserInformation], Error?) -> ()) Retrieves an array of UserInformation in onSuccess.
Name Type Description
completionHandler @escaping ([UserInformation], Error?) -> () A callback containing array of UserInformation or error. UserInformation( authenticationToken: String, partnerUserID: String?, height: Int?, weight: Double?, birthdate: String?, gender: String?, connectedSources: [ConnectedSource])

authenticationToken: Thryve access token (String)
partnerUserID: PartnerUserID (String)
height: Height in cm (Integer)
weight: Weight in kg (Double)
birthdate: Birth date String in YYYY-MM-dd format
gender: Gender from Gender, i.e. male, female, genderless
connectedSources: Array of ConnectedSource. ConnectedSource(dataSource: Int, connectedAt: String), whereas connectedAt is a String timestamp of the connection time and dataSource is the Thryve ID for the connected source.

AppleHealth: Constructor

Before calling any method of the Apple Health module, please create the corresponding constructor.

import ModuleAppleHealth

let hKconnector = HKConnector()
Code Description
HKConnector() Creates the HKConnector-object

AppleHealth: Integration

To integrate Apple’s HealthKit with your iOS app, you need to integrate the AppleHealth module of Thryve’s SDK. This will allow your users to connect with Apple Health and to automatically trigger a background routine to retrieve the latest health data.

<key>NSHealthShareUsageDescription</key>
<string>This app needs permission to share your health data</string>
<key>NSHealthUpdateUsageDescription</key>
<string>This app needs permission to update your health data</string>

To enable Apple Health data retrieval, the corresponding routines, authorizeHealthKit and startHealthKitIntegration, must be called at least once successfully. When your users authorized your app to access Apple Health data, the module will start the background fetch mechanism through HealthKit from that moment onward until the stopHealthKitIntegration method is called.

You can specify the data types you would like access via the setHealthKit-Method. To enable background fetch (retrieving data when your app is closed), please refer to the sample code to the right side of this documentation. To perform the background fetch successfully, you need to additionally apply the integration inside the didFinishLaunchingWithOptions method of AppDelegate.

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Use any types you require or HKConnectorType.allTypes
        let types = [
            HKConnectorType.heartRate,
            HKConnectorType.stepCount
        ]
        //When apple's Health app decides to, it will wake your application through didFinishLaunchingWithOptions.
        //Once awoken, HealthKit waits a few seconds to see if said app wants to renew it's observing query contracts.
        //calling this method as early as possible renews those query contracts.
        HKConnector().enableBackgroundDeliveryFor(types: types)
        // Override point for customization after application launch.
        return true
    }
Code Description
authorizeHealthKit(types: Set HKObjectType, completionHandler: @escaping (Set HKObjectType , Bool, Error?) -> ()) -> ()) This method launches the Apple Health app permission system with the given requested types. If those are granted, a secure keystore is initiated with some default values, awaiting for startHealthKitIntegration or enableBackgroundDeliveryFor to be called.
startHealthKitIntegration(types: Set HKObjectType, completionHandler: @escaping (Bool, Error?) -> ()) Starts HealthKit integration and health data exchanging. Inits observing queries for fetching data with set data types. Call this function in the "completionHandler" of authorizeHealthKit function in order to pass authorized types to read as a "types" parameter. Will set to "true" isEnabled for service.
enableBackgroundDeliveryFor(types: Set HKObjectType) Registers the SDK and calling app to receive background updates from HealthKit. This is called automatically after Start(), and must be called every AppDelegate didFinishLaunchingWithOptions. The frequency of the updates varies per user device's configuration, caller app usage patterns, and a set of minimum gaps. The average is between 1h and 5h per session data types.
stopHealthKitIntegration(completionHandler: @escaping (Bool, Error?) -> ()) Stops HealthKit integration, can be called anytime. Will set to "false" isEnabled for service and will notify the Backend.
isEnabled(for service: Service) -> Bool Use with .healthKit. Checks, if HealthKit-integration is enabled.
synchronizeEpoch( types: Set<HKObjectType>, completionHandler: @escaping (Bool, Error?) -> Void) Performs Epoch synchronization from last upload date to today.
Call this function in order to synchronise the epoch data immediately.
synchronizeEpoch( startDate: Date, endDate: Date, types: Set<HKObjectType>, completionHandler: @escaping (Bool, Error?) -> Void) Performs Epoch synchronization between given dates, with a maximum of 30 days.
- If difference is longer than 30 days, end date - 30 days is used as start date.
- Call this function in order to synchronise the epoch data immediately.
- Parameter completionHandler: completion handler reporting of synchronize status.
synchronizeDaily( types: Set<HKObjectType>, completionHandler: @escaping (Bool, Error?) -> Void) Performs Daily synchronization from last upload date to today.
Call this function in order to synchronise the epoch data immediately.
- Parameter completionHandler: completion handler reporting of synchronize status.
synchronizeDaily( startDate: Date, endDate: Date, types: Set<HKObjectType>, completionHandler: @escaping (Bool, Error?) -> Void) Performs Daily synchronization between given dates, with a maximum of 30 days.
- If difference is longer than 365 days, end date - 365 days is used as start date.
- Call this function in order to synchronise the epoch data immediately.
- Parameter completionHandler: completion handler reporting of synchronize status.
Name Type Description
types Set <HKObjectType> Set specified data types to be handled in HealthKit.
completionHandler @escaping (Set <HKObjectType> , Bool, Error?) -> () Completion handler will be called after presenting the Apple Health permission window in order to allow reading access for the selected types. The callback will return the status, if the permission window was successfully presented, and an optional error if the window was not presented properly to the requested HKObjectType set. Please note that AppleHealth does not allow to check the status of the permissions for types that have been requested to be read. If the user does not allow data types reading intentionally or by mistake, the user should manually set up permission inside the AppleHealth application, otherwise the integration will not proceed.
completionHandler @escaping (Bool, Error?) -> () Completion handler notifies about the status for starting/stopping integration for every observing query.
service Service Service to check if enabled.
startDate Date The date which will be used as the starting point of a synchronization call.
endDate Date The date which will be used as the end point of a synchronization call.

AppleHealth: Synchronize Methods Error Codes

Code Name Description
70010 HK_SYNC_START_AFTER_NOW the start date set in synchronizeEpoch or synchronizeDaily is set as a date that is in the future
70011 HK_SYNC_END_AFTER_NOW the end date set in synchronizeEpoch or synchronizeDaily is set as a date that is in the future
70012 HK_SYNC_END_BEFORE_START the end date set in synchronizeEpoch or synchronizeDaily is set before the start date

BGMModule (experimental): Constructor

The BGM module allows to connect connected blood glucose meters from i-Sens, B.Braun and Roche with your app via bluetooth.

The module uses the swift classes CBCharacteristic and CBPeripheral that are wrapped inside the BLEDelegate-protocol that your ViewModel must extent. To use the module, you must handover the ViewModel itself to the BGMModule constructor as well.

class BGMViewModel: ObservableObject {
  private var connector: BGMConnector?
  var source: BGMDevice

  init(_ source: BGMDevice) {
      self.source = source
  }

  func setConnector() {
      if connector == nil {
          connector = BGMConnector(delegate: self, source: source)
      }
  }

  func startScanning() {
      connector?.startScanning()
  }

  func stopScanning() {
      connector?.stopScanning()
  }

  func connect(device: CBPeripheral) {
      connector?.connect(peripheral: device)
  }

  func sync() {
      if racp != nil {
          connector?.sync(racp: racp!)
      }
  }

  func disconnect() {
      connector?.disconnect()
  }

  func delete(source: BGMDevice) {
      connector?.delete(source: source)
  }
}
Code Description
BGMConnector(delegate: ModuleBGM.BLEDelegate, source: ModuleBGM.BGMDevice) Creates the BGMConnector-object. Should be called from within a ViewModel. Parameters:

delegate: The responsible View object to implement BLEProtocol connection callbacks
device: The BGM device mark which will transport the blood glucose data (iSens, bBraun, or rocheAccuChek).

Your ViewModel must implement the protocol BLEDelegate. Please refer to the sample Swift code on the right side.

BGMModule (experimental): Scanning

To initially find your BGM device via Bluetooth, you need to start a scanning process that searches for nearby Bluetooth devices. You can perform this with the BGMConnector via the following two methods:

Code Description
startScanning() Starts the Bluetooth scanning process. This process runs indefinitely.
stopScanning() Stops the Bluetooth scanning process. Does nothing, if it is not running.

The scanning process will not stop, until it is stopped manually. Please ensure to implement proper Threads, if you want to setup a timeout for device search.

BGMModule (experimental): Connection

When the scan finds one or more suitable devices, the BLEDelegate-protocol method ble(didFound peripheral: CBPeripheral, rssi: Int) will be called, handing over the CBPeripheral object. This device can either be stored temporarily for later connection or a connection to pair the device can directly be established.

Code Description
connect(peripheral: CBPeripheral) Connects to the given BluetoothDevice. Stores pairing information in the background. If a connection is active and paired the ble(ready racpCharacteristic: CBCharacteristic)-method from BLEDelegate protocol will be called and data can be synchronized.
disconnect() Disconnects the Bluetooth device. The pairing information stays valid.
delete(source: ModuleBGM.BGMDevice) Deletes the Bluetooth device by removing the pairing information as well.

BGMModule (experimental): Syncing

The synchronization process should be executed after the device has been successfully connected and the CBCharacteristic has been handed over via the BLEDelegate protocol ble(ready racpCharacteristic: CBCharacteristic) method. The sync-method will perform the synchronization of recent BloodGlucose data in the background (up to one week, if the data has not been synchronized before) and will perform a direct upload to the Thryve data warehouse. When the upload succeeds, the disconnect of the BGMConnector will be executed automatically. The method is structured as followed:

Code Description
sync(racp: CBCharacteristic) Performs a background synchronization of the BloodGlucose data. A debug printout will be performed, if the synchronization was successful.

Android integration

IMPORTANT NOTE

Android SDK 4.9.3 and later was built using Kotlin 1.6.0. For more information see kotlinlang.org/docs/compatibility-modes. Also, please ensure that the compileSdkVersion must be at least 31.

Experimental module: BGMModule

Please note the all "experimental modules", i.e. BGMModule, are implementations that are currently not maintained by Thryve anymore and have not been tested with various test devices or a big user base. Please keep this in mind when considering to use those modules in a production environment.

The Android SDKs and modules are standard .aar-libraries. As previously described, the CoreSDK provides all necessary methods to allow your users to establish a data sources connection with OAuth and Direct Access data sources. The different SDK modules are extensions to the SDK’s functionality with additional data retrieval routines. It is required to instantiate the CoreConnector when using any of the additional modules. The CoreSDK and all modules have separate constructors to set up the corresponding object instance needed to call the desired method.

Integrating the .arr libraries

module build.gradle

...
  ...
  android {
    ...
    compileOptions {
      sourceCompatibility 1.8
      targetCompatibility 1.8
    }
    kotlinOptions {
        jvmTarget = 1.8
    }
    // Only needed, if CommonsModule is added
    packagingOptions {
        pickFirst 'lib/x86_64/libsqlcipher.so'
        pickFirst 'lib/armeabi-v7a/libsqlcipher.so'
        pickFirst 'lib/x86/libsqlcipher.so'
        pickFirst 'lib/armeabi/libsqlcipher.so'
        pickFirst 'lib/arm64-v8a/libsqlcipher.so'
    }
  }
  ...
  dependencies {
    ...
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'com.google.android.gms:play-services-location:17.0.0'

    // Referenced in project's build.gradle
    implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version'

    implementation files('libs/thryve_core_sdk_4.9.6.aar')

    // CommonsModule import needed for other modules
    implementation files('libs/thryve_module_commons_4.9.6.aar')
    // Optional modules
    implementation files('libs/thryve_module_shealth_4.9.6.aar')
    implementation files('libs/thryve_module_gfit_4.9.6.aar')
    ...
    // dependencies for network and others
    implementation "com.google.code.gson:gson:$gson"
    implementation "androidx.legacy:legacy-support-v4:$legacySupport"

    implementation "io.reactivex.rxjava2:rxjava:$rxJava2"
    implementation "io.reactivex.rxjava2:rxandroid:$rxAndroid2"
    implementation "io.reactivex.rxjava2:rxkotlin:$rxKotlin"

    // dependencies for background sync (Google Fit, Samsung Health)
    implementation "androidx.work:work-runtime:$work"
    implementation "androidx.work:work-runtime-ktx:$work"
    implementation "androidx.work:work-gcm:$work"

    implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle"
    implementation "androidx.lifecycle:lifecycle-process:$lifecycle"

    implementation "androidx.concurrent:concurrent-futures:$concurrentFutures"
    implementation "androidx.concurrent:concurrent-futures-ktx:$concurrentFutures"

    // dependencies for Google Fit
    implementation "com.google.android.gms:play-services-auth:${playServicesAuth}"
    implementation "com.google.android.gms:play-services-fitness:${playServicesFitness}"

    // dependencies for internal database systems
    implementation "androidx.room:room-runtime:$room"
    implementation "androidx.room:room-ktx:$room"
    implementation "androidx.annotation:annotation:$annotation"

    // dependencies for encryption/auth mechanisms
    implementation "net.zetetic:android-database-sqlcipher:$sqlCipher"
    implementation "androidx.security:security-crypto:$crypto"
  }
buildscript {
    ext.androidXCore = "1.7.0"
    ext.androidxTest = "1.4.0"
    ext.androidXJunit = "1.1.3"
    ext.appCompat = "1.4.1"
    ext.annotation ="1.3.0"
    ext.constraintLayout = "2.1.2"
    ext.crypto = "1.1.0-alpha03"
    ext.concurrentFutures = "1.1.0"
    ext.dokka = "1.4.32"
    ext.espressoCore = "3.4.0"
    ext.jacocoPlugin = "0.16.0"
    ext.junit = "4.13.2"
    ext.kotlinPlugin = "1.6.10"
    ext.legacySupport = "1.0.0"
    ext.lifecycle = "2.4.0"
    ext.mockito = "4.2.0"
    ext.powerMock = "2.0.2"
    ext.playServicesLocation = "19.0.1"
    ext.playServicesWearable = "17.1.0"
    ext.playServicesFitness = "21.0.1"
    ext.playServicesAuth = "19.2.0"
    ext.roboelectric = "4.7.3"
    ext.room = "2.4.0"
    ext.rxAndroid2 = "2.1.1"
    ext.rxJava2 = "2.2.10"
    ext.rxKotlin = "2.4.0"
    ext.sentrySdk = "5.5.2"
    ext.sqlCipher = "4.5.0"
    ext.work = "2.7.1"
    ext.gson = "2.8.9"
    ...
}

Add library file

The versions as of May 2022 which can be put to the project or app build.gradle file:

  1. Add the dedicated aar-file to a folder of your project (e.g. libs)
  2. Add implementation files('libs/.aar') to your build.gradle file
  3. Add Kotlin support with annotation processing
  4. Add support for Android AppCompat-Library
  5. Add target and source compatibility for Java 1.8
  6. Add Jvm target for kotlin to version 1.8
  7. For further information please refer to the example build.gradle-script on the right. If CommonsModule is added, add packagingOptions accordingly

Create the constructor

To use the library’s functionality, create a CoreConnector-object with the below described constructor. There are two different types of constructors. Depending on the usage of the partnerUserId-String, the getAccessToken()-method will create different results.

// Application, Activity or Service Android Context
final Context context = this;

final String appId = "FEh9HQNaa87cwdbB";
final String appSecret = "NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM";

// Connector object without partnerUserId
CoreConnector connector = new CoreConnector(context, appId, appSecret);

final String partnerUserId = "FVMW6fp9wnUxKnfekrQduZ96Xt6gemVk";

// Connector object with partnerUserId
CoreConnector connector = new CoreConnector(context, appId, appSecret, partnerUserId);

final String language = "en";

// Connector object with partnerUserId
CoreConnector connector = new CoreConnector(context, appId, appSecret, partnerUserId, language);
Code Description
new CoreConnector(Context context, String appId, String appSecret) Creates the CoreConnector-object when using the following correct
parameters:

context: Default Android Context
appId: The application code you received upon signing the cooperation as a String
appSecret: Your application secret you received upon signing the cooperation as a String

new CoreConnector(Context context, String appId, String appSecret, @Nullable String partnerUserId) Creates the CoreConnector-object in the same way as the above constructor, but with a partnerUserId.
Additional Parameter:

partnerUserId: A customer ID, which allows Thryve to persistently relate a token to a user. If provided, Thryve will return the last valid accessToken previously created for this user instead of generating a new one e.g. FVMW6fp9wnUxKnfekrQduZ96Xt6gemVk

Note: PartnerUserID is only allowed as alpha numeric characters.

new CoreConnector(Context context, String appId, String appSecret, @Nullable String partnerUserId, @Nullable String language) Creates the CoreConnector-object in the same way as the above constructor, but with a user related language.
Additional Parameter:

language: Optionally determines the language setting for the corresponding user. Will be set as a string of the country code, e.g.: en. We currently support five languages: English, German, Spanish, French and Italian. If not set, the default partner language will be used.

Generate a Thryve user token (partnerUserID)

All data stored of any given user is linked to a pseudonymous Thryve user token (partnerUserID). This token will be generated on backend side when calling the getAccessToken method for your user.

An accessToken is required for each user to connect data sources, upload data or retrieve data. You will get the accessToken when calling the method getAccessToken for your user. This method will return the unique accessToken.

If you want to define the Thryve user token (partnerUserID), you need to construct the connector with partnerUserID and call the getAccessToken method with the given partnerUserID. If an accessToken is returned, you defined your user's partnerUserID successfully.

// Must be called in background thread to avoid NetworkOnMainThreadException

String accessToken = connector.getAccessToken();

// Result of 'accessToken' will be e.g. 'de3d1e068537dd927b48988cb6969abe'
Code Description
String getAccessToken() Retrieves a new Thryve access token (Token to access the individual health information of this user), which corresponds to an anonymous user, if a partnerUserId was not set, and will only be generated once. If a partnerUserId was set, the token corresponds to a specific user and will be resent in case of a repeating method call.

Display data source connection screen

Having received the accessToken for your user, the following method allows your user to connect their 3rd party devices via the data source connection screen. To give you a head-start we provide a ready-to-use WebView for the data source connection screen that you can display in your app (see below) – providing a seamless experience for your users.

The Thryve data sources selection screen uses the web view of the OS. To implement the pre-built data source connection screen, an Android-WebView-instance must be passed as a parameter to the method:

final webView = findViewById(R.id.web_view);

connector.handleDataSourceConnection(webView);
Code Description
void handleDataSourceConnection (WebView webview) Generates and shows the data source URL inside the web view for the user related to the last retrieved accessToken (valid for 1h).
webView: Plain android.webkit.WebView-object. Must not be null. Warning: most settings will be overwritten

Android extensions

The following chapter describes additional functionalities of the CoreSDK and modules. You can download the SDK framework files and the sample project in the above chapter.

CoreSDK: Custom data source connection screen (direct connection)

If you want your users to connect their data source through your own data source connection screen or other GUI, you can use the SDKs direct connection functionality.

You can directly connect or revoke a tracker using the SDK’s interface. Since the user provides data input inside a web view, this method needs an instance of a WebView Android object and an implementation of a custom callback function, to asynchronously check for the corresponding result or error, respectively.

Use the data source ID to display the authorization-page of the corresponding data source, e.g., use ID 1 when connecting Fitbit. For the correct data source IDs, please refer to the data sources overview.

The interface method is as follows:

final ConnectorCallback connectorCallback = new ConnectorCallback() {
  @Override
  public void onResult(String accessToken, String url, int dataSourceType, boolean connected, String message) {
    // React on result accordingly, e.g. log result and finish activity that displays WebView
    Log.d("MyTag", "Connection successful. Source " + dataSourceType + ". Token " + accessToken);
    finish();
  }

  @Override
  public void onError(String accessToken, String url, int dataSourceType, String message) {
    // Handle error
    Log.w("MyTag", "Error while connecting. Source " + dataSourceType + ". Token " + accessToken);
  }

  @Override
  public void onSocialLoginError(String accessToken, String url, int dataSourceType, String message) {
    // Handle error
    Log.w("MyTag", "SocialLogin not supported in WebView. Source " + dataSourceType + ". Token " + accessToken);
  }
})

final webView = findViewById(R.id.web_view);
connector.handleDataSourceDirectConnection(
  true,               // Connection: yes
  1,                  // Source Fitbit
  false,              // instantRevoke is irrelevant for connection
  webView,            // WebView object, must not be null
  connectorCallback   // Above defined ConnectorCallback object
);
Code Description
void handleDataSourceDirectConnection (boolean isConnection, int dataSourceType, Boolean isInstantRevoke, WebView webView, ConnectorCallback callback) Handles a new direct connection or revoke request for the data source type using the WebView object. Calls corresponding methods of the callback.

isConnection: true,to handle a new connection, false, to handle revoke
dataSourceType: integertype of the data source (e.g. 1 for Fitbit)
isInstantRevoke: set this to true to not show the Thryve revoke page at all
webView: Plain android.webkit.WebView-object. Must not be null. Warning: most settings will be overwritten
callback: Implementation of the ConnectorCallback

An instance of the ConnectorCallback object must override the methods onResult, onError and onSocialLoginError like in the snippet on the right. For further examples, please also refer to the ThryveSample Android Studio project.

CoreSDK: PartnerDataUpload

The PartnerDataUpload methods allows to upload custom data from the app to the Thryve data warehouse which is not data recorded by any data source. This might be a users demographic data like birthdate and gender or certain indications needed for health status calculation or other analytics of Thryve’s interpretation layer.

Data can be uploaded as DailyDynamicValue, or, for slowly or never changing values parameters as ConstantValues.

Please refer to the sections Accessing the API and the Interpretation Layer for more information.

Health status/Interpretation layer data types

The following values are required for health status and interpretation layer analytics and need to be uploaded via the PartnerDataUpload:

DailyDynamicValue

// Must be called in background thread to avoid NetworkOnMainThreadException

final Date date = new Date();
final int dailyDynamicValueType = 5020;
final long value = 70.5;

connector.uploadDailyDynamicValue(date, dailyDynamicValueType, value);
Code Description
boolean uploadDailyDynamicValue(Date date, int type, long value) Uploads DailyDynamicValue for given day of date and type (LONG). Returns true if upload was successful, false otherwise.
boolean uploadDailyDynamicValue(Date date, int type, double value) Uploads DailyDynamicValue for given day of date and type (DOUBLE). Returns true if upload was successful, false otherwise.
boolean uploadDailyDynamicValue(Date date, int type, boolean value) Uploads DailyDynamicValue for given day of date and type (BOOLEAN). Returns true if upload was successful, false otherwise.
boolean uploadDailyDynamicValue(Date date, int type, String value) Uploads DailyDynamicValue for given day of date and type (STRING). Returns true if upload was successful, false otherwise.
boolean uploadDailyDynamicValue(Date date, int type, Date value) Uploads DailyDynamicValue for given day of date and type (DATE). Returns true if upload was successful, false otherwise.

ConstantValue

// Must be called in background thread to avoid NetworkOnMainThreadException

final int constantValueType = 9050;
final long value = 2;

connector.uploadConstantValue(constantValueType, value);
Code Description
boolean uploadConstantValue(int type, long value) Uploads ConstantValue for given type (LONG). Date is not needed. Returns true if upload was successful, false otherwise.
boolean uploadConstantValue(int type, double value) Uploads ConstantValue for given type (DOUBLE). Date is not needed. Returns true if upload was successful, false otherwise.
boolean uploadConstantValue(int type, boolean value) Uploads ConstantValue for given type (BOOLEAN). Date is not needed. Returns true if upload was successful, false otherwise.
boolean uploadConstantValue(int type, String value) Uploads ConstantValue for given type (STRING). Date is not needed. Returns true if upload was successful, false otherwise.
boolean uploadConstantValue(int type, Date value) Uploads ConstantValue for given type (DATE). Date is not needed. Returns true if upload was successful, false otherwise.

UserInformation

// Must be called in background thread to avoid NetworkOnMainThreadException

final User user = new User(173, 74.2, "1990-01-18", Gender.MALE);
connector.uploadUserInformation(user);
Code Description
boolean uploadUserInformation(
User user)
Uploads UserInformation for the given User object:
public User(int height, double weight, String birthdate, Gender gender)

height: Height in cm (Integer)
weight: Weight in kg (Double)
birthdate: Birth date String in YYYY-MM-dd format
gender: Gender from enum, i.e. MALE, FEMALE, GENDERLESS.

CoreSDK: Get UserInformation

You can retrieve user information directly with the CoreSDK, without the need to call the Wearable API. The implementation makes use of an UserInformation-object that will be wrapped inside a list. Please refer to the right code section for an example.

// Must be called in background thread to avoid NetworkOnMainThreadException

final List<UserInformation> userInformation;
userInformation = connector.getUserInformation();
Code Description
List getUserInformation() Retrieves a list of UserInformation-objects:
public UserInformation(String authenticationToken, String partnerUserID, int height, double weight, String birthdate, Gender gender, List connectedSources)

authenticationToken: Thryve access token (String)
partnerUserID: PartnerUserID (String)
height: Height in cm (Integer)
weight: Weight in kg (Double)
birthdate: Birth date String in YYYY-MM-dd format
gender: Gender from Enum, i.e. MALE, FEMALE, GENDERLESS
connectedSources: List of ConnectedSource. Constructor: public ConnectedSource(String connectedAt, int dataSource), whereas connectedAt is a String timestamp of the connection time and dataSource is the Thryve ID for the connected source.

CoreSDK: ThryveLogProcessor

When implementing the SDK, this interface will allow you to view the logs that the SDK outputs to the console and respond to them.

...//Application onCreate
 Logger.init(Logger.Verbosity.INFO, listOf(ThryveSentryProcessor(this), CustomProcessor(), ...)
...class ThryveSentryProcessor(val context: Context) : ThryveLogProcessor {

    ...

    override val identifier: String
        get() = ThryveSentryProcessor::class.java.simpleName

    ...

    override fun process(level: Int, tag: String?, msg: String?, throwable: Throwable?) {
        if (throwable != null && level == Log.ERROR) {
            Sentry.captureException(throwable, "$msg")
        } else {
            if (level != Log.WARN && level != Log.ERROR) {
                Sentry.addBreadcrumb("$tag::" + (msg ?: throwable?.localizedMessage ?: throwable?.message ?: return))
            } else {
                Sentry.captureMessage(
                    "$tag " + (msg ?: throwable?.localizedMessage ?: throwable?.message ?: return),
                    when (level) {
                        Log.WARN -> SentryLevel.WARNING
                        Log.ERROR -> SentryLevel.ERROR
                        else -> return
                    }
                )
            }
        }
    }

Implementing the interface requires overriding the next methods/values:

Code Description
val identifier : String During logging, this identification is used to verify the registered processors.
fun process(@Logger.Level level: Int, tag: String?, msg: String?, throwable: Throwable? = null) When an event is received, the SDK's internal logging tools invoke this method. The processor should then parse it and treat it properly.

SHealth: Constructor

Before calling any method of the SHealth module, please create the corresponding constructor.

import com.thryve.connector.shealth.SHealthConnector;
SHealthConnector sConnector = new SHealthConnector(this);
Code Description
SHealthConnector() Creates the SHealthConnector-object

SHealth: Integration

Important Note for the registration of Samsung Production Access

Please be aware that Thryve Android SDK versions 4.4.2 and above are equipped with Samsung Health's Data Library Version 1.5.0, whereas the Android SDK versions 4.4.1 and below are equipped with Samsung Health's Data Library Version 1.4.0. Please update Thryve Android SDK to version 4.4.2 (or above) for the smooth usability.

To integrate Samsung Health with your Android app, you need to integrate the SHealth module of Thryve’s SDK. This will allow your users to connect with Samsung Health and to automatically trigger a background routine to retrieve the latest health data. The data is stored in a local database and continuously uploaded to the Thryve servers.

The SHealth integration can be toggled as a service that is running in the background. If startSHealthIntegration-method will be executed for the first time, a permission screen from SHealth app will ask for the user's consent including what data he wants to share. The state of confirmation to this permission will be stored in the SHealth application. The Thryve SDK methods need an Android Activity reference.

private void toggleSHealthIntegration(Activity a) {
    if(!sConnector.isActive(SHealthConnector.Service.SAMSUNG)) {
      List<SHealthDataType> valueTypes = new ArrayList<>();
            valueTypes.add(SHealthDataType.STEP_COUNT);
            valueTypes.add(SHealthDataType.HEART_RATE);
            valueTypes.add(SHealthDataType.SLEEP);
            valueTypes.add(SHealthDataType.SLEEP_STAGE);
            valueTypes.add(SHealthDataType.BLOOD_PRESSURE);
            sConnector.startSHealthIntegration(valueTypes, () -> null, (e) ->
            {
                e.printStackTrace();
                return null;
            });
    } else {
      sConnector.stopSHealthIntegration(a);
    }
}
Code Description
void startSHealthIntegration(List types, Success callback, Error(Exception) callback) Starts the SHealth data retrieval and upload service. Shows the SHealth permission screen when started for the first time. Is accompanied with two callbacks.
void stopSHealthIntegration() Stops the SHealth service, does nothing if service is not running.
boolean isActive(Service service) Use with SHealthConnector.Service.SAMSUNG. Checks, if SHealth-integration is enabled. Should be called to automatically set parameters for GUI etc.
boolean isSHealthSupported(
Activity androidActivity)
Checks if the device supports SHealth integration (if SHealth app is installed and the SDK has access to it). Parameters:

androidActivity: Android Activity reference

void syncronize()
deprecated
Performs SHealth data syncronization. Call this function in order to syncronize the data immediately.
void syncronize(Date before)
deprecated
Performs SHealth data syncronization. Call this function in order to syncronise the data immediately. The Date before is the desired date for which the 30-day historical download will be fetched the data inside this interval. For example, if Date before is set on 30th December 2020, then the historical download will cover the period between 1st December 2020 and 30th December 2020. The function will automatically set the historical download range to 30 days, if there will be more than 30 days difference.
synchronizeEpoch( types: List) Performs Epoch synchronization from last upload date to today.
Call this function in order to synchronise the epoch data immediately.
synchronizeEpoch( startDate: Date, endDate: Date, types: List) Performs Epoch synchronization between given dates, with a maximum of 30 days.
- If difference is longer than 30 days, end date - 30 days is used as start date.
- Call this function in order to synchronise the epoch data immediately.
synchronizeDaily( types: List) Performs Daily synchronization from last upload date to today.
Call this function in order to synchronise the epoch data immediately.
synchronizeDaily( startDate: Date, endDate: Date, types: List) Performs Daily synchronization between given dates, with a maximum of 30 days.
- If difference is longer than 365 days, end date - 365 days is used as start date.
- Call this function in order to synchronise the epoch data immediately.
Name Type Description
Success callback () -> () Callback if integration was successful.
Error callback (Exception) -> () Callback if integration had an exception. If the SHealth is not installed on the device, SHealthIntegrationException will be thrown in Error callback. In case the types are invalid, the IllegalArgumentException will be thrown in Error callback.

SHealth: Synchronize Methods Error Codes

Code Name Description
60010 SYNC_START_AFTER_NOW the start date set in synchronizeEpoch or synchronizeDaily is set as a date that is in the future
60011 SYNC_END_AFTER_NOW the end date set in synchronizeEpoch or synchronizeDaily is set as a date that is in the future
60012 SYNC_END_BEFORE_START the end date set in synchronizeEpoch or synchronizeDaily is set before the start date

GoogleFit Native: Constructor

Before calling any method of the GoogleFit Native module, please create the corresponding constructor.

import com.thryve.connector.module_gfit.GFitConnector;

GFitConnector gfitConnector = new GFitConnector(this);
Code Description
GFitConnector() Creates the GFitConnector-object

GoogleFit Native: Integration

Disclaimer: unreliable/discrepant GoogleFit data:
Please acknowledge that data drawn from GoogleFit REST and/or GoogleFitNative can deviate considerably from the data shown in the GoogleFit app (e.g. up to 1000 steps difference). According to Google, it's due to the calculation engine that creates temporary differences. This has been a known issue and many clients are experiencing these persistent differences. Please refer to GoogleFit official reference for further details.

To integrate Google Fit native with you Android app, you need to integrate the GFitModule of Thryve’s SDK. The module will allow your users to connect with Google Fit locally on their smartphone and will allow your app to trigger a background routine to retrieve both evaluated and aggregated values from different sources and sensors connected to Google Fit.

The Google Fit native integration through the Thryve SDK module will run as a service in the background. The first time the connect()-method is executed, a permission screen built-in the Android operating system will ask for the user's consent to grant access to the specified data types of Google Fit. The state of confirmation of this permission will be stored in the Android settings. The GFitModule methods need a Android Activity reference.

private GFitConnector gFitConnector;

private void createGFitConnector(Activity a){
  this.gFitConnector = GFitConnector(a);
}

private void toggleGFitConnector() {
    if(!gFitConnector.isActive(GFitConnector.Service.GOOGLE_FIT)) {
      gFitConnector.connect();
    } else {
      gFitConnector.disconnect();
    }
}
Code Description
void isActive(Service service) Returns true if the service is registered as active.
boolean isGoogleFitInstalled() (moved) please refer to isGFitAvailable
boolean isGFitAvailable Utility attribute to prevent trying to connect a user to Google Fit Native if the system app is not present in the device ( no data would be retrieved ).
boolean hasPermissions() returns true if the system has the permissions required.
void connect()
deprecated
Begins the connection process to register the service, request permissions, and starts sending data. As of 4.2.2 and specially 4.3.0 onwards, you should replace this with the methods  authorizeGFitIntegration/startGFitIntegration as the initialisers of the google fit flow
void authorizeGFitIntegration(types: List, onAuthorized: (Boolean) -> Callback, onError: (error: java.lang.Exception) -> Callback) New way to initialize a google fit connection safely with all the checks and recieving feedback.
boolean startGFitIntegration (types: List) A more lightweight way to perform the same operations as above, with less feedback.
void disconnect() Ends the connection, unregisters all services and worker threads.
void onPermissionGranted()
deprecated
Method that must be called when user permission is granted, to proceed with the integration. As of 4.2.2 and specially 4.3.0 onwards, you should replace this method with the methods onRequestPermissionsResult/onActivityResult
void onRequestPermissionsResult(params) Use this method to pass an activity's onRequestPermissionsResult parameters to the connector.
void onActivityResult(params) Use this method to pass an activity's onActivityResult parameters to the connector.
void forceUpload()
deprecated
Forces an instant upload of any available data since the last. Warning: do not run this method at ease, it's meant to be an exceptional case and not a replacement for the battery-saving built-in WorkManager
Boolean retrieveHistoricalDataSince(date: Date)
deprecated
Single-use ( per app installation ) method to retrieve historical data from google fit SDK since a moment in time to the date of execution. The maximum is 60 days before the date of execution, any date before that is trimmed internally.
synchronizeEpoch( types: List) Performs Epoch synchronization from last upload date to today.
Call this function in order to synchronise the epoch data immediately.
synchronizeEpoch( startDate: Date, endDate: Date, types: List) Performs Epoch synchronization between given dates, with a maximum of 30 days.
- If difference is longer than 30 days, end date - 30 days is used as start date.
- Call this function in order to synchronise the epoch data immediately.
synchronizeDaily( types: List) Performs Daily synchronization from last upload date to today.
Call this function in order to synchronise the epoch data immediately.
synchronizeDaily( startDate: Date, endDate: Date, types: List) Performs Daily synchronization between given dates, with a maximum of 30 days.
- If difference is longer than 365 days, end date - 365 days is used as start date.
- Call this function in order to synchronise the epoch data immediately.

Additional details

class App : Application, Configuration.Provider {
    override fun getWorkManagerConfiguration() =
            Configuration.Builder()
                    .setMinimumLoggingLevel(Log.VERBOSE)
                    .build()

GoogleFit Native: Synchronize Methods Error Codes

Code Name Description
80010 SYNC_START_AFTER_NOW the start date set in synchronizeEpoch or synchronizeDaily is set as a date that is in the future
80011 SYNC_END_AFTER_NOW the end date set in synchronizeEpoch or synchronizeDaily is set as a date that is in the future
80012 SYNC_END_BEFORE_START the end date set in synchronizeEpoch or synchronizeDaily is set before the start date

BGMModule (experimental): Constructor

The BGM module allows your users to connect their connected bloog glucose meters from the data sources i-Sens, B.Braun, and RocheAccuchek with your app via Bluetooth.

The module uses the Android classes BluetoothDevice and BluetoothGattCharacteristic that are wrapped inside the IBLE-interface that your underlying activity needs to implement. You must handover the activity itself to the BGMModule constructor as well.

public class BGMActivity extends AppCompatActivity implements IBLE {
  private BGMConnector bgmConnector;

  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    bgmConnector = new BGMConnector(this, BGMDevice.BBRAUN);
    // [...]
  }

  @Override
  public void bleReady(@NotNull BluetoothGattCharacteristic racp) {
    bgmConnector.sync(racp);
  }

  @Override
  public void bleActivated(boolean isOn) {
    if(isOn) {
      bgmConnector.startScanning();
    }
  }

  @Override
  public void bleFound(@NotNull BluetoothDevice bluetoothDevice, int rssi) {
    bgmConnector.connect(bluetoothDevice);
  }

  @Override
  public void blePaired(@NotNull BluetoothDevice bluetoothDevice) {
    bgmConnector.disconnect();
  }

  @Override
  public void bleDisconnected(@NotNull BluetoothDevice bluetoothDevice) {}
}
Code Description
BGMConnector(AppCompatActivity activity, BGMDevice device) Creates the BGMConnector-object. Should be called from within an Android Activity. Parameters:

activity: Android AppCompatActivity activity
device: The device as defined in the BGMDevice Enum. Either ISENS, BBRAUN, or ROCHE_ACCUCHEK

We recommend initializing the BGMConnector-object inside your Android Activity class. This class also needs to implement the IBLE-interface. Please refer to the sample Java code on the right side of the documentation.

BGMModule (experimental): Scanning

To initially find your BGM device via Bluetooth, you need to start a scanning process that searches for nearby Bluetooth devices. You can perform this with the BGMConnector by using the following two methods:

Code Description
void startScanning() Starts the Bluetooth scanning process. This process runs indefinitely.
void stopScanning() Stops the Bluetooth scanning process. Does nothing, if it is not running.

The scanning process will not stop running, until it is stopped manually. Please ensure to implement proper Handlers, if you want to setup a timeout for device search.

BGMModule (experimental): Connection

When the scan finds one or more suitable devices, the IBLE-interface method bleFound(BluetoothDevice bluetoothDevice, int rssi) will be called, handing over the Android BluetoothDevice object. This device can either be stored temporarily for later connection or a connection to pair the device can directly be established.

Code Description
void connect(BluetoothDevice device) Connects to the given BluetoothDevice. Stores pairing information in the background. If a connection is active and paired the bleReady-method from IBLE interface will be called and data can be synchronized.
void disconnect() Disconnects the Bluetooth device. The pairing information stays valid.
void delete(BGMDevice device) Deletes the Bluetooth device by removing the pairing information as well.

BGMModule (experimental): Syncing

The synchronization process should be executed after the device has been successfully connected and the BluetoothGattCharacteristic has been handed over via the IBLE interface bleReady(BluetoothGattCharacteristic racp) method. The sync-method will perform the synchronization of recent BloodGlucose data in the background (up to one week, if the data has not been synchronized before) and will perform a direct upload to the Thryve data warehouse. When the upload succeeds, the disconnect of the BGMConnector will be executed automatically.

The method is structured as followed:

Code Description
void sync(BluetoothGattCharacteristic racp) Performs a background synchronization of the BloodGlucose data. A debug printout will be performed, if the synchronization was successful.

Integration for Web-Apps

The integration of Thryve into your web app does not require an SDK, but rather a collection of simple API-methods described in the next paragraphs. The methods allow to create a pseudonymous Thryve user token for your user and to allow your users to connect and disconnect 3rd party OAuth or direct access data sources. Please refer to the code snippets on the right side of the documentation for example implementations.

Get Thryve User Token

All data stored of any given user is linked to a pseudonymous Thryve user token (partnerUserID). This token will be generated on backend side when calling the this method for your user.

An accessToken is required for each user to connect data sources, upload data or retrieve data. You will get the accessToken when calling the method getAccessToken for your user. This method will return the unique accessToken.

If you want to define the Thryve user token (partnerUserID), you need to construct the connector with partnerUserID and call the getAccessToken method with the given partnerUserID. If an accessToken is returned, you defined your user's partnerUserID successfully.

curl --location --request POST -u'healthapp:A7qmaxf9a' \
--header'AppAuthorization: Basic RkVoOUhRTmFhODdjd2RiQjpOTDduVGVnUG01REt0OExyQlpQNjJIUXo2Vk5aYUd1TQ==' \
--data-urlencode'partnerUserID=FVMW6fp9wnUxKnfekrQduZ96Xt6gemVk' --data-urlencode'language=en'\
'https://api.und-gesund.de/v5/accessToken'
HttpURLConnection connection = null;
try {
  URL url = new URL("https://api.und-gesund.de/restjson/v5/accessToken");
  connection = (HttpURLConnection) url.openConnection();
  connection.setDoInput(true);
  connection.setDoOutput(true);

  connection.setRequestMethod("POST");
  connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

  String authorization = new String(Base64.getEncoder().encode("healthapp:A7qmaxf9a".getBytes()));
  connection.setRequestProperty("Authorization", String.format("Basic %s", authorization));

  String appAuthorization = new String(Base64.getEncoder().encode("FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM".getBytes()));
  connection.setRequestProperty("AppAuthorization", String.format("Basic %s", appAuthorization));

  connection.connect();

  OutputStream outputStream = connection.getOutputStream();
  OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
  BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
  bufferedWriter.write("partnerUserID=FVMW6fp9wnUxKnfekrQduZ96Xt6gemVk&language=en");
  bufferedWriter.flush(); bufferedWriter.close(); outputStream.close();

  if(connection.getResponseCode() == 200) {
    StringBuilder response = new StringBuilder();
    Reader inputStreamReader = new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8);
    BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
    for(String line = bufferedReader.readLine(); line != null; line = bufferedReader.readLine()) {
      response.append(line);
    }
    System.out.println(response.toString());
  }
} catch(IOException e) {
  e.printStackTrace();
} finally {
  if(connection != null) {
    connection.disconnect();
  }
}
let url = URL(string: "https://api.und-gesund.de/v5/accessToken")!

var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")

let authCredentials = String("healthapp:A7qmaxf9a").data(using: String.Encoding.utf8)!.base64EncodedString()
request.setValue("Basic \(authCredentials)", forHTTPHeaderField: "Authorization")

let appAuthCredentials = String("FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM").data(using: String.Encoding.utf8)!.base64EncodedString()
request.setValue("Basic \(appAuthCredentials)", forHTTPHeaderField: "AppAuthorization")

let data = "partnerUserID=FVMW6fp9wnUxKnfekrQduZ96Xt6gemVk&language=en"
request.httpBody = NSMutableData(data: String(data).data(using: String.Encoding.utf8)!) as Data

let task = URLSession(configuration: URLSessionConfiguration.default).dataTask(with: request) {(data, response, error) in
    if let httpResponse = response as? HTTPURLResponse {
        if httpResponse.statusCode == 200 {
            print(String(data: data!, encoding: .utf8)!)
        }
    }
}
task.resume()
import json, requests, base64

headers = {
    'content-type': 'application/x-www-form-urlencoded',
    'Authorization': 'Basic %s' % base64.b64encode('healthapp:A7qmaxf9a'),
    'AppAuthorization': 'Basic %s' % base64.b64encode('FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM')
}
params = {
  'partnerUserID': 'FVMW6fp9wnUxKnfekrQduZ96Xt6gemVk',
  'language': 'en'
}

r = requests.post(
    'https://api.und-gesund.de/v5/accessToken',
    params = params,
    headers = headers
)

if r.status_code == 200:
    print r.text
$(function () {
  var formData = {
    partnerUserID: "FVMW6fp9wnUxKnfekrQduZ96Xt6gemVk",
    language: "en",
  };

  $.ajax({
    url: "https://api.und-gesund.de/v5/accessToken",
    type: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
      Authorization: "Basic " + btoa("healthapp:A7qmaxf9a"),
      AppAuthorization:
        "Basic " + btoa("FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM"),
    },
    data: formData,
    statusCode: {
      200: function (data) {
        console.log(data.responseText);
      },
    },
  });
});
post
https://api.und-gesund.de/v5/accessToken
Header Parameter
Authorization
required
(Basic b64) username:password
AppAuthorization
required
(Basic b64) appID:appSecret
Query Parameter
appID
required
The application code provided upon entering a contract. Example structure: FEh9HQNaa87cwdbB
appSecret
required
Your application secret provided upon entering a contract. Example structure: NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM
PartnerUserID
optional
An optional customerID, which allows Thryve to persistently relate the accessToken to a user. If a customerID is provided, Thryve will return the last valid accessToken previously created for this user instead of generating a new one. Example structure: FVMW6fp9wnUxKnfekrQduZ96Xt6gemVk.

Note: partnerUserID is only allowed to consist of alphanumeric characters.
language
optional
Optionally determines the language setting for the corresponding user. Will be set as a string of the country code, e.g.: en. We currently support five languages: English, German, Spanish, French and Italian. If not set, the default partner language will be used.

Responses

Response Codes Description
200 Request successful
4xx error code


Response Parameter
accessToken Sample response: de3d1e068537dd927b48988cb6969abe

Sources connection

Having received the accessToken for your user, the following API call allows your user to connect their 3rd party devices via the data source connection screen. To give you a head-start we provide a ready-to-use website-URL for the data source connection screen that you can display e.g. as a Web view in your app – providing a seamless experience for your users.

The following interface returns a URL-String that can be used for your customer's data source connection.

curl --location --request POST -u'healthapp:A7qmaxf9a' \
--header'AppAuthorization: Basic RkVoOUhRTmFhODdjd2RiQjpOTDduVGVnUG01REt0OExyQlpQNjJIUXo2Vk5aYUd1TQ==' \
--data-urlencode'authenticationToken=de3d1e068537dd927b48988cb6969abe' \
'https://api.und-gesund.de/v5/dataSourceURL'
HttpURLConnection connection = null;
try {
  URL url = new URL("https://api.und-gesund.de/v5/dataSourceURL");
  connection = (HttpURLConnection) url.openConnection();
  connection.setDoInput(true);
  connection.setDoOutput(true);

  connection.setRequestMethod("POST");
  connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

  String authorization = new String(Base64.getEncoder().encode("healthapp:A7qmaxf9a".getBytes()));
  connection.setRequestProperty("Authorization", String.format("Basic %s", authorization));

  String appAuthorization = new String(Base64.getEncoder().encode("FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM".getBytes()));
  connection.setRequestProperty("AppAuthorization", String.format("Basic %s", appAuthorization));

  connection.connect();

  OutputStream outputStream = connection.getOutputStream();
  OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
  BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
  bufferedWriter.write("authenticationToken=de3d1e068537dd927b48988cb6969abe");
  bufferedWriter.flush(); bufferedWriter.close(); outputStream.close();

  if(connection.getResponseCode() == 200) {
    StringBuilder response = new StringBuilder();
    Reader inputStreamReader = new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8);
    BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
    for(String line = bufferedReader.readLine(); line != null; line = bufferedReader.readLine()) {
      response.append(line);
    }
    System.out.println(response.toString());
  }
} catch(IOException e) {
  e.printStackTrace();
} finally {
  if(connection != null) {
    connection.disconnect();
  }
}
let url = URL(string: "https://api.und-gesund.de/v5/dataSourceURL")!

var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")

let authCredentials = String("healthapp:A7qmaxf9a").data(using: String.Encoding.utf8)!.base64EncodedString()
request.setValue("Basic \(authCredentials)", forHTTPHeaderField: "Authorization")

let appAuthCredentials = String("FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM").data(using: String.Encoding.utf8)!.base64EncodedString()
request.setValue("Basic \(appAuthCredentials)", forHTTPHeaderField: "AppAuthorization")

let data = "authenticationToken=de3d1e068537dd927b48988cb6969abe"
request.httpBody = NSMutableData(data: String(data).data(using: String.Encoding.utf8)!) as Data

let task = URLSession(configuration: URLSessionConfiguration.default).dataTask(with: request) {(data, response, error) in
    if let httpResponse = response as? HTTPURLResponse {
        if httpResponse.statusCode == 200 {
            print(String(data: data!, encoding: .utf8)!)
        }
    }
}
task.resume()
import json, requests, base64

headers = {
    'content-type': 'application/x-www-form-urlencoded',
    'Authorization': 'Basic %s' % base64.b64encode('healthapp:A7qmaxf9a'),
    'AppAuthorization': 'Basic %s' % base64.b64encode('FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM')
}
params = { 'authenticationToken': 'de3d1e068537dd927b48988cb6969abe' }

r = requests.post(
    'https://api.und-gesund.de/v5/dataSourceURL',
    params = params,
    headers = headers
)

if r.status_code == 200:
    print r.text
$(function () {
  var formData = {
    authenticationToken: "de3d1e068537dd927b48988cb6969abe",
  };

  $.ajax({
    url: "https://api.und-gesund.de/v5/dataSourceURL",
    type: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
      Authorization: "Basic " + btoa("healthapp:A7qmaxf9a"),
      AppAuthorization:
        "Basic " + btoa("FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM"),
    },
    data: formData,
    statusCode: {
      200: function (data) {
        console.log(data.responseText);
      },
    },
  });
});
post
https://api.und-gesund.de/v5/dataSourceURL
Header Parameter
Authorization
required
(Basic b64) username:password
AppAuthorization
required
(Basic b64) appID:appSecret
Query Parameter
authenticationToken
required
The Thryve acces token generated for your user. Example: de3d1e068537dd927b48988cb6969abe

Response example

https://service.und-gesund.de/dataSourceSelection.html?token=2098X4583882346015849835

Responses

Response Codes Description
200 Request successful
4xx error code

Custom data source connection screen (direct connection)

If you want your users to connect their data source through your own data source connection screen or other GUI, there is also a possibility to directly open the corresponding tracker connection or revoke page.

Since the redirect logic must be connected to our server routines, you will need to open the tracker connection page in the same window with a URL that needs to be constructed as described in this chapter. Moreover, to correctly come back to your website, you will need to provide a redirect URL.

To open the correct tracker connection page, you will need to provide the Thryve internal source ID.

Construct direct connection URL

Direct connection example URL

https://service.und-gesund.de/dataSourceDirectConnection.html?token=2098X4583882346015849835&dataSource=1

Direct revoke example URL

https://service.und-gesund.de/dataSourceDirectRevoke.html?token=2098X4583882346015849835&dataSource=1&direct=true

The direct connection URLs are based on the access of the dataSourceSelection page. Thus you will need to call the dataSourceURL-interface as described in the corresponding section above: Sources connection.

The result URL will contain a parameter with the key token. Example structure:
https://service.und-gesund.de/dataSourceSelection.html?token=2098X4583882346015849835

Since this token is the temporary authentication for the data sources, it is needed to perform a request to the direct connection URL as well.

For direct connection or revoke, you will have to exchange the html file like the following:

For addressing the correct connection page, the data source ID must be added with the URL-parameter dataSource. Please also refer to the data sources overview that also lists all source ID's. Example structure:
dataSource=1 (e.g. for Fitbit)

For the revoke call, you may also add another URL-parameter to directly get redirected to the revoke page of the corresponding source. Thus the Thryve confirmation page won't be shown beforehand. For this, just set the parameter direct to true. If set to false or not set at all, the confirmation page will be shown as usual. Example structure:
direct=true

Please refer to the code example on the right to see a full built URL. This URL can now be opened in any browser.

Parse result

Direct connection result example URL

https://service.und-gesund.de/dataSourceDirectConnectionResult.html?token=2098X4583882346015849835&dataSource=2&connected=true

After the user interaction, the Thryve server will automatically redirect to the page
dataSourceDirectConnectionResult.html.
The URL-parameter connected will now indicate, if the connection or revoke was successful. The following states can happen:

Please refer to the code example on the right to see a full result URL.

Handle redirect URL

Direct connection example URL

https://service.und-gesund.de/dataSourceDirectConnection.html?token=2098X4583882346015849835&dataSource=1&redirect_uri=https://example.com/result

Direct revoke example URL

https://service.und-gesund.de/dataSourceDirectRevoke.html?token=2098X4583882346015849835&dataSource=1&direct=true&redirect_uri=https://example.com/result

For a better user experience, you have also the possibility to set a redirect URL that will be used instead of the Thryve result page. This allows to seemlessly integrate into your web based application.

For this you will need to additionally set the URL-parameter redirect_uri for all direct connection calls. Example structure:
redirect_uri=https://example.com/result

The connected-result will also handed over to your URL.

Please refer to the code example on the right to see a full connection and revoke URL that contains the redirect URL as well.

PartnerDataUpload

The PartnerDataUpload allows you to upload custom data from the app to the Thryve data warehouse which is not data recorded by any data source. This might be a user’s demographic data like birthdate and gender or certain indications needed for health status calculation or other analytics of Thryve’s interpretation layer.

Data can be uploaded as DynamicEpochValues, DailyDynamicValue, or for slowly changing, long-term parameters as ConstantValues.

Please also refer to the sections Accessing the API and the Interpretation Layer of the data types overview.

While information about dynamicEpochValues DataTypes to be uploaded can be found in the Biomarkers page, the Additional Recording Information section explains the information related to "details" parameter, stated in the payload.

DynamicEpochValue

JSON Object String example

{
  "data": [
    {
      "value": "38.7",
      "startTimestamp": "2020-01-20T07:07:00+01:00",
      "endTimestamp": "2020-01-20T07:08:00+01:00",
      "dynamicValueType": 5041,
      "details": {
        "trustworthiness": "plausible",
        "medicalGrade": true,
        "userReliability": "confirmed",
        "timezoneOffset": 120
      }
    }
  ]
}
curl --location --request PUT -u'healthapp:A7qmaxf9a' --header'Content-Type:application/json' \
--header'AppAuthorization: Basic RkVoOUhRTmFhODdjd2RiQjpOTDduVGVnUG01REt0OExyQlpQNjJIUXo2Vk5aYUd1TQ==' \
--header'authenticationToken:de3d1e068537dd927b48988cb6969abe' \
--data-urlencode'@data.json' 'https://api.und-gesund.de/v5/dynamicEpochValue'
// JSON-String upload data, please refer to above example
// final String data = ...

HttpURLConnection connection = null;
try {
  URL url = new URL("https://api.und-gesund.de/v5/dynamicEpochValue");
  connection = (HttpURLConnection) url.openConnection();
  connection.setDoInput(true);
  connection.setDoOutput(true);

  connection.setRequestMethod("PUT");
  connection.setRequestProperty("Content-Type", "application/json");

  String authorization = new String(Base64.getEncoder().encode("healthapp:A7qmaxf9a".getBytes()));
  connection.setRequestProperty("Authorization", String.format("Basic %s", authorization));

  String appAuthorization = new String(Base64.getEncoder().encode("FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM".getBytes()));
  connection.setRequestProperty("AppAuthorization", String.format("Basic %s", appAuthorization));

  connection.setRequestProperty("authenticationToken", "de3d1e068537dd927b48988cb6969abe");
  connection.connect();

  OutputStream outputStream = connection.getOutputStream();
  OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
  BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
  bufferedWriter.write(data);
  bufferedWriter.flush(); bufferedWriter.close(); outputStream.close();

  if(connection.getResponseCode() == 204) {
    System.out.println("success");
  }
} catch(IOException e) {
  e.printStackTrace();
} finally {
  if(connection != null) {
    connection.disconnect();
  }
}
let url = URL(string: "https://api.und-gesund.de/v5/dynamicEpochValue")!

// JSON-String upload data, please refer to above example
// let data = ...

var request = URLRequest(url: url)
request.httpMethod = "PUT"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")

let authCredentials = String("healthapp:A7qmaxf9a").data(using: String.Encoding.utf8)!.base64EncodedString()
request.setValue("Basic \(authCredentials)", forHTTPHeaderField: "Authorization")

let appAuthCredentials = String("FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM").data(using: String.Encoding.utf8)!.base64EncodedString()
request.setValue("Basic \(appAuthCredentials)", forHTTPHeaderField: "AppAuthorization")

request.setValue("de3d1e068537dd927b48988cb6969abe", forHTTPHeaderField: "authenticationToken")

request.httpBody = NSMutableData(data: String(data).data(using: String.Encoding.utf8)!) as Data

let task = URLSession(configuration: URLSessionConfiguration.default).dataTask(with: request) {(data, response, error) in
    if let httpResponse = response as? HTTPURLResponse {
        if httpResponse.statusCode == 204 {
            print("success")
        }
    }
}
task.resume()
# JSON-String upload data, please refer to above example
# data = ...

headers = {
    'content-type': 'application/json',
    'Authorization': 'Basic %s' % base64.b64encode('healthapp:A7qmaxf9a'),
    'AppAuthorization': 'Basic %s' % base64.b64encode('FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM'),
    'authenticationToken': 'de3d1e068537dd927b48988cb6969abe'
}

r = requests.put(
    'https://api.und-gesund.de/v5/dynamicEpochValue',
    data = data,
    headers = headers
)

if r.status_code == 204:
    print 'success'
$(function () {
  // JSON-String upload data, please refer to above example
  // var data = ...

  $.ajax({
    url: "https://api.und-gesund.de/v5/dynamicEpochValue",
    type: "PUT",
    headers: {
      "Content-Type": "application/json",
      Authorization: "Basic " + btoa("healthapp:A7qmaxf9a"),
      AppAuthorization:
        "Basic " + btoa("FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM"),
      authenticationToken: "de3d1e068537dd927b48988cb6969abe",
    },
    data: data,
    statusCode: {
      204: function (data, textStatus, xhr) {
        console.log("success");
      },
    },
  });
});
PUT
https://api.und-gesund.de/v5/dynamicEpochValue
Header Parameter
Authorization
required
(Basic b64) username:password
AppAuthorization
required
(Basic b64) appID:appSecret
authenticationToken
required
The Thryve acces token generated for your user. Example: de3d1e068537dd927b48988cb6969abe
Query Parameter
[JSON Object String] JSON object, including the data array that contains objects. Please check the right column for the structure and an example.

While information about dynamicEpochValues DataTypes to be uploaded can be found in the Biomarkers page, the Additional Recording Information section explains the information related to "details" parameter, stated in the payload.

Responses

Response Codes Description
204 Upload successful
4xx error code

DailyDynamicValue

JSON Object String example

{
  "data": [
    {
      "day": "2019-01-01T00:00:00Z",
      "dailyDynamicValueType": 5020,
      "value": 70.5
    }
  ]
}
curl --location --request PUT -u'healthapp:A7qmaxf9a' --header'Content-Type:application/json' \
--header'AppAuthorization: Basic RkVoOUhRTmFhODdjd2RiQjpOTDduVGVnUG01REt0OExyQlpQNjJIUXo2Vk5aYUd1TQ==' \
--header'authenticationToken:de3d1e068537dd927b48988cb6969abe' \
--data-urlencode'@data.json' 'https://api.und-gesund.de/v5/dailyDynamicValues'
// JSON-String upload data, please refer to above example
// final String data = ...

HttpURLConnection connection = null;
try {
  URL url = new URL("https://api.und-gesund.de/v5/dailyDynamicValues");
  connection = (HttpURLConnection) url.openConnection();
  connection.setDoInput(true);
  connection.setDoOutput(true);

  connection.setRequestMethod("PUT");
  connection.setRequestProperty("Content-Type", "application/json");

  String authorization = new String(Base64.getEncoder().encode("healthapp:A7qmaxf9a".getBytes()));
  connection.setRequestProperty("Authorization", String.format("Basic %s", authorization));

  String appAuthorization = new String(Base64.getEncoder().encode("FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM".getBytes()));
  connection.setRequestProperty("AppAuthorization", String.format("Basic %s", appAuthorization));

  connection.setRequestProperty("authenticationToken", "de3d1e068537dd927b48988cb6969abe");
  connection.connect();

  OutputStream outputStream = connection.getOutputStream();
  OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
  BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
  bufferedWriter.write(data);
  bufferedWriter.flush(); bufferedWriter.close(); outputStream.close();

  if(connection.getResponseCode() == 204) {
    System.out.println("success");
  }
} catch(IOException e) {
  e.printStackTrace();
} finally {
  if(connection != null) {
    connection.disconnect();
  }
}
let url = URL(string: "https://api.und-gesund.de/v5/dailyDynamicValues")!

// JSON-String upload data, please refer to above example
// let data = ...

var request = URLRequest(url: url)
request.httpMethod = "PUT"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")

let authCredentials = String("healthapp:A7qmaxf9a").data(using: String.Encoding.utf8)!.base64EncodedString()
request.setValue("Basic \(authCredentials)", forHTTPHeaderField: "Authorization")

let appAuthCredentials = String("FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM").data(using: String.Encoding.utf8)!.base64EncodedString()
request.setValue("Basic \(appAuthCredentials)", forHTTPHeaderField: "AppAuthorization")

request.setValue("de3d1e068537dd927b48988cb6969abe", forHTTPHeaderField: "authenticationToken")

request.httpBody = NSMutableData(data: String(data).data(using: String.Encoding.utf8)!) as Data

let task = URLSession(configuration: URLSessionConfiguration.default).dataTask(with: request) {(data, response, error) in
    if let httpResponse = response as? HTTPURLResponse {
        if httpResponse.statusCode == 204 {
            print("success")
        }
    }
}
task.resume()
# JSON-String upload data, please refer to above example
# data = ...

headers = {
    'content-type': 'application/json',
    'Authorization': 'Basic %s' % base64.b64encode('healthapp:A7qmaxf9a'),
    'AppAuthorization': 'Basic %s' % base64.b64encode('FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM'),
    'authenticationToken': 'de3d1e068537dd927b48988cb6969abe'
}

r = requests.put(
    'https://api.und-gesund.de/v5/dailyDynamicValues',
    data = data,
    headers = headers
)

if r.status_code == 204:
    print 'success'
$(function () {
  // JSON-String upload data, please refer to above example
  // var data = ...

  $.ajax({
    url: "https://api.und-gesund.de/v5/dailyDynamicValues",
    type: "PUT",
    headers: {
      "Content-Type": "application/json",
      Authorization: "Basic " + btoa("healthapp:A7qmaxf9a"),
      AppAuthorization:
        "Basic " + btoa("FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM"),
      authenticationToken: "de3d1e068537dd927b48988cb6969abe",
    },
    data: data,
    statusCode: {
      204: function (data, textStatus, xhr) {
        console.log("success");
      },
    },
  });
});
PUT
https://api.und-gesund.de/v5/dailyDynamicValues
Header Parameter
Authorization
required
(Basic b64) username:password
AppAuthorization
required
(Basic b64) appID:appSecret
authenticationToken
required
The Thryve acces token generated for your user. Example: de3d1e068537dd927b48988cb6969abe
Query Parameter
[JSON Object String] JSON object, including the data array that contains objects. Please check the right column for the structure and an example.

Responses

Response Codes Description
204 Upload successful
4xx error code

ConstantValue

JSON Object String example

{
  "data": [
    {
      "dailyDynamicValueType": 9050,
      "value": 2
    }
  ]
}
curl --location --request PUT -u'healthapp:A7qmaxf9a' --header'Content-Type:application/json' \
--header'AppAuthorization: Basic RkVoOUhRTmFhODdjd2RiQjpOTDduVGVnUG01REt0OExyQlpQNjJIUXo2Vk5aYUd1TQ==' \
--header'authenticationToken:de3d1e068537dd927b48988cb6969abe' \
--data-urlencode'@data.json' 'https://api.und-gesund.de/v5/constantValue'
// JSON-String upload data, please refer to above example
// final String data = ...

HttpURLConnection connection = null;
try {
  URL url = new URL("https://api.und-gesund.de/v5/constantValue");
  connection = (HttpURLConnection) url.openConnection();
  connection.setDoInput(true);
  connection.setDoOutput(true);

  connection.setRequestMethod("PUT");
  connection.setRequestProperty("Content-Type", "application/json");

  String authorization = new String(Base64.getEncoder().encode("healthapp:A7qmaxf9a".getBytes()));
  connection.setRequestProperty("Authorization", String.format("Basic %s", authorization));

  String appAuthorization = new String(Base64.getEncoder().encode("FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM".getBytes()));
  connection.setRequestProperty("AppAuthorization", String.format("Basic %s", appAuthorization));

  connection.setRequestProperty("authenticationToken", "de3d1e068537dd927b48988cb6969abe");
  connection.connect();

  OutputStream outputStream = connection.getOutputStream();
  OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
  BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
  bufferedWriter.write(data);
  bufferedWriter.flush(); bufferedWriter.close(); outputStream.close();

  if(connection.getResponseCode() == 204) {
    System.out.println("success");
  }
} catch(IOException e) {
  e.printStackTrace();
} finally {
  if(connection != null) {
    connection.disconnect();
  }
}
let url = URL(string: "https://api.und-gesund.de/v5/constantValue")!

// JSON-String upload data, please refer to above example
// let data = ...

var request = URLRequest(url: url)
request.httpMethod = "PUT"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")

let authCredentials = String("healthapp:A7qmaxf9a").data(using: String.Encoding.utf8)!.base64EncodedString()
request.setValue("Basic \(authCredentials)", forHTTPHeaderField: "Authorization")

let appAuthCredentials = String("FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM").data(using: String.Encoding.utf8)!.base64EncodedString()
request.setValue("Basic \(appAuthCredentials)", forHTTPHeaderField: "AppAuthorization")

request.setValue("de3d1e068537dd927b48988cb6969abe", forHTTPHeaderField: "authenticationToken")

request.httpBody = NSMutableData(data: String(data).data(using: String.Encoding.utf8)!) as Data

let task = URLSession(configuration: URLSessionConfiguration.default).dataTask(with: request) {(data, response, error) in
    if let httpResponse = response as? HTTPURLResponse {
        if httpResponse.statusCode == 204 {
            print("success")
        }
    }
}
task.resume()
# JSON-String upload data, please refer to above example
# data = ...

headers = {
    'content-type': 'application/json',
    'Authorization': 'Basic %s' % base64.b64encode('healthapp:A7qmaxf9a'),
    'AppAuthorization': 'Basic %s' % base64.b64encode('FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM'),
    'authenticationToken': 'de3d1e068537dd927b48988cb6969abe'
}

r = requests.put(
    'https://api.und-gesund.de/v5/constantValue',
    data = data,
    headers = headers
)

if r.status_code == 204:
    print 'success'
$(function () {
  // JSON-String upload data, please refer to above example
  // var data = ...

  $.ajax({
    url: "https://api.und-gesund.de/v5/constantValue",
    type: "PUT",
    headers: {
      "Content-Type": "application/json",
      Authorization: "Basic " + btoa("healthapp:A7qmaxf9a"),
      AppAuthorization:
        "Basic " + btoa("FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM"),
      authenticationToken: "de3d1e068537dd927b48988cb6969abe",
    },
    data: data,
    statusCode: {
      204: function (data, textStatus, xhr) {
        console.log("success");
      },
    },
  });
});
PUT
https://api.und-gesund.de/v5/constantValue
Header Parameter
Authorization
required
(Basic b64) username:password
AppAuthorization
required
(Basic b64) appID:appSecret
authenticationToken
required
The Thryve acces token generated for your user. Example: de3d1e068537dd927b48988cb6969abe
Query Parameter
[JSON Object String] JSON object, including the data array that contains objects. Please check the right column for the structure and an example.

Responses

Response Codes Description
204 Upload successful
4xx error code

UserInformation

JSON Object String example

{
  "height": 192,
  "weight": 91,
  "birthdate": "1991-01-06",
  "gender": "male"
}
curl --location --request PUT -u'healthapp:A7qmaxf9a' --header'Content-Type:application/json' \
--header'AppAuthorization: Basic RkVoOUhRTmFhODdjd2RiQjpOTDduVGVnUG01REt0OExyQlpQNjJIUXo2Vk5aYUd1TQ==' \
--header'authenticationToken:de3d1e068537dd927b48988cb6969abe' \
--data-urlencode'@data.json' 'https://api.und-gesund.de/v5/userInformation'
// JSON-String upload data, please refer to above example
// final String data = ...

HttpURLConnection connection = null;
try {
  URL url = new URL("https://api.und-gesund.de/v5/userInformation");
  connection = (HttpURLConnection) url.openConnection();
  connection.setDoInput(true);
  connection.setDoOutput(true);

  connection.setRequestMethod("PUT");
  connection.setRequestProperty("Content-Type", "application/json");

  String authorization = new String(Base64.getEncoder().encode("healthapp:A7qmaxf9a".getBytes()));
  connection.setRequestProperty("Authorization", String.format("Basic %s", authorization));

  String appAuthorization = new String(Base64.getEncoder().encode("FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM".getBytes()));
  connection.setRequestProperty("AppAuthorization", String.format("Basic %s", appAuthorization));

  connection.setRequestProperty("authenticationToken", "de3d1e068537dd927b48988cb6969abe");
  connection.connect();

  OutputStream outputStream = connection.getOutputStream();
  OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
  BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
  bufferedWriter.write(data);
  bufferedWriter.flush(); bufferedWriter.close(); outputStream.close();

  if(connection.getResponseCode() == 204) {
    System.out.println("success");
  }
} catch(IOException e) {
  e.printStackTrace();
} finally {
  if(connection != null) {
    connection.disconnect();
  }
}
let url = URL(string: "https://api.und-gesund.de/v5/userInformation")!

// JSON-String upload data, please refer to above example
// let data = ...

var request = URLRequest(url: url)
request.httpMethod = "PUT"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")

let authCredentials = String("healthapp:A7qmaxf9a").data(using: String.Encoding.utf8)!.base64EncodedString()
request.setValue("Basic \(authCredentials)", forHTTPHeaderField: "Authorization")

let appAuthCredentials = String("FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM").data(using: String.Encoding.utf8)!.base64EncodedString()
request.setValue("Basic \(appAuthCredentials)", forHTTPHeaderField: "AppAuthorization")

request.setValue("de3d1e068537dd927b48988cb6969abe", forHTTPHeaderField: "authenticationToken")

request.httpBody = NSMutableData(data: String(data).data(using: String.Encoding.utf8)!) as Data

let task = URLSession(configuration: URLSessionConfiguration.default).dataTask(with: request) {(data, response, error) in
    if let httpResponse = response as? HTTPURLResponse {
        if httpResponse.statusCode == 204 {
            print("success")
        }
    }
}
task.resume()
# JSON-String upload data, please refer to above example
# data = ...

headers = {
    'content-type': 'application/json',
    'Authorization': 'Basic %s' % base64.b64encode('healthapp:A7qmaxf9a'),
    'AppAuthorization': 'Basic %s' % base64.b64encode('FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM'),
    'authenticationToken': 'de3d1e068537dd927b48988cb6969abe'
}

r = requests.put(
    'https://api.und-gesund.de/v5/userInformation',
    data = data,
    headers = headers
)

if r.status_code == 204:
    print 'success'
$(function () {
  // JSON-String upload data, please refer to above example
  // var data = ...

  $.ajax({
    url: "https://api.und-gesund.de/v5/userInformation",
    type: "PUT",
    headers: {
      "Content-Type": "application/json",
      Authorization: "Basic " + btoa("healthapp:A7qmaxf9a"),
      AppAuthorization:
        "Basic " + btoa("FEh9HQNaa87cwdbB:NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM"),
      authenticationToken: "de3d1e068537dd927b48988cb6969abe",
    },
    data: data,
    statusCode: {
      204: function (data, textStatus, xhr) {
        console.log("success");
      },
    },
  });
});
PUT
https://api.und-gesund.de/v5/userInformation
Header Parameter
Authorization
required
(Basic b64) username:password
AppAuthorization
required
(Basic b64) appID:appSecret
authenticationToken
required
The Thryve acces token generated for your user. Example: de3d1e068537dd927b48988cb6969abe
Query Parameter
height Height in centimeters.
weight Weight in kilograms.
birthdate Birth date in YYYY-MM-dd format.
gender Gender information as String (i.e. male, female, genderless)

Responses

Response Codes Description
204 Upload successful
4xx error code

Cross-Platform-Integration

Thryve maintains most parts of the SDK and Modules for Cross-Platform-Integrations based on ReactNative CLI and Flutter.

ReactNative

//Example integration in javascript
import React from "react";
import { StyleSheet, View, Text, Button } from "react-native";
import { Colors } from "react-native/Libraries/NewAppScreen";
import {
  getAccessToken,
  getDataSourceUrl,
  uploadConstantValue,
  uploadDailyDynamicValue,
} from "react-native-thryve-core-sdk-library";
import {
  isEnabled,
  isHealthKitAvailable,
  startHealthKitIntegration,
  stopHealthKitIntegration,
} from "react-native-thryve-module-apple-health-library";
import {
  isActive,
  isSHealthAvailable,
  startSHealthIntegration,
  stopSHealthIntegration,
} from "react-native-thryve-module-s-health-library";

const appId = "FEh9HQNaa87cwdbB";
const appSecret = "NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM";
const partnerUserId = "reactNativeAndroid";
const language = "en";

const App: () => React$Node = () => {
  return (
    <>
      <View>
        <Button
          onPress={() => {
            getAccessToken(
              appId,
              appSecret,
              partnerUserId,
              language,
              (error, event) => {}
            );
          }}
          title="Get Access Token"
        />
        <Text style={styles.welcome}>iOS ONLY</Text>
        <Button
          onPress={() => {
            startHealthKitIntegration((error, event) => {});
          }}
          title="Start HealthKit integration"
        />
        <Button
          onPress={() => {
            stopHealthKitIntegration((error, event) => {});
          }}
          title="Stop HealthKit integration"
        />
        <Text style={styles.welcome}>Android ONLY</Text>
        <Button
          onPress={() => {
            startSHealthIntegration((error, event) => {});
          }}
          title="Start SHealth integration"
        />
        <Button
          onPress={() => {
            stopSHealthIntegration((error, event) => {});
          }}
          title="Stop SHealth integration"
        />
      </View>
    </>
  );
};
export default App;

Developing ReactNative applications with the ReactNative CLI will result into projects that are usually deployed with Android Studio or Xcode for iOS. Please refer to the direct integration chapters for making use of the Thryve SDKs and modules:

  "dependencies": {
    "react-native-thryve-core-sdk-library": "file:../react-native-thryve-core-sdk-library",
    "react-native-thryve-module-apple-health-library": "file:../react-native-thryve-module-apple-health-library",
    "react-native-thryve-module-commons-library": "file:../react-native-thryve-module-commons-library",
    "react-native-thryve-module-s-health-library": "file:../react-native-thryve-module-s-health-library"
  },

Example of AppleHealth initialization in Objective-C

- (instancetype)init
{
    self = [super init];
    if (self) {
        /// Add additional types here
        types = [NSSet setWithObjects:
                 HKConnectorType.dateOfBirth,
                 HKConnectorType.height,
                 HKConnectorType.stepCount,
                 HKConnectorType.heartRate,
                 HKConnectorType.restingHeartRate,
                 HKConnectorType.sleepAnalysis,
                 nil];
    }
    return self;
}

To integrate the React native SDK please follow the next few steps

Example of SHealth initialization in Java

    public ThryveModuleSHealthLibraryModule(ReactApplicationContext reactContext)
    {
        super(reactContext);
        this.reactContext = reactContext;
        this.mTypeList = new ArrayList<>();
        /*
         * Add data types here
         */
        mTypeList.add(SHealthDataType.STEP_COUNT);
        mTypeList.add(SHealthDataType.SLEEP);
        mTypeList.add(SHealthDataType.SLEEP_STAGE);
        mTypeList.add(SHealthDataType.HEART_RATE);
    }



Flutter

Developing Flutter applications with the Flutter SDK will result into projects that can be deployed with Android Studio or Xcode for iOS. Please refer to the direct integration chapters for a walk-through of the Thryve SDKs and modules:

Example of CoreSDK integration in Dart

class CoreService {
  CoreSdk _coreSdk;

  CoreSDKService({String partnerUserId = 'Dart'}) {
    _coreSdk = CoreSdk(
        appId: 'FEh9HQNaa87cwdbB',
        appSecret: 'NL7nTegPm5DKt8LrBZP62HQz6VNZaGuM',
        partnerUserId: partnerUserId,
        language: 'en');
  }

  Future<String> getAccessToken() async => await _coreSdk.getAccessToken();
}

class YourWidget extends StatelessWidget {
    ...
    @override
    Widget build(BuildContext context) {
      return Container(
        child: FlatButton(
          onPressed: () async {
            final CoreService service = CoreService("FVMW6fp9wnUxKnfekrQduZ96Xt6gemVk");
            final accessToken = await service.getAccessToken();
          }),
        ),
      );
    }
    ...
}

Please refer to our Flutter Sample App for example implementations of the different modules.

To integrate the Flutter SDK please follow the next few steps

All available functions derive from the native SDK's:

The bridging between iOS and Android native code and Dart VM is already implemented appropriately for each platform. For better integration of functions related to get values like UserInformation the plugin already contains data modules inside the model package.