Files
digital-pilates/utils/healthKit.ts
2025-09-17 18:05:11 +08:00

147 lines
4.1 KiB
TypeScript

/**
* HealthKit Native Module Interface
* React Native TypeScript bindings for iOS HealthKit access
*/
import { NativeModules } from 'react-native';
export interface HealthKitPermissions {
[key: string]: 'notDetermined' | 'denied' | 'authorized' | 'unknown';
}
export interface HealthKitAuthorizationResult {
success: boolean;
permissions: HealthKitPermissions;
}
export interface SleepDataSource {
name: string;
bundleIdentifier: string;
}
export interface SleepDataSample {
id: string;
startDate: string; // ISO8601 format
endDate: string; // ISO8601 format
value: number;
categoryType: 'inBed' | 'asleep' | 'awake' | 'core' | 'deep' | 'rem' | 'unknown';
duration: number; // Duration in seconds
source: SleepDataSource;
metadata: Record<string, any>;
}
export interface SleepDataOptions {
startDate?: string; // ISO8601 format, defaults to 7 days ago
endDate?: string; // ISO8601 format, defaults to now
limit?: number; // Maximum number of samples, defaults to 100
}
export interface SleepDataResult {
data: SleepDataSample[];
count: number;
startDate: string;
endDate: string;
}
export interface HealthKitManagerInterface {
/**
* Request authorization to access HealthKit data
* This will prompt the user for permission to read/write health data
*/
requestAuthorization(): Promise<HealthKitAuthorizationResult>;
/**
* Get sleep analysis data from HealthKit
* @param options Query options including date range and limit
*/
getSleepData(options?: SleepDataOptions): Promise<SleepDataResult>;
}
console.log('NativeModules', NativeModules);
// Native module interface
const HealthKitManager: HealthKitManagerInterface = NativeModules.HealthKitManager;
export default HealthKitManager;
// Utility functions for working with sleep data
export class HealthKitUtils {
/**
* Convert seconds to hours and minutes
*/
static formatDuration(seconds: number): string {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
if (hours > 0) {
return `${hours}h ${minutes}m`;
}
return `${minutes}m`;
}
/**
* Get total sleep duration from sleep samples for a specific date
*/
static getTotalSleepDuration(samples: SleepDataSample[], date: Date): number {
const targetDate = date.toISOString().split('T')[0];
return samples
.filter(sample => {
const sampleDate = new Date(sample.startDate).toISOString().split('T')[0];
return sampleDate === targetDate &&
['asleep', 'core', 'deep', 'rem'].includes(sample.categoryType);
})
.reduce((total, sample) => total + sample.duration, 0);
}
/**
* Group sleep samples by date
*/
static groupSamplesByDate(samples: SleepDataSample[]): Record<string, SleepDataSample[]> {
return samples.reduce((grouped, sample) => {
const date = new Date(sample.startDate).toISOString().split('T')[0];
if (!grouped[date]) {
grouped[date] = [];
}
grouped[date].push(sample);
return grouped;
}, {} as Record<string, SleepDataSample[]>);
}
/**
* Get sleep quality metrics from samples
*/
static getSleepQualityMetrics(samples: SleepDataSample[]) {
const sleepSamples = samples.filter(s =>
['asleep', 'core', 'deep', 'rem'].includes(s.categoryType)
);
if (sleepSamples.length === 0) {
return null;
}
const totalDuration = sleepSamples.reduce((sum, s) => sum + s.duration, 0);
const deepSleepDuration = sleepSamples
.filter(s => s.categoryType === 'deep')
.reduce((sum, s) => sum + s.duration, 0);
const remSleepDuration = sleepSamples
.filter(s => s.categoryType === 'rem')
.reduce((sum, s) => sum + s.duration, 0);
return {
totalDuration,
deepSleepDuration,
remSleepDuration,
deepSleepPercentage: totalDuration > 0 ? (deepSleepDuration / totalDuration) * 100 : 0,
remSleepPercentage: totalDuration > 0 ? (remSleepDuration / totalDuration) * 100 : 0,
};
}
/**
* Check if HealthKit is available (iOS only)
*/
static isAvailable(): boolean {
return HealthKitManager != null;
}
}