spike_flutter_sdk 1.2.10 copy "spike_flutter_sdk: ^1.2.10" to clipboard
spike_flutter_sdk: ^1.2.10 copied to clipboard

Spike Health data reader for Android and iOS platforms.

Spike Flutter SDK allows you to read the Apple HealthKit and Android Health Connect data. Then, it allows you to send the data to your backend chosen, and get the data returned which is processed by the backend. Also, you can enable background delivery in case iOS device is being used. And finally, it is possible to enable event tracking to see if everything works as intended or just provide your own event tracking library.

Features #

  1. Read the Apple HealthKit data.
  2. Send this data to backend.
  3. Register periodic background tasks to send and read the data.
  4. Track various events of the package by utilizing shared preferences.

SDK version update guide #

iOS Considerations #

Sometimes we need to update the native SDK version regarding iOS part. It is always recommended to call pod update command from the Flutter's iOS folder when you change the Flutter SDK version.

Getting started #

iOS Setup Guide #

At first in your app's entitlements select HealthKit and in your app's info.plist file add permissions:

<key>NSHealthShareUsageDescription</key>
<string>WHY_YOU_NEED_TO_SHARE_DATA</string>
<key>NSHealthUpdateUsageDescription</key>
<string>WHY_YOU_NEED_TO_USE_DATA</string>

If you plan to use WorkoutRoute Series please provide additionally CoreLocation permissions:

<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>WHY_YOU_NEED_TO_ALWAYS_SHARE_LOCATION</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>WHY_YOU_NEED_TO_SHARE_LOCATION</string>

Note. You must configure XCode appropriately. You should add the following capability: Background Delivery and Clinical Health Records from the HealthKit. This is crucial, since otherwise you will be unable to extract the iOS data.

Another note. Each time you see in the updates CHANGELOG that iOS Native SDK version was updated, you should run the following command from your project's iOS folder:

pod update

Otherwise, you risk your app to be not working as native code changes might result in changes required for you as a client.

Android Setup Guide #

Note. All these steps ARE COMPLETELY NECESSARY! However, if you want this to work with Android 14, you MUST VERY CAREFULLY READ Android 14 Setup Guide, and execute the steps written there exactly as they are written.

First of all you have to add the required health permissions to a resource file. You can reuse an existing one or create a new one. That allows you to fully utilise this SDK and to read the necessary data from apps that integrate with HealthConnect.

  1. Create config.xml in the following path: android/app/src/main/res/values. Content of this file must be the following:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<array  name="health_permissions">
<item>androidx.health.permission.Nutrition.READ</item>
<item>androidx.health.permission.ActiveCaloriesBurned.READ</item>
<item>androidx.health.permission.TotalCaloriesBurned.READ</item>
<item>androidx.health.permission.Steps.READ</item>
<item>androidx.health.permission.Distance.READ</item>
<item>androidx.health.permission.ElevationGained.READ</item>
<item>androidx.health.permission.RestingHeartRate.READ</item>
<item>androidx.health.permission.HeartRateVariabilityRmssd.READ</item>
<item>androidx.health.permission.FloorsClimbed.READ</item>
<item>androidx.health.permission.BasalMetabolicRate.READ</item>
<item>androidx.health.permission.SleepSession.READ</item>
<item>androidx.health.permission.HeartRate.READ</item>
<item>androidx.health.permission.ExerciseSession.READ</item>
<item>androidx.health.permission.Speed.READ</item>
<item>androidx.health.permission.Power.READ</item>
<item>androidx.health.permission.OxygenSaturation.READ</item>
<item>androidx.health.permission.BloodGlucose.READ</item>
<item>androidx.health.permission.RespiratoryRate.READ</item>
<item>androidx.health.permission.Weight.READ</item>
<item>androidx.health.permission.Height.READ</item>
<item>androidx.health.permission.BodyFat.READ</item>
<item>androidx.health.permission.LeanBodyMass.READ</item>
<item>androidx.health.permission.BodyWaterMass.READ</item>
<item>androidx.health.permission.BodyTemperature.READ</item>
<item>androidx.health.permission.BloodPressure.READ</item>
<item>androidx.health.permission.BoneMass.READ</item>
</array>
</resources>
  1. Now comes the AndroidManifest.xml modification part. Add the following query in the
<queries>
<package android:name="com.google.android.apps.healthdata" />
</queries>
  1. Then reference the permissions to your AndroidManifest.xml file in the activity that will call for health permissions. This meta-data reference should be added under the
<meta-data
  android:name="health_permissions"
  android:resource="@array/health_permissions"  />
  1. As well as that you have to add intent filter to your activity definition so that you can request the permissions at runtime. This intent filter reference should be added under the
<intent-filter>
<action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
</intent-filter>
  1. In case permissions handling is not working, this might be related to launch mode being singleTop. This might be not needed, but some apps faced problems when requesting permissions. If you face them, then you should try removing the following line:
android:launchMode="singleTop"
  1. In case app is not building, it might be related to label replacement issue. In this case, you should add the following line to the
tools:replace="android:label"
  1. Afterwards, add the following line into the android/app/build.gradle under dependencies:
implementation 'androidx.appcompat:appcompat:1.3.1'
  1. Since we are using closed source native Android SDK, separate repository is needed. Thus, add the following dependency into your android/builde.gradle file (it must be added both in repositories and allprojects node of repositories):

maven {
  url 'https://gitlab.com/api/v4/projects/43396247/packages/maven'
}

  1. Finally, in local.properties of your project for Flutter, add these lines:
flutter.minSdkVersion=28
flutter.compileSdkVersion=34
  1. In case your default Flutter project does not take these values, do the necessary changes in the android/app/build.gradle:
def flutterCompileSdkVersion = localProperties.getProperty('flutter.compileSdkVersion')
if (flutterCompileSdkVersion == null) {
  flutterCompileSdkVersion = '34'
}

  1. And then, add or change compileSdkVersion under android node like this:
compileSdkVersion flutterCompileSdkVersion.toInteger()
  1. In case you face an issue not described here, feel free to contact us, and give us as much information as possible for us to provide more information about how to resolve your specific issue. The problems sometimes are caused by just different environments, and there is no way to test all of them when there are so many of them.

Android 14 Setup Guide #

  1. You must make sure you have followed "Android Setup Guide", and it works completely with devices before Android 14. Otherwise, these steps might not work, because you did not set-up initially.

  2. Check the contents of android/app/src/main/kotlin/{YOUR_PACKAGE_ID}/MainActivity.kt. You must see something like that in case it is the new app being developed:

import io.flutter.embedding.android.FlutterActivity

class MainActivity: FlutterActivity() {
}

The main thing here is that you must change FlutterActivity to the FlutterFragmentActivity which means that you code should turn into the result similar to the one below:

import io.flutter.embedding.android.FlutterFragmentActivity

class MainActivity: FlutterFragmentActivity() {
}
  1. In the Health Connect permissions activity there is a link to your privacy policy. You need to grant the Health Connect app access in order to link back to your privacy policy. In the example below, you should either replace .MainActivity with an activity that presents the privacy policy or have the Main Activity route the user to the policy. This step may be required to pass Google app review when requesting access to sensitive permissions.
<activity-alias
     android:name="ViewPermissionUsageActivity"
     android:exported="true"
     android:targetActivity=".MainActivity"
     android:permission="android.permission.START_VIEW_PERMISSION_USAGE">
        <intent-filter>
            <action android:name="android.intent.action.VIEW_PERMISSION_USAGE" />
            <category android:name="android.intent.category.HEALTH_PERMISSIONS" />
        </intent-filter>
</activity-alias>
  1. Android 14 uses different model for the Health permissions. So you must add the following permissions for requesting into the AndroidManifest.xml:
<!-- Health permissions -->
<uses-permission android:name="android.permission.health.READ_SLEEP "/>
<uses-permission android:name="android.permission.health.READ_WEIGHT"  />
<uses-permission android:name="android.permission.health.READ_ACTIVE_CALORIES_BURNED"  />
<uses-permission android:name="android.permission.health.READ_BASAL_METABOLIC_RATE"  />
<uses-permission android:name="android.permission.health.READ_BLOOD_GLUCOSE"  />
<uses-permission android:name="android.permission.health.READ_BLOOD_PRESSURE"  />
<uses-permission android:name="android.permission.health.READ_BODY_FAT"  />
<uses-permission android:name="android.permission.health.READ_BONE_MASS"  />
<uses-permission android:name="android.permission.health.READ_EXERCISE"  />
<uses-permission android:name="android.permission.health.READ_DISTANCE"  />
<uses-permission android:name="android.permission.health.READ_ELEVATION_GAINED"  />
<uses-permission android:name="android.permission.health.READ_FLOORS_CLIMBED"  />
<uses-permission android:name="android.permission.health.READ_HEART_RATE"  />
<uses-permission android:name="android.permission.health.READ_HEART_RATE_VARIABILITY"  />
<uses-permission android:name="android.permission.health.READ_HEIGHT"  />
<uses-permission android:name="android.permission.health.READ_OXYGEN_SATURATION"  />
<uses-permission android:name="android.permission.health.READ_POWER"  />
<uses-permission android:name="android.permission.health.READ_RESPIRATORY_RATE"  />
<uses-permission android:name="android.permission.health.READ_RESTING_HEART_RATE"  />
<uses-permission android:name="android.permission.health.READ_SPEED"  />
<uses-permission android:name="android.permission.health.READ_STEPS"  />
<uses-permission android:name="android.permission.health.READ_TOTAL_CALORIES_BURNED"  />
<uses-permission android:name="android.permission.health.READ_BODY_TEMPERATURE" />
<uses-permission android:name="android.permission.health.READ_BASAL_METABOLIC_RATE" />

Spike SDK Usage #

The examples below demonstrate how to use this package in various of cases.

Initialization #

First, you must get the correct credentials in order for this package to be usable. In case you want to test web hooks, but do not have or want to set-up your own environment, you can try the following free service: https://webhook.site


const authToken = '{YOUR_AUTH_TOKEN}';

const appId = '{YOUR_APP_ID}';

const customerEndUserId = '{YOUR_CUSTOMER_END_USER_ID}';

const postbackURL = 'https://example.com/';

Creating Spike connection #

Using the credentials received, you can create the connection. This is done in the following way.

final connection = await  SpikeSDK.createConnection(
  appId: appId,
  authToken: authToken,
  customerEndUserId: customerEndUserId,
  postbackURL: postbackURL, // Optional, provides functionality to send data to webhook and use background deliveries.
  logger: const  PrintLogger() // Optional, class which conforms to SpikeLogger interface
);

Creating Spike connection (For the specified environment) #

Using the credentials received, you can create the connection. This is done in the following way.

final connection = await  SpikeSDK.createConnection(
  appId: appId,
  authToken: authToken,
  customerEndUserId: customerEndUserId,
  postbackURL: postbackURL, // Optional, provides functionality to send data to webhook and use background deliveries.
  logger: const  PrintLogger(), // Optional, class which conforms to SpikeLogger interface
);

Also, you can add a logger that logs the most important events in the connection. First, you would need to implement the SpikeLogger abstract class. Example of how to do so is below.


import  'package:spike_flutter_sdk/spike_flutter_sdk.dart';



class  PrintLogger  extends  SpikeLogger {

const  PrintLogger();



// You specify if you want to receive debug messages through this logger.

@override

bool  get isDebugEnabled => true;



// You specify if you want to receive error messages through this logger.

@override

bool  get isErrorEnabled => true;



// You specify if you want to receive info messages through this logger.

@override

bool  get isInfoEnabled => true;



@override

void  debug(SpikeConnection connection, String message) =>

_log(connection, message, 'debug');



@override

void  error(SpikeConnection connection, String message) =>

_log(connection, message, 'error');



@override

void  info(SpikeConnection connection, String message) =>

_log(connection, message, 'info');



/*

* Of course, in the production apps using 'print' is an antipattern in Flutter,

* but this is intended to give you an idea how to use logger, and also to have

* a compilable version of the logger to get you started as fast as possible.

*/

void  _log(SpikeConnection connection, String message, String level) =>

print('[$level] [${connection.connectionId}]: $message');

}

Requesting permissions #

In order to use the connection properly, you must first ask for the permissions needed for your purposes. Let us assume you want to get heart data. For this, you must to ask for heart data permissions. See the example below about how easy it is to do so.

connection.ensurePermissionsAreGrantedV2(types: [SpikeDataType.heart]);

Just one line of code, and you are requesting the permissions.

Note. During the next release, this method is going to be changed into 'ensurePermissionsAreGranted' as this way of returning the result is more preferable.

Requesting permissions (before 1.2.0) #

In order to use the connection properly, you must first ask for the permissions needed for your purposes. Let us assume you want to get heart data. For this, you must to ask for heart data permissions. See the example below about how easy it is to do so.

connection.ensurePermissionsAreGranted(types: [SpikeDataType.heart]);

Just one line of code, and you are requesting the permissions.

Note. During the next release, this method is going to change the return type.

Requesting permissions (additional considerations) #

It is worth to mention that Health Kit does not support feedbacking about if user actually gave requested permissions or not, so the return type is always going to be SpikePermissionsResult.undefined. However, on Android Health Connect gives us more information, but in a very limited way. For instance, if user cancels giving permissions two times, then Health Connect app is never going to open again for the user until you call 'revokeAllPermissions' (or clear your app or Health Connect storage). In the table below, we describe potential return types for the SpikePermissionsResult enum:

Method Platforms Description
SpikePermissionsResult.granted Android It means all the requested permissions for the specified Spike data type have been given.
SpikePermissionsResult.grantedPartially Android It means that user gave only a subset of permissions for your app regarding Health Connect data, but not all the data required for the Spike data type
SpikePermissionsResult.canceled Android User did not give any permissions at all.
SpikePermissionsResult.blocked Android User cancelled giving permissions two times, and Health Connect blocked your app. You either should call revokeAllPermissions() or ask your users to clear the app storage (or storage of the Health Connect).
SpikePermissionsResult.undefined iOS This is going to be returned on iOS in all cases, since there is no way to check if user gave permissions or not for the Health Kit.

Using the connection to get the data (without specified date range) #

Using both simple and Web Hook connections are very easy to use. See the examples below how to use both simple and WebHook connections without date range specified.


final stepsData = await connection.extractData(SpikeDataType.steps);

if (stepsData.data.isNotEmpty) {

print("Steps (Count): ${stepsData.data.length}");

print("Steps[0]: ${stepsData.data[0].value}");

}



final heartJobDetails = await connection.extractAndPostData(SpikeDataType.heart);

print('submittedAt: ${heartJobDetails.submittedAt}');

print('notificationId: ${heartJobDetails.notificationId}');

Using the connection to get the data (with specified date range) #

Also, you are allowed to specify the date range wanted. Flutter does not use extractDataInRange or something like that, since Dart gives us a way to use optional parameters, and this feature if Dart is being used here. See the examples below:


final now = DateTime.now()

final stepsData = await connection.extractData(

SpikeDataType.steps,

from: now.subtract(const  Duration(days: 1)),

to: now,

);



if (stepsData.data.isNotEmpty) {

print("Steps (Count): ${stepsData.data.length}");

print("Steps[0]: ${stepsData.data[0].value}");

}



final heartJobDetails = await connection.extractAndPostData(

SpikeDataType.heart,

from: now.subtract(const  Duration(days: 1)),

to: now,

);

print('submittedAt: ${heartJobDetails.submittedAt}');

print('notificationId: ${heartJobDetails.notificationId}');

Configure for background deliveries (WebHook connection and iOS only) #

Under your project Signing & Capabilities section enable Background Delivery for HealthKit. Call Spike configure methods on each app start to trigger background deliveries tasks. Add Spike initialization code to ios/Runner/AppDelegate.swift file. The code should look like the one below.

import UIKit
import Flutter
import SpikeSDK

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
    Spike.configure()

    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

Enabling background delivery (WebHook connection and iOS only) #

Enabling the background delivery should be straightforward as shown below.

await connection.enableBackgroundDelivery(
  types: [SpikeDataType.summary, SpikeDataType.heart],
);

Disabling background delivery (WebHook connection and iOS only) #

Disabling the background delivery should be straightforward as shown below.

await connection.disableBackgroundDelivery();

Creating listener for the background delivery and using it (WebHook connection and iOS only) #

  1. In case you want to listen what happens with background delivery, you can implement SpikeWebhookConnectionListener, and provide your implementation as listener for the background delivery. Below, there is an example of how to implement SpikeWebhookConnectionListener.

import  'package:spike_flutter_sdk/spike_flutter_sdk.dart';



class  PrintBackgroundListener  extends  SpikeWebhookConnectionListener {

const  PrintBackgroundListener();



@override

void  onBackgroundLog(String log) => print(log);

}

  1. To listen for the events for the background delivery, you should not just enable it, but also set the listener. Example below shows how to both enable background delivery and set listener to it:

await connection.enableBackgroundDelivery(

types: [SpikeDataType.summary, SpikeDataType.heart],

);

connection.setListener(const  PrintBackgroundListener());

Checking the background delivery types (WebHook connection and iOS only) #

Sometimes you may want to check for which types the connection are registered for the background delivery. You may use the following example in order to recieve the types for the background delivery.


final types = await connection.getBackgroundDeliveryTypes();

Get Spike End User ID #

Use the following.


final result = await connection.getSpikeEndUserId();

Errors and Exceptions #

SpikeException #

The abstract parent of all concrete exceptions.

SpikeConnectionIsClosedException #

Thrown if any method is invoked on SpikeConnection or one of its subclasses after the connection has been closed.

SpikeInvalidCredentialsException #

Thrown if the credentials provided are not recognised, i.e. are invalid or expired.

SpikeInvalidDateRangeException #

Thrown if duration exceeds the allowed maximum or if ‘from’ date is greater than ‘to’ date. The message will contain a specific cause description.

SpikeInvalidPostbackUrlException #

Thrown if the callback URL is not a valid URL, does not have HTTPS schema, or is not a sub-URL of the main callback URL configured for the application. The exception message will contain a specific reason.

SpikePackException #

Thrown when a connection cannot be serialised to a packed state. The exception cause will contain the underlying error.

SpikePermissionsNotGrantedException #

Thrown when Spike SDK is unable to access the data due to permissions not being granted.

SpikeServerException #

Whenever any operation with the Spike backend fails and the error does not fall into any of the above exceptions. The exception cause will contain the underlying error.

SpikeUnpackException #

Thrown when a connection cannot be deserialised from a packed state. The exception cause will contain the underlying error.

Flutter Spike SDK Classes and Information #

Spike Data Types #

  • SpikeDataType.activitiesStream
  • SpikeDataType.activitiesSummary
  • SpikeDataType.body
  • SpikeDataType.breathing
  • SpikeDataType.calories
  • SpikeDataType.distance
  • SpikeDataType.glucose
  • SpikeDataType.heart
  • SpikeDataType.oxygenSaturation
  • SpikeDataType.sleep
  • SpikeDataType.steps

SpikeSDK #

This is static class intended to be used in order to create new connections, check for existing connections, and so on.

Methods

Method Platforms Description
createConnection iOS, Android Creates a new SpikeConnection instance with the given user details.
getBackgroundConnections iOS Returns all connections that are configured to deliver data in the background.
isPackageInstalled Android Check if Health Connect installed on a device.

SpikeConnection #

This is the main class that is used for managing the Spike connection. It is not allowed to instantiate it directly, thus use SpikeSDK.createConnection.

Methods

Method Platforms Description
manageHealthConnect Android Opens Health Connect app (Android only)
ensurePermissionsAreGranted iOS, Android Use this in order to request permissions from the user
revokeAllPermissions Android Revokes all permissions (Android only)
close iOS, Android Terminates any ongoing connections with Spike’s backend servers, clears any caches, and removes provided user details and tokens from the memory. Once the connection is closed, it cannot be used, and any method other than close() will throw a SpikeConnectionIsClosedException exception.
extractData iOS, Android Extracts local device data for the current date in the end-user’s time zone. Optionally time range can be provided. On iOS, the maximum allowed single-query time interval is 90 days. If required, data of any longer time period can be accessed by iterating multiple queries of 90 days. On Android the earliest date data can be requested for is 30 days before a permission was given to the user's app to read from HealthConnect. There is no limit on how many days in total.
extractAndPostData iOS, Android Extracts local device data for the current date in the local user time zone and sends it as a webhook notification to the customer’s backend. Optionally time range can be provided. On iOS, the maximum allowed single-query time interval is 90 days. If required, data of any longer time period can be accessed by iterating multiple queries of 90 days. On Android the earliest date data can be requested for is 30 days before a permission was given to the user's app to read from HealthConnect. There is no limit on how many days in total.
getSpikeEndUserId iOS, Android Retrieves the unique identifier assigned to the end-user by Spike.
setListener iOS, Android Sets a listener that is to handle notifications from the background delivery process.
5
likes
120
pub points
71%
popularity

Publisher

verified publisherspikeapi.com

Spike Health data reader for Android and iOS platforms.

Homepage

Documentation

API reference

License

MIT (LICENSE)

Dependencies

flutter, get_it, shared_preferences, update

More

Packages that depend on spike_flutter_sdk