react-native-healthkit-bridge
Version:
A comprehensive React Native bridge for Apple HealthKit with TypeScript support, advanced authorization, and flexible data queries
189 lines (162 loc) • 5.26 kB
text/typescript
import { useCallback, useEffect, useState } from 'react';
import { HealthKitBridge } from '../core/HealthKitBridge';
import { HealthKitQuantity, HealthKitCategoryData, HealthKitWorkout } from '../core/types/HealthKitTypes';
// Singleton instance of HealthKitBridge with proper cleanup
let healthKitInstance: HealthKitBridge | null = null;
function getHealthKitBridge(): HealthKitBridge {
if (!healthKitInstance) {
healthKitInstance = new HealthKitBridge();
}
return healthKitInstance;
}
// Cleanup function for testing and memory management
export function cleanupHealthKitBridge(): void {
healthKitInstance = null;
}
interface UseHealthKitResult<T> {
data: T | null;
loading: boolean;
error: string | null;
refetch: () => void;
}
// Generic hook for quantitative data
export function useHealthKitQuantity(
identifier: string,
unit: string
): UseHealthKitResult<HealthKitQuantity[]> {
const [data, setData] = useState<HealthKitQuantity[] | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const fetch = useCallback(async () => {
setLoading(true);
setError(null);
try {
const result = await getHealthKitBridge().getQuantitySamples(identifier, unit);
// Convert to HealthKitQuantity format
const formattedData = result.map((sample, index) => ({
value: sample.value,
unit,
startDate: Date.now() / 1000 - (index * 86400),
endDate: Date.now() / 1000 - (index * 86400) + 86400
}));
setData(formattedData);
} catch (e: any) {
setError(e?.message || 'Error fetching data');
} finally {
setLoading(false);
}
}, [identifier, unit]);
useEffect(() => {
fetch();
}, [fetch]);
return { data, loading, error, refetch: fetch };
}
// Generic hook for categorical data
export function useHealthKitCategory(
identifier: string
): UseHealthKitResult<HealthKitCategoryData[]> {
const [data, setData] = useState<HealthKitCategoryData[] | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const fetch = useCallback(async () => {
setLoading(true);
setError(null);
try {
const result = await getHealthKitBridge().getCategorySamples(identifier);
setData(result);
} catch (e: any) {
setError(e?.message || 'Error fetching data');
} finally {
setLoading(false);
}
}, [identifier]);
useEffect(() => {
fetch();
}, [fetch]);
return { data, loading, error, refetch: fetch };
}
// Hook for workouts
export function useHealthKitWorkouts(): UseHealthKitResult<HealthKitWorkout[]> {
const [data, setData] = useState<HealthKitWorkout[] | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const fetch = useCallback(async () => {
setLoading(true);
setError(null);
try {
const result = await getHealthKitBridge().getWorkoutsGeneric();
setData(result);
} catch (e: any) {
setError(e?.message || 'Error fetching data');
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
fetch();
}, [fetch]);
return { data, loading, error, refetch: fetch };
}
// Hook for available types
export function useHealthKitTypes() {
const [types, setTypes] = useState<any[] | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const fetchTypes = useCallback(async () => {
setLoading(true);
setError(null);
try {
const result = await getHealthKitBridge().getAvailableTypes();
setTypes(result);
return result;
} catch (e: any) {
const errorMessage = e?.message || 'Error fetching available types';
setError(errorMessage);
return [];
} finally {
setLoading(false);
}
}, []);
const getTypeInfo = useCallback(async (identifier: string) => {
setLoading(true);
setError(null);
try {
const result = await getHealthKitBridge().getTypeInfo(identifier as any);
return result;
} catch (e: any) {
const errorMessage = e?.message || 'Error fetching type information';
setError(errorMessage);
return null;
} finally {
setLoading(false);
}
}, []);
return {
types,
loading,
error,
fetchTypes,
getTypeInfo
};
}
// Hook for provider information
export function useHealthKitProvider() {
const [providerInfo, setProviderInfo] = useState<any>(null);
const [availableProviders, setAvailableProviders] = useState<string[]>([]);
const [allProvidersInfo, setAllProvidersInfo] = useState<any[]>([]);
const fetchProviderInfo = useCallback(() => {
const info = getHealthKitBridge().getProviderInfo();
const available = getHealthKitBridge().getAvailableProviders();
const allInfo = getHealthKitBridge().getAllProvidersInfo();
setProviderInfo(info);
setAvailableProviders(available);
setAllProvidersInfo(allInfo);
return { info, available, allInfo };
}, []);
return {
providerInfo,
availableProviders,
allProvidersInfo,
fetchProviderInfo
};
}