feat: 支持 healthkit
This commit is contained in:
147
utils/healthKit.ts
Normal file
147
utils/healthKit.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user