react-native-healthkit-bridge
Version:
A comprehensive React Native bridge for Apple HealthKit with TypeScript support, advanced authorization, and flexible data queries
358 lines (357 loc) • 16.1 kB
JavaScript
import { HEALTHKIT_CONFIG } from '../../config/healthkit.config';
import { HealthKitValidator } from '../../utils/ValidationUtils';
import { HealthKitErrorHandler } from '../../utils/ErrorHandler';
export class MockProvider {
constructor(config = {}) {
this.config = Object.assign({ mockDelay: HEALTHKIT_CONFIG.MOCK_DELAY, authorizationChance: HEALTHKIT_CONFIG.AUTHORIZATION_CHANCE }, config);
this.mockDelay = this.config.mockDelay || HEALTHKIT_CONFIG.MOCK_DELAY;
this.authorizationChance = this.config.authorizationChance || HEALTHKIT_CONFIG.AUTHORIZATION_CHANCE;
}
async simulateDelay() {
await new Promise(resolve => setTimeout(resolve, this.mockDelay));
}
generateMockQuantityData(identifier, days) {
const mockData = [];
for (let i = 0; i < days; i++) {
switch (identifier) {
case 'stepCount':
mockData.push(Math.floor(Math.random() * 5000) + 2000);
break;
case 'distanceWalkingRunning':
mockData.push(Math.floor(Math.random() * 5000) + 1000);
break;
case 'activeEnergyBurned':
mockData.push(Math.floor(Math.random() * 300) + 100);
break;
case 'heartRate':
mockData.push(Math.floor(Math.random() * 40) + 60);
break;
case 'bodyMass':
mockData.push(Math.floor(Math.random() * 20) + 60);
break;
case 'height':
mockData.push(Math.floor(Math.random() * 50) + 150);
break;
case 'flightsClimbed':
mockData.push(Math.floor(Math.random() * 10) + 1);
break;
case 'appleExerciseTime':
mockData.push(Math.floor(Math.random() * 60) + 10); // minutos
break;
default:
mockData.push(Math.floor(Math.random() * 100));
}
}
return mockData;
}
generateMockWorkouts(days) {
const workouts = [];
const workoutTypes = [3, 7, 37]; // Running, Walking, Swimming
const workoutCount = Math.min(Math.floor(days / 3), 10);
for (let i = 0; i < workoutCount; i++) {
workouts.push({
startDate: (Date.now() / 1000) - (i * 86400),
endDate: (Date.now() / 1000) - (i * 86400) + 3600,
activityType: workoutTypes[Math.floor(Math.random() * workoutTypes.length)],
duration: Math.floor(Math.random() * 3600) + 1800,
totalEnergyBurned: Math.floor(Math.random() * 500) + 200
});
}
return workouts;
}
async requestAuthorization() {
return HealthKitErrorHandler.withErrorHandling(async () => {
await this.simulateDelay();
console.log('Mock: Autorização solicitada');
const authorized = Math.random() < this.authorizationChance;
console.log(`Mock: Autorização ${authorized ? 'concedida' : 'negada'}`);
return authorized;
}, 'MockProvider.requestAuthorization', false);
}
async requestSelectiveAuthorization(identifiers) {
return HealthKitErrorHandler.withErrorHandling(async () => {
if (!HealthKitValidator.validateAuthorizationRequest(identifiers)) {
throw new Error('Identificadores inválidos para autorização');
}
await this.simulateDelay();
console.log(`Mock: Autorizando tipos seletivos: ${identifiers.join(', ')}`);
const authorized = Math.random() < this.authorizationChance;
console.log(`Mock: Autorização seletiva ${authorized ? 'concedida' : 'negada'}`);
return authorized;
}, 'MockProvider.requestSelectiveAuthorization', false);
}
async getAuthorizationStatus(identifiers) {
return HealthKitErrorHandler.withErrorHandling(async () => {
console.log('🔍 MockProvider.getAuthorizationStatus - Identificadores recebidos:', identifiers);
// Validação mais flexível
if (!identifiers || !Array.isArray(identifiers) || identifiers.length === 0) {
console.warn('⚠️ Identificadores inválidos, retornando array vazio');
return [];
}
await this.simulateDelay();
console.log(`Mock: Verificando status para tipos: ${identifiers.join(', ')}`);
return identifiers.map(identifier => ({
identifier,
status: Math.random() > 0.3 ? 'sharingAuthorized' : 'sharingDenied'
}));
}, 'MockProvider.getAuthorizationStatus', []);
}
async getAvailableTypes() {
return HealthKitErrorHandler.withErrorHandling(async () => {
await this.simulateDelay();
console.log('Mock: Obtendo tipos disponíveis');
return [
{
identifier: 'stepCount',
name: 'Passos',
description: 'Número de passos dados',
category: 'activity',
type: 'quantity'
},
{
identifier: 'distanceWalkingRunning',
name: 'Distância',
description: 'Distância caminhada/corrida',
category: 'activity',
type: 'quantity'
},
{
identifier: 'activeEnergyBurned',
name: 'Calorias Ativas',
description: 'Calorias queimadas em atividade',
category: 'activity',
type: 'quantity'
},
{
identifier: 'heartRate',
name: 'Frequência Cardíaca',
description: 'Batimentos por minuto',
category: 'heart',
type: 'quantity'
},
{
identifier: 'bodyMass',
name: 'Peso',
description: 'Peso corporal',
category: 'body',
type: 'quantity'
},
{
identifier: 'height',
name: 'Altura',
description: 'Altura corporal',
category: 'body',
type: 'quantity'
},
{
identifier: 'flightsClimbed',
name: 'Lances de Escada',
description: 'Número de lances de escada subidos',
category: 'activity',
type: 'quantity'
},
{
identifier: 'appleExerciseTime',
name: 'Tempo de Exercício',
description: 'Tempo de exercício ativo',
category: 'activity',
type: 'quantity'
}
];
}, 'MockProvider.getAvailableTypes', []);
}
async getTypeInfo(identifier) {
return HealthKitErrorHandler.withErrorHandling(async () => {
if (!HealthKitValidator.validateIdentifier(identifier)) {
throw new Error('Identificador inválido');
}
await this.simulateDelay();
console.log(`Mock: Obtendo info para tipo ${identifier}`);
return {
identifier,
name: identifier.charAt(0).toUpperCase() + identifier.slice(1),
description: `Informações sobre ${identifier}`,
category: 'activity',
type: 'quantity',
unit: HEALTHKIT_CONFIG.DEFAULT_UNITS[identifier] || 'count',
permissions: {
read: true,
write: false
}
};
}, 'MockProvider.getTypeInfo', {});
}
async getQuantitySamplesForDays(identifier, unit, days) {
return HealthKitErrorHandler.withErrorHandling(async () => {
if (!HealthKitValidator.validateIdentifier(identifier) ||
!HealthKitValidator.validateUnit(unit) ||
!HealthKitValidator.validateDays(days)) {
throw new Error('Parâmetros inválidos para query de quantidade');
}
await this.simulateDelay();
console.log(`Mock: Obtendo ${identifier} para ${days} dias`);
return this.generateMockQuantityData(identifier, days);
}, 'MockProvider.getQuantitySamplesForDays', []);
}
async getCategorySamplesForDays(identifier, days) {
return HealthKitErrorHandler.withErrorHandling(async () => {
if (!HealthKitValidator.validateIdentifier(identifier) ||
!HealthKitValidator.validateDays(days)) {
throw new Error('Parâmetros inválidos para query de categoria');
}
await this.simulateDelay();
console.log(`Mock: Obtendo dados categóricos ${identifier} para ${days} dias`);
return [];
}, 'MockProvider.getCategorySamplesForDays', []);
}
async getWorkoutsForDays(days) {
return HealthKitErrorHandler.withErrorHandling(async () => {
if (!HealthKitValidator.validateDays(days)) {
throw new Error('Parâmetros inválidos para query de workouts');
}
await this.simulateDelay();
console.log(`Mock: Obtendo workouts para ${days} dias`);
return this.generateMockWorkouts(days);
}, 'MockProvider.getWorkoutsForDays', []);
}
async getAudiogramsForDays(days) {
return HealthKitErrorHandler.withErrorHandling(async () => {
if (!HealthKitValidator.validateDays(days)) {
throw new Error('Parâmetros inválidos para query de audiogramas');
}
await this.simulateDelay();
console.log(`Mock: Obtendo audiogramas para ${days} dias`);
return [];
}, 'MockProvider.getAudiogramsForDays', []);
}
async getECGForDays(days) {
return HealthKitErrorHandler.withErrorHandling(async () => {
if (!HealthKitValidator.validateDays(days)) {
throw new Error('Parâmetros inválidos para query de ECG');
}
await this.simulateDelay();
console.log(`Mock: Obtendo ECG para ${days} dias`);
return [];
}, 'MockProvider.getECGForDays', []);
}
async getQuantitySamplesWithRange(identifier, unit, startDate, endDate) {
return HealthKitErrorHandler.withErrorHandling(async () => {
if (!HealthKitValidator.validateIdentifier(identifier) ||
!HealthKitValidator.validateUnit(unit)) {
throw new Error('Parâmetros inválidos para query de quantidade com range');
}
await this.simulateDelay();
console.log(`Mock: Obtendo ${identifier} com range`);
const days = Math.ceil((endDate - startDate) / 86400);
return this.generateMockQuantityData(identifier, days);
}, 'MockProvider.getQuantitySamplesWithRange', []);
}
async getCategorySamplesWithRange(identifier, startDate, endDate) {
return HealthKitErrorHandler.withErrorHandling(async () => {
if (!HealthKitValidator.validateIdentifier(identifier)) {
throw new Error('Parâmetros inválidos para query de categoria com range');
}
await this.simulateDelay();
console.log(`Mock: Obtendo dados categóricos ${identifier} com range`);
return [];
}, 'MockProvider.getCategorySamplesWithRange', []);
}
async getWorkoutsWithRange(startDate, endDate) {
return HealthKitErrorHandler.withErrorHandling(async () => {
await this.simulateDelay();
console.log('Mock: Obtendo workouts com range');
const days = Math.ceil((endDate - startDate) / 86400);
return this.generateMockWorkouts(days);
}, 'MockProvider.getWorkoutsWithRange', []);
}
async getAudiogramsWithRange(startDate, endDate) {
return HealthKitErrorHandler.withErrorHandling(async () => {
await this.simulateDelay();
console.log('Mock: Obtendo audiogramas com range');
return [];
}, 'MockProvider.getAudiogramsWithRange', []);
}
async getECGWithRange(startDate, endDate) {
return HealthKitErrorHandler.withErrorHandling(async () => {
await this.simulateDelay();
console.log('Mock: Obtendo ECG com range');
return [];
}, 'MockProvider.getECGWithRange', []);
}
async getQuantitySamples(identifier, unit) {
return this.getQuantitySamplesForDays(identifier, unit, HEALTHKIT_CONFIG.DEFAULT_DAYS);
}
async getCategorySamples(identifier) {
return this.getCategorySamplesForDays(identifier, HEALTHKIT_CONFIG.DEFAULT_DAYS);
}
async getWorkoutsGeneric() {
return this.getWorkoutsForDays(HEALTHKIT_CONFIG.DEFAULT_DAYS);
}
async getAudiograms() {
return this.getAudiogramsForDays(HEALTHKIT_CONFIG.DEFAULT_DAYS);
}
async getECG() {
return this.getECGForDays(HEALTHKIT_CONFIG.DEFAULT_DAYS);
}
// Métodos avançados (implementação básica para mock)
async queryQuantitySamples(identifier, unit, options) {
return HealthKitErrorHandler.withErrorHandling(async () => {
if (!HealthKitValidator.validateQueryOptions(options)) {
throw new Error('Opções de query inválidas');
}
await this.simulateDelay();
console.log(`Mock: Query avançada para ${identifier}`);
const days = options.days || HEALTHKIT_CONFIG.DEFAULT_DAYS;
const data = this.generateMockQuantityData(identifier, days);
return {
data: data.map((value, index) => ({
value,
unit,
startDate: Date.now() / 1000 - (index * 86400),
endDate: Date.now() / 1000 - (index * 86400) + 86400
})),
count: data.length,
hasMore: false
};
}, 'MockProvider.queryQuantitySamples', { data: [], count: 0, hasMore: false });
}
async queryCategorySamples(identifier, options) {
return HealthKitErrorHandler.withErrorHandling(async () => {
if (!HealthKitValidator.validateQueryOptions(options)) {
throw new Error('Opções de query inválidas');
}
await this.simulateDelay();
console.log(`Mock: Query avançada para categoria ${identifier}`);
return {
data: [],
count: 0,
hasMore: false
};
}, 'MockProvider.queryCategorySamples', { data: [], count: 0, hasMore: false });
}
async queryWorkouts(options) {
return HealthKitErrorHandler.withErrorHandling(async () => {
if (!HealthKitValidator.validateQueryOptions(options)) {
throw new Error('Opções de query inválidas');
}
await this.simulateDelay();
console.log('Mock: Query avançada para workouts');
const days = options.days || HEALTHKIT_CONFIG.DEFAULT_DAYS;
const data = this.generateMockWorkouts(days);
return {
data,
count: data.length,
hasMore: false
};
}, 'MockProvider.queryWorkouts', { data: [], count: 0, hasMore: false });
}
isAvailable() {
return true; // Mock sempre disponível
}
getProviderName() {
return 'MockProvider';
}
getProviderVersion() {
return '1.0.0';
}
}