New applications bring new features and new features bring change in our lives. In this blog, we will be discussing a feature that can be used in React Native Applications to retrieve and save data from Google Fit Application. After reading this blog, you will be able to integrate your react native application with google fit.
Ever wondered, how can we retrieve data from our fit bands and use it in our custom application? This blog has all the answers. First, we will be discussing what Google Fit application is and what its purpose is.
Google Fit:
Google fit is a health-tracking platform that you can download as an application on your Android or Ios Operating System to maintain a healthy lifestyle by keeping an eye over your daily activities, weight, sleep schedule, etc. Google Fit maintains a heart point factor that determines your physical activity throughout the day and it suggests you perform subsequent activities which will help you reach WHO’s recommended amount of activities for a person.
How does it work?
Now we will discuss the step by step process of integrating the google fit with a react native application.
- Create React Native Application:
First of all, create your react native application using the following command on the power shell:
npx react-native init MyApplication
After this, set up your mobile device/ emulator for debugging purposes and install Android Studio for further usage.
- Run Your Application:
Now, running our application using the following command on power shell:
cd MyApplication
npx react-native run-android
- Install the required Package:
Use the following command to install the required package that is react-native-google-fit. This package helps us to communicate with the google fit application, i.e. helps us to bridge the communication with the google fit.
npm install react-native-google-fit
- Importing the Package:
We can add another screen and perform navigation using stack navigator but here we will be performing our task in the App.js file that is prebuilt when we perform the previous step. In the App.js file, import the package using the following statement:
import GoogleFit, {Scopes} from 'react-native-google-fit';
- Defining State Variables:
After importing the package, we will define the state variables where we will be saving our data fetched from Google Fit. We will be retrieving the following data, hence creating state variables for the following using the hook useState()
.
- Daily Steps
- Heart Rate
- Calories
- Sleep
- Weight
- Blood Pressure
var [dailySteps, setdailySteps] = useState(0);
var [heartRate, setHeartRate] = useState(0);
var [calories, setCalories] = useState(0);
var [hydration, setHydration] = useState(0);
var [sleep, setSleep] = useState(0);
var [weight, setWeight] = useState(0);
var [bloodPressure, setBloodPressure] = useState({});
var [loading, setLoading] = useState(true);
- Getting OAuth 2.0 Client ID:
Now to communicate with the Google Fit API, we need to authorize our application using the OAuth 2.0 Client ID. This is the Client ID that we get after registering our app on https://console.cloud.google.com/apis and after enabling the Google Fit API on our project, we have to create credentials for the OAuth 2.0 Client ID so that we get the user consent for accessing his data.
Setup your client ID on your system using the following manual:
https://developers.google.com/fit/android/get-api-key
- Authorizing User:
Now compile your android application again on Android Studio and then re-run the react-native application. The next step is to authorize the application to use the user’s data. We will first check if the application is already authorized or not. If yes then we will directly start fetching the data but if not then we will have to authorize it.
For authorization, we have to provide options to the GoogleFit.authorize() function in which we define the scopes, which refer to the type of data for which we want to access. Here we can see that we want to read and save activity data for which we added Scopes.FITNESS_ACTIVITY_READ and Scopes.FITNESS_ACTIVITY_READ. These permissions will help us to record the daily step count. Similarly, our permissions help us to perform other operations
const options = {
scopes: [
Scopes.FITNESS_ACTIVITY_READ,
Scopes.FITNESS_ACTIVITY_WRITE,
Scopes.FITNESS_BODY_READ,
Scopes.FITNESS_BODY_WRITE,
Scopes.FITNESS_BLOOD_PRESSURE_READ,
Scopes.FITNESS_BLOOD_PRESSURE_WRITE,
Scopes.FITNESS_BLOOD_GLUCOSE_READ,
Scopes.FITNESS_BLOOD_GLUCOSE_WRITE,
Scopes.FITNESS_NUTRITION_WRITE,
Scopes.FITNESS_SLEEP_READ,
],
};
GoogleFit.checkIsAuthorized().then(() => {
var authorized = GoogleFit.isAuthorized;
console.log(authorized);
if (authorized) {
// if already authorized, fetch data
} else {
// Authentication if already not authorized for a particular device
GoogleFit.authorize(options)
.then(authResult => {
if (authResult.success) {
console.log('AUTH_SUCCESS');
// if successfully authorized, fetch data
} else {
console.log('AUTH_DENIED ' + authResult.message);
}
})
.catch(() => {
dispatch('AUTH_ERROR');
});
}
});
- Retrieving Data:
Now after authorizing, we will define functions that will help us to retrieve data from the Google Fit Application:
Retrieving Daily Steps:
For Retrieving the daily steps or even if we want to find the steps taken for a particular interval of time, say for a week or a month, we can use GoogleFit.getDailyStepCountSamples() to retrieve the steps by providing the following options as arguments. Here, we are retrieving steps for the past week.
We are using the Date() function to get the current date and last week’s date. The getDailyStepCountSamples function requires the date to be in ISO Format, therefore we have to convert it into an ISO String using .toISOString().
var today = new Date();
var lastWeekDate = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate() - 8,
);
const opt = {
startDate: lastWeekDate.toISOString(), // required ISO8601Timestamp
endDate: today.toISOString(), // required ISO8601Timestamp
bucketUnit: 'DAY', // optional - default "DAY". Valid values: "NANOSECOND" | "MICROSECOND" | "MILLISECOND" | "SECOND" | "MINUTE" | "HOUR" | "DAY"
bucketInterval: 1, // optional - default 1.
};
After defining the options to be sent to the function, we will now call the function in another wrapper function using async/await to handle the promise. We check if the result if it is not empty then we set the dailySteps state as the result but if it is then we print it as not found. Here we are also looping through the result as the result that we get is in the form of array
let fetchStepsData = async opt => {
const res = await GoogleFit.getDailyStepCountSamples(opt);
if (res.length !== 0) {
for (var i = 0; i < res.length; i++) {
if (res[i].source === 'com.google.android.gms:estimated_steps') {
let data = res[i].steps.reverse();
dailyStepCount = res[i].steps;
setdailySteps(data[0].value);
}
}
} else {
console.log('Not Found');
}
};
In the resultant variable res, we get the data from three resources that are the “source”: “com.Xiaomi.hm.health”, “source”: “com.google.android.gms:merge_step_deltas” and “source”: “com.google.android.gms:estimated_steps”. Here we are using the third one hence we have kept the condition to check the source. The resultant object that we get is as follows:
{
"source": "com.google.android.gms:estimated_steps",
"steps": [
{
"date": "2021-07-10",
"value": 3613
},
{
"date": "2021-07-11",
"value": 1511
},
{
"date": "2021-07-12",
"value": 1254
},
{
"date": "2021-07-13",
"value": 239
}
],
"rawSteps": [
{
"steps": 3613,
"endDate": 1625942595508,
"startDate": 1625857949465
},
{
"steps": 1511,
"endDate": 1626028389117,
"startDate": 1625983566782
},
{
"steps": 1254,
"endDate": 1626116340348,
"startDate": 1626031762184
},
{
"steps": 239,
"endDate": 1626149101997,
"startDate": 1626116458062
}
]
}
Here we can use the raw steps as well but for simplicity purposes, we are using steps as the raw steps have the Date time values. This is how we can retrieve daily steps in our react native application.
Retrieving Heart Rate:
Similarly, if we want to find the heart rate via Google Fit API, we have to perform authorization for FITNESS_BODY_READ Scope and then will be able to retrieve the heart rate using a similar function that is getHeartRateSamples() while keeping the same options that we kept in the Daily Step Counts as follows:
let fetchHeartData = async opt => {
const res = await GoogleFit.getHeartRateSamples(opt);
let data = res.reverse();
if (data.length === 0) {
setHeartRate('Not Found');
} else {
setHeartRate(data[0].value);
}
};
This function will return results as follows:
[
{
"endDate": "2021-07-13T07:30:00.000Z",
"startDate": "2021-07-13T07:30:00.000Z",
"value": 70,
"day": "Tue"
}
]
Retrieving Calories:
For retrieving the amount of calories that we will have to consume or we can also say that we have left in our body that we have to utilize, we can also use Google Fit API. We can use getDailyCaloriesSamples() function to get the calories keeping the options same as previous using the following function:
let fetchCaloriesData = async opt => {
const res = await GoogleFit.getDailyCalorieSamples(opt);
let data = res.reverse();
if (data.length === 0) {
setCalories('Not Found');
} else {
setCalories(Math.round(data[0].calorie * -1 * 100) / 100);
}
};
Here we are rounding off the calories to 2 decimal digits to enhance visualization. Here we get the result in the form of an array containing calories of each day from the start day to the end day. Sample output is as follows:
[
{
"calorie": 132.2113037109375,
"endDate": "2021-07-07T19:00:00.000Z",
"startDate": "2021-07-06T19:00:00.000Z",
"day": "Wed"
},
{
"calorie": 59.9029541015625,
"endDate": "2021-07-06T19:00:00.000Z",
"startDate": "2021-07-05T19:00:00.000Z",
"day": "Tue"
},
{
"calorie": 9.9998779296875,
"endDate": "2021-07-05T19:00:00.000Z",
"startDate": "2021-07-04T19:00:00.000Z",
"day": "Mon"
}
]
Retrieving Sleep:
To retrieve the hours you have slept during the day or throughout the week can also be determined using the getSleepSamples() with the same options as previous. Observe that we want the data for the current day, therefore we want the sleep from the last midnight to the current time, hence we have used the midnight and further processing to calculate the hours of sleep. We want the result in hours that is why we have divided the result by 1000 x 60 x 60. If we wanted the results in minutes, we would have to divide the result by 1000 x 60 only.
let fetchSleepData = async opt => {
var midnight = new Date();
midnight.setHours(0, 0, 0, 0);
let sleepTotal = 0;
const res = await GoogleFit.getSleepSamples(opt);
for (var i = 0; i < res.length; i++) {
if (new Date(res[i].endDate) > Date.parse(midnight)) {
if (new Date(res[i].startDate) > Date.parse(midnight)) {
sleepTotal +=
Date.parse(res[i].endDate) - Date.parse(res[i].startDate);
} else {
sleepTotal += Date.parse(res[i].endDate) - Date.parse(midnight);
}
if (
i + 1 < res.length &&
Date.parse(res[i].startDate) < Date.parse(res[i + 1].endDate)
) {
sleep total -=
Date.parse(res[i + 1].endDate) - Date.parse(res[i].startDate);
}
}
}
setSleep(Math.round((sleepTotal / (1000 * 60 * 60)) * 100) / 100);
};
The raw form of data that we receive from the API is as follows:
{
"granularity": [],
"endDate": "2021-07-06T05:41:00.000Z",
"startDate": "2021-07-05T09:41:03.845Z",
"addedBy": "com.google.android.apps.fitness"
},
{
"granularity": [],
"endDate": "2021-07-06T09:31:49.052Z",
"startDate": "2021-07-05T11:31:00.000Z",
"addedBy": "com.google.android.apps.fitness"
},
{
"granularity": [],
"endDate": "2021-07-07T02:37:00.000Z",
"startDate": "2021-07-06T20:47:00.000Z",
"addedBy": "com.google.android.apps.fitness"
}
You might be thinking what is this granularity? Google fit saves the sleep data in two formats. One is the raw data in which sleep can be measured by the timestamps and the other one is the granular data which will help us to denote the type of sleep in terms of the following:
Awake (during sleep cycle) | 1 |
Sleep | 2 |
Out-of-bed | 3 |
Light sleep | 4 |
Deep sleep | 5 |
REM | 6 |
Retrieving Weights:
To retrieve the current weight into the react native application, we have to define the options again because of the unit that we have to define I the options as “Kg” or “Pounds” as follows:
var today = new Date();
var lastWeekDate = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate() - 8,
);
const opt = {
startDate: lastWeekDate.toISOString(), // required ISO8601Timestamp
endDate: today.toISOString(),
unit: 'kg', // required; default 'kg'
bucketUnit: 'DAY', // optional - default "DAY". Valid values: "NANOSECOND" | "MICROSECOND" | "MILLISECOND" | "SECOND" | "MINUTE" | "HOUR" | "DAY"
bucketInterval: 1, // optional - default 1.
ascending: false, // optional; default false
};
Now using the getWeightSamples() function to retrieve the weights recorded in the previous week but we will only keep the latest one.
let fetchWeightData = async (opt) => {
const res = await GoogleFit.getWeightSamples(opt);
let data = res.reverse();
setWeight(Math.round(data[0].value * 100) / 100);
};
The result that we get in raw format is as follows but we will only keep the latest one that is 40Kg.
[
{
"addedBy": "com.foliofit",
"endDate": "2021-07-09T07:27:06.161Z",
"startDate": "2021-07-09T07:27:06.161Z",
"value": 40,
"day": "Fri"
},
{
"addedBy": "com.foliofit",
"endDate": "2021-07-08T05:51:28.352Z",
"startDate": "2021-07-08T05:51:28.352Z",
"value": 100,
"day": "Thu"
},
{
"addedBy": "com.foliofit",
"endDate": "2021-07-07T05:45:10.992Z",
"startDate": "2021-07-07T04:53:55.683Z",
"value": 46.48557662963867,
"day": "Wed"
}
]
Retrieving Blood Pressure:
Blood Pressure contains two values, one is the Systolic and the other one is Diastolic. We can retrieve both of these values from Google Fit using getBloodPressureSamples()
function in an async/await wrapper function as follows:
let fetchBloodPressure = async opt => {
const res = await GoogleFit.getBloodPressureSamples(opt);
let data = res.reverse();
if (res.length === 0) {
setBloodPressure({diastolic: 'Not Found', systolic: 'Not Found'});
} else {
setBloodPressure({
diastolic: data[0].diastolic,
systolic: data[0].systolic,
});
}
};
Here also in the raw format, we get the blood pressure for the whole week but we will only keep the latest ones. The raw result is as follows:
[
{
"endDate": "2021-07-13T06:46:04.921Z",
"startDate": "2021-07-13T06:46:04.921Z",
"systolic": 116,
"diastolic": 76,
"day": "Tue"
},
{
"endDate": "2021-07-06T08:31:10.633Z",
"startDate": "2021-07-06T08:31:10.633Z",
"systolic": 120,
"diastolic": 80,
"day": "Tue"
},
{
"endDate": "2021-07-05T00:40:07.108Z",
"startDate": "2021-07-05T00:40:07.108Z",
"systolic": 120,
"diastolic": 80,
"day": "Mon"
}
]
- Calling the Retrieval Functions On Load:
Now after studying and defining all the retrieval functions, we have to call them in the effect() hook so that as soon as we load our application, the data gets retrieved. But as we have to perform authorization whenever the application is loaded, therefore we have to make a wrapper function where we will first authorize the device and then call the asynchronous retrieval functions.
We will call that wrapper function in the useEffect()
hook instead as follows:
useEffect(() => {
getAllDataFromAndroid();
}, []);
- Displaying the Results on the interface:
As we have saved the retrieved data in the state variables, therefore, we will use those state variables to display the results on the user interface.
Firstly we will perform the following imports:
import {Text, StyleSheet, View} from 'react-native';
Now creating the required Stylesheet to style our components:
const styles = StyleSheet.create({
row: {
flexDirection: 'row',
height: 30,
margin: 10,
marginTop: 12,
},
rowBlue: {
padding: 2,
},
row_1: {
flex: 1,
},
row_2: {
flex: 2,
},
containerBlue: {
marginTop: 10,
height: 50,
backgroundColor: '#187FA1',
color: 'white',
},
containerWhite: {
marginTop: 10,
height: 50,
backgroundColor: 'white',
color: '#187FA1',
},
textContainerBlue: {
paddingTop: 15,
paddingLeft: 15,
color: 'white',
},
textContainerWhite: {
paddingTop: 15,
paddingLeft: 70,
color: '#187FA1',
},
});
Now we will define the display content for the application in the return statement as follows:
<View style={[{flex: 1}]}>
<View style={styles.row}>
<View style={[styles.row_2, styles.containerBlue]}>
<Text style={styles.textContainerBlue}>Step Count - Today</Text>
</View>
<View style={[styles.row_2, styles.containerWhite]}>
<Text style={styles.textContainerWhite}>{dailySteps}</Text>
</View>
</View>
<View style={styles.row}>
<View style={[styles.row_2, styles.containerBlue]}>
<Text style={styles.textContainerBlue}>Heart Rate</Text>
</View>
<View style={[styles.row_2, styles.containerWhite]}>
<Text style={styles.textContainerWhite}>{heartRate}</Text>
</View>
</View>
<View style={styles.row}>
<View style={[styles.row_2, styles.containerBlue]}>
<Text style={styles.textContainerBlue}>BP- Systolic </Text>
</View>
<View style={[styles.row_2, styles.containerWhite]}>
<Text style={styles.textContainerWhite}>
{bloodPressure.systolic}
</Text>
</View>
</View>
<View style={styles.row}>
<View style={[styles.row_2, styles.containerBlue]}>
<Text style={styles.textContainerBlue}>BP - Diastolic </Text>
</View>
<View style={[styles.row_2, styles.containerWhite]}>
<Text style={styles.textContainerWhite}>
{bloodPressure.diastolic}
</Text>
</View>
</View>
<View style={styles.row}>
<View style={[styles.row_2, styles.containerBlue]}>
<Text style={styles.textContainerBlue}>Calories</Text>
</View>
<View style={[styles.row_2, styles.containerWhite]}>
<Text style={styles.textContainerWhite}>{calories}</Text>
</View>
</View>
<View style={styles.row}>
<View style={[styles.row_2, styles.containerBlue]}>
<Text style={styles.textContainerBlue}>Sleep - Today</Text>
</View>
<View style={[styles.row_2, styles.containerWhite]}>
<Text style={styles.textContainerWhite}>{sleep} hours</Text>
</View>
</View>
<View style={styles.row}>
<View style={[styles.row_2, styles.containerBlue]}>
<Text style={styles.textContainerBlue}>Weight</Text>
</View>
<View style={[styles.row_2, styles.containerWhite]}>
<Text style={styles.textContainerWhite}>{weight} Kg</Text>
</View>
</View>
</View>
Now run your application again and here you go, you have integrated the Google Fit Application with your react native application. Furthermore, you can also design different screens for retrieving data and also add a loader to show the loading status while the data is being fetched.
Conclusion:
Using APIs has helped us in very critical situations where we do not have much time to develop our own features and those features are already available in other applications. In our situation, we wanted to communicate with the Google Fit API in a react native application and we accomplished this using react-native-google-fit package. We can further deduce more information regarding a person’s health using these results and help him to live a better life.