androidreact-nativeexpohealthconnect

Why doesn't Android Health Connect capture any data?


I am building a React Native application with Health Connect to analyze some basic health data (Steps, Distance, FloorsClimbed). initialize() works fine, permissions are granted but readRecords() returns an empty array.

app.json

"android": {
  "permissions": [
    "android.permission.health.READ_STEPS",
    "android.permission.health.READ_DISTANCE",
    "android.permission.health.READ_FLOORS_CLIMBED"
  ],
  ...
}

The Data and access section within the Health Connect settings on my phone shows 'No data' (see snapshots below). Samsung Health app seems to work just fine.

I have Samsung A52 with Android 14. Starting Android 14 (API Level 34), Health Connect is part of the Android Framework. I.e. there's no additional setup necessary (such as installing the Health Connect app).

Any ideas how to solve it? Could it be a configuration on the phone side?

enter image description here

useHealthData.ts

//
// Custom hook to handle integration with Health Connect (Android).
//

import { Platform } from 'react-native';
import { useState, useEffect } from 'react';

// library to integrate with Google's Health Connect API.
import { initialize, requestPermission, readRecords } from 'react-native-health-connect';
import { Permission } from 'react-native-health-connect/lib/typescript/types';
import { TimeRangeFilter } from 'react-native-health-connect/lib/typescript/types/base.types';

type HealthData = {
  steps: number;
  distance: number;
  floors: number;
};

const useHealthData = (date: Date): HealthData => {
  const [steps, setSteps] = useState<number>(0);
  const [distance, setDistance] = useState<number>(0);
  const [floors, setFloors] = useState<number>(0);

  //
  // HealthKit implementation
  //
  const [androidPermissions, setAndroidPermissions] = useState<Permission[]>([]);

  // helper function to check if we have specific permission.
  const hasAndroidPermission = (recordType: string) => {
    return androidPermissions.some((perm) => perm.recordType === recordType);
  };

  // initialize Health Connect.
  useEffect(() => {
    // check if the app is running on Android.
    if (Platform.OS !== 'android') {
      return;
    }

    // initialize the client
    const initializeHealthConnect = async () => {
      const isInitialized = await initialize();

      if (!isInitialized) {
        console.log('Failed to initialize Health Connect');
        return;
      }

      // request permissions
      const grantedPermissions = await requestPermission([
        { accessType: 'read', recordType: 'Steps' },
        { accessType: 'read', recordType: 'Distance' },
        { accessType: 'read', recordType: 'FloorsClimbed' },
      ]);
      console.log('Granted permissions ', { grantedPermissions });

      // we save the permissions in state, to be able to check later what data we have access to.
      setAndroidPermissions(grantedPermissions);
    };

    initializeHealthConnect();
  }, []);

  const getHealthData = async () => {
    // check if we have the required permissions.
    if (
      !hasAndroidPermission('Steps') ||
      !hasAndroidPermission('Distance') ||
      !hasAndroidPermission('FloorsClimbed')
    ) {
      console.log('Error getting permissions');
      return;
    }

    // to read data from health connect, we have to send the filter.
    // for the filter, we create a time range filter for the last 24 hours.
    const timeRangeFilter: TimeRangeFilter = {
      operator: 'between',
      startTime: new Date(date.setHours(0, 0, 0, 0)).toISOString(), // setHours(0, 0, 0, 0) to set the time to 00:00
      endTime: new Date(date.setHours(23, 59, 59, 999)).toISOString(),
    };

    // Steps
    const stepsRecords = await readRecords('Steps', { timeRangeFilter }); // read the Steps records.
    console.log(stepsRecords);
    const totalSteps = stepsRecords.records.reduce((sum, cur) => sum + cur.count, 0); // calculate total number of steps.
    setSteps(totalSteps);

    // Distance
    const distanceRecords = await readRecords('Distance', { timeRangeFilter });
    const totalDistance = distanceRecords.records.reduce(
      (sum, cur) => sum + cur.distance.inMeters,
      0
    );
    setDistance(totalDistance);

    // Floors climbed
    const floorsClimbedRecords = await readRecords('FloorsClimbed', {
      timeRangeFilter,
    });
    const totalFloors = floorsClimbedRecords.records.reduce((sum, cur) => sum + cur.floors, 0);
    setFloors(totalFloors);
  };

  useEffect(() => {
    getHealthData();
  }, [date, androidPermissions);

  return { steps, distance, floors };
};

export default useHealthData;

enter image description here


Solution

  • If you have no data in Health Connect, you won't be able to read it, so the empty arrays sound correct. You can use the Health Connect Toolbox app (which can be downloaded here) to add some test data into Health Connect. Otherwise you can use any other apps that are integrated with the platform to create some real data for you to test with.