spike_flutter_sdk 1.0.21
spike_flutter_sdk: ^1.0.21 copied to clipboard
Reads HealthKit data from iOS HealthKit, and syncs it with Spike backend.
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 #
- Read the Apple HealthKit data.
- Send this data to backend.
- Register periodic background tasks to send and read the data.
- Track various events of the package by utilizing shared preferences.
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>
Android Setup Guide #
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.
- 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>
- Now comes the
AndroidManifest.xml
modification part. Add the following query in the
<queries>
<package android:name="com.google.android.apps.healthdata" />
</queries>
- 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" />
- 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>
- 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"
- 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"
- Afterwards, add the following line into the android/app/build.gradle under dependencies:
implementation 'androidx.appcompat:appcompat:1.3.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'
}
- Finally, in local.properties of your project for Flutter, add these lines:
flutter.minSdkVersion=28
flutter.compileSdkVersion=33
- 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 = '33'
}
- And then, add or change compileSdkVersion under android node like this:
compileSdkVersion flutterCompileSdkVersion.toInteger()
- 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.
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
);
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.ensurePermissionsAreGranted(types: [SpikeDataType.heart]);
Just one line of code, and you are requesting the permissions.
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}');
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],
);
Creating listener for the background delivery and using it (WebHook connection and iOS only) #
- 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);
}
- 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();