0.0.4 • Published 5 months ago
caphealthkit v0.0.4
CapHealthKit 플러그인
Ionic/Capacitor용 건강 데이터 접근 플러그인입니다. iOS의 HealthKit과 Android의 Health Connect를 통합하여 크로스 플랫폼 건강 데이터 접근을 제공합니다.
설치 방법
npm install caphealthkit
플랫폼 별 설정
iOS (HealthKit)
- Xcode에서 HealthKit Capability 활성화
- Info.plist에 권한 설명 추가:
<key>NSHealthShareUsageDescription</key>
<string>걸음 수 데이터를 읽기 위해 권한이 필요합니다.</string>
<key>NSHealthUpdateUsageDescription</key>
<string>걸음 수 데이터를 쓰기 위해 권한이 필요합니다.</string>
Android (Health Connect)
- AndroidManifest.xml에 권한 추가:
<uses-permission android:name="android.permission.health.READ_STEPS"/>
<uses-permission android:name="android.permission.health.WRITE_STEPS"/>
<uses-permission android:name="android.permission.ACCESS_HEALTH_CONNECT" />
<queries>
<package android:name="com.google.android.apps.healthdata" />
</queries>
- build.gradle에 Health Connect 의존성 추가:
dependencies {
implementation 'androidx.health.connect:connect-client:1.1.0-alpha02'
}
기본 사용법
1. 권한 요청
import { CapHealthKit } from 'caphealthkit';
// Health Connect/HealthKit 사용 가능 여부 확인
const checkAvailability = async () => {
const result = await CapHealthKit.isHealthConnectAvailable();
if (!result.available) {
// Android의 경우 Health Connect 설치 페이지로 이동
await CapHealthKit.openHealthConnectSettings();
return;
}
};
// 권한 요청
const requestPermissions = async () => {
try {
const result = await CapHealthKit.requestAuthorization({
read: ['steps'],
write: ['steps'],
});
if (result.status === 'settings_opened') {
// Android에서 권한 설정 화면으로 이동된 경우
console.log('Health Connect 설정 화면으로 이동됨');
}
} catch (error) {
console.error('권한 요청 실패:', error);
}
};
2. 걸음 수 데이터 조회
// 특정 기간의 걸음 수 조회
const getSteps = async () => {
try {
const result = await CapHealthKit.getStepCount({
startDate: '2024-03-01T00:00:00.000Z',
endDate: '2024-03-01T23:59:59.999Z',
});
console.log('걸음 수:', result.count);
} catch (error) {
console.error('걸음 수 조회 실패:', error);
}
};
3. 실시간 걸음 수 모니터링
// 실시간 업데이트 시작
await CapHealthKit.startStepCountUpdates({
interval: 60000, // 1분마다 업데이트
});
// 업데이트 이벤트 리스너
const listener = await CapHealthKit.addListener('stepUpdate', (data) => {
console.log('현재 걸음 수:', data.count);
});
// 업데이트 중지
await CapHealthKit.stopStepCountUpdates();
// 리스너 제거
listener.remove();
API 레퍼런스
메서드
isHealthConnectAvailable()
- Health Connect/HealthKit 사용 가능 여부 확인
- 반환:
Promise<HealthConnectAvailabilityResult>
openHealthConnectSettings()
- Android에서 Health Connect 설정 화면 열기
- 반환:
Promise<void>
requestAuthorization(options: HealthKitPermissions)
- 건강 데이터 접근 권한 요청
- 매개변수:
HealthKitPermissions
객체 - 반환:
Promise<HealthKitAuthorizationResult>
checkAuthorization()
- 현재 권한 상태 확인
- 반환:
Promise<HealthKitAuthorizationResult>
getStepCount(options: StepCountRequest)
- 특정 기간의 걸음 수 조회
- 매개변수:
StepCountRequest
객체 - 반환:
Promise<StepCountData>
startStepCountUpdates(options?: StepCountListener)
- 실시간 걸음 수 모니터링 시작
- 매개변수:
StepCountListener
객체 (선택) - 반환:
Promise<void>
stopStepCountUpdates()
- 실시간 걸음 수 모니터링 중지
- 반환:
Promise<void>
인터페이스
interface HealthKitPermissions {
read: string[]; // 읽기 권한 목록
write: string[]; // 쓰기 권한 목록
}
interface HealthKitAuthorizationResult {
authorized: boolean;
status: 'authorized' | 'denied' | 'not_determined' | 'not_available' | 'settings_opened';
message?: string;
}
interface HealthConnectAvailabilityResult {
available: boolean;
message?: string;
}
interface StepCountRequest {
startDate: string; // ISO 형식 날짜
endDate: string; // ISO 형식 날짜
}
interface StepCountData {
count: number;
startDate: string;
endDate: string;
}
interface StepCountListener {
interval?: number; // 업데이트 간격 (ms)
}
Context 기반 플러그인 사용 예시
1. HealthKit Context 정의 (HealthKitContext.ts)
import React, { createContext, useContext, useState, useEffect } from "react";
import { CapHealthKit } from "caphealthkit";
import logger from "../core/utils/logger.service";
// Context 타입 정의
interface HealthKitContextType {
isAvailable: boolean;
isAuthorized: boolean;
stepCount: number;
error: string | null;
requestAuthorization: () => Promise<void>;
getStepCount: (startDate: string, endDate: string) => Promise<void>;
startStepCountUpdates: (interval: number) => Promise<void>;
stopStepCountUpdates: () => Promise<void>;
}
// Context 생성
export const HealthKitContext = createContext<HealthKitContextType>(
{} as HealthKitContextType
);
// Provider 컴포넌트
export const HealthKitProvider: React.FC<{
children: React.ReactNode;
}> = ({ children }) => {
// 상태 관리
const [isAvailable, setIsAvailable] = useState(false);
const [isAuthorized, setIsAuthorized] = useState(false);
const [stepCount, setStepCount] = useState(0);
const [error, setError] = useState<string | null>(null);
// 초기화 시 Health API 사용 가능 여부 확인
useEffect(() => {
checkAvailability();
}, []);
// Health API 사용 가능할 때 권한 상태 확인 및 요청
useEffect(() => {
if (isAvailable) {
const checkAndRequestAuthorization = async () => {
try {
const result = await CapHealthKit.checkAuthorization();
setIsAuthorized(result.authorized);
// 권한이 없는 경우 자동으로 요청
if (!result.authorized) {
logger.info(
"[HealthKit] Requesting authorization automatically"
);
await requestAuthorization();
}
} catch (err: any) {
setError(err.message);
logger.error(
"[HealthKit] Authorization check failed:",
err
);
}
};
checkAndRequestAuthorization();
}
}, [isAvailable, isAuthorized]);
// 초기화 시 Health 오늘 걸음 수 조회
useEffect(() => {
// 컴포넌트 마운트 시 오늘 걸음 수 조회
const getTodaySteps = async () => {
if (isAvailable && isAuthorized) {
const today = new Date();
const startDate = new Date(
today.setHours(0, 0, 0, 0)
).toISOString();
const endDate = new Date().toISOString();
await getStepCount(startDate, endDate);
}
};
getTodaySteps();
}, [isAvailable, isAuthorized]);
useEffect(() => {
// 실시간 업데이트 시작
const startRealTimeUpdates = async () => {
if (isAvailable && isAuthorized) {
await startStepCountUpdates(10000); // 10초마다 업데이트
}
};
startRealTimeUpdates();
// 컴포넌트 언마운트 시 업데이트 중지
return () => {
stopStepCountUpdates();
};
}, [isAvailable, isAuthorized]);
// 리스너 관련 타입 정의
type CleanupFunction = () => void;
// 실시간 업데이트를 위한 이벤트 리스너 설정 부분 수정
useEffect(() => {
if (isAvailable) {
const setupListeners = async (): Promise<CleanupFunction> => {
try {
const stepListener = await CapHealthKit.addListener(
"stepUpdate",
(data: any) => {
setStepCount(data.count);
logger.info(
"[HealthKit] Step update:",
data.count
);
}
);
return () => {
const cleanup = async () => {
await stepListener.remove();
};
cleanup().catch((err) => {
logger.error(
"[HealthKit] Cleanup failed:",
err
);
});
};
} catch (err: any) {
logger.error(
"[HealthKit] Setup listeners failed:",
err
);
return () => {};
}
};
const unsubscribe: Promise<CleanupFunction> = setupListeners();
return () => {
unsubscribe.then((cleanup: CleanupFunction) => cleanup());
};
}
}, [isAvailable]);
// Health API 사용 가능 여부 확인
const checkAvailability = async () => {
try {
const result = await CapHealthKit.isHealthConnectAvailable();
setIsAvailable(result.available);
logger.info(
"[HealthKit] Health API available:",
result.available
);
} catch (err: any) {
setError(err.message);
logger.error("[HealthKit] Availability check failed:", err);
}
};
// 권한 요청
const requestAuthorization = async () => {
try {
setError(null);
const result = await CapHealthKit.requestAuthorization({
read: ["steps"],
write: ["steps"],
});
setIsAuthorized(result.authorized);
if (result.status === "settings_opened") {
await CapHealthKit.openHealthConnectSettings();
}
logger.info(
"[HealthKit] Authorization requested : " +
JSON.stringify(result)
);
} catch (err: any) {
setError(err.message);
logger.error(
"[HealthKit] Authorization request failed:",
err
);
}
};
// 특정 기간의 걸음 수 조회
const getStepCount = async (startDate: string, endDate: string) => {
try {
setError(null);
const result = await CapHealthKit.getStepCount({
startDate: startDate,
endDate: endDate,
});
setStepCount(result.count);
logger.info(
"[HealthKit] Step count retrieved:",
result.count
);
} catch (err: any) {
setError(err.message);
logger.error("[HealthKit] Get step count failed:", err);
}
};
// 실시간 걸음 수 업데이트 시작
const startStepCountUpdates = async (interval: number) => {
try {
setError(null);
await CapHealthKit.startStepCountUpdates({ interval });
logger.info("[HealthKit] Step count updates started");
} catch (err: any) {
setError(err.message);
logger.error("[HealthKit] Start updates failed:", err);
}
};
// 실시간 걸음 수 업데이트 중지
const stopStepCountUpdates = async () => {
try {
setError(null);
await CapHealthKit.stopStepCountUpdates();
logger.info("[HealthKit] Step count updates stopped");
} catch (err: any) {
setError(err.message);
logger.error("[HealthKit] Stop updates failed:", err);
}
};
// Context 값
const value: HealthKitContextType = {
isAvailable,
isAuthorized,
stepCount,
error,
requestAuthorization,
getStepCount,
startStepCountUpdates,
stopStepCountUpdates,
};
return (
<HealthKitContext.Provider value={value}>
{children}
</HealthKitContext.Provider>
);
};
// Custom Hook
export const useHealthKit = () => {
const context = useContext(HealthKitContext);
if (context === undefined) {
throw new Error(
"useHealthKit must be used within a HealthKitProvider"
);
}
return context;
};
2. Context 사용 방법
// App.tsx 또는 최상위 컴포넌트
import { HealthKitProvider } from './contexts/useHealthKit';
const App: React.FC = () => {
return (
<HealthKitProvider>
<YourApp />
</HealthKitProvider>
);
};
// 하위 컴포넌트에서 사용
import { useHealthKit } from './contexts/useHealthKit';
const StepCounter: React.FC = () => {
const {
isAvailable,
isAuthorized,
stepCount,
error,
requestAuthorization,
getStepCount,
startStepCountUpdates,
stopStepCountUpdates,
} = useHealthKit();
// Context의 상태와 메서드 사용
return (
<div>
{stepCount} 걸음
</div>
);
};
Context 기능 설명
상태 관리
isAvailable
: Health Connect/HealthKit 사용 가능 여부isAuthorized
: 권한 부여 상태stepCount
: 현재 걸음 수error
: 오류 메시지
메서드
requestAuthorization()
: 건강 데이터 접근 권한 요청getStepCount(startDate, endDate)
: 특정 기간의 걸음 수 조회startStepCountUpdates(interval)
: 실시간 걸음 수 모니터링 시작stopStepCountUpdates()
: 실시간 모니터링 중지
자동화된 기능
- 컴포넌트 마운트 시 자동으로 사용 가능 여부 확인
- 권한 상태 자동 체크
- 이벤트 리스너 자동 등록/해제
- 오류 처리 및 상태 업데이트
이 Context 기반 구현은 React 애플리케이션에서 CapHealthKit 플러그인을 효율적으로 사용할 수 있게 해주며, 상태 관리와 에러 처리를 포함한 모든 필요한 기능을 제공합니다.
주의사항
권한 처리
- 항상 isHealthConnectAvailable() 결과를 먼저 확인
- Android에서는 Health Connect 앱 설치 여부 확인 필요
- 권한 거부 시 적절한 사용자 안내 필요
데이터 동기화
- 실시간 업데이트 사용 시 적절한 interval 설정
- 불필요할 때는 반드시 stopStepCountUpdates() 호출
메모리 관리
- 리스너는 반드시 컴포넌트 언마운트 시 제거
- 과도한 업데이트 interval은 배터리 소모 증가
에러 처리
- 모든 API 호출에 대한 적절한 에러 처리 구현
- 사용자에게 친화적인 에러 메시지 제공
라이선스
MIT License