0.0.4 • Published 5 months ago

caphealthkit v0.0.4

Weekly downloads
-
License
MIT
Repository
github
Last release
5 months ago

CapHealthKit 플러그인

Ionic/Capacitor용 건강 데이터 접근 플러그인입니다. iOS의 HealthKit과 Android의 Health Connect를 통합하여 크로스 플랫폼 건강 데이터 접근을 제공합니다.

설치 방법

npm install caphealthkit

플랫폼 별 설정

iOS (HealthKit)

  1. Xcode에서 HealthKit Capability 활성화
  2. Info.plist에 권한 설명 추가:
<key>NSHealthShareUsageDescription</key>
<string>걸음 수 데이터를 읽기 위해 권한이 필요합니다.</string>
<key>NSHealthUpdateUsageDescription</key>
<string>걸음 수 데이터를 쓰기 위해 권한이 필요합니다.</string>

Android (Health Connect)

  1. 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>
  1. 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 기능 설명

  1. 상태 관리

    • isAvailable: Health Connect/HealthKit 사용 가능 여부
    • isAuthorized: 권한 부여 상태
    • stepCount: 현재 걸음 수
    • error: 오류 메시지
  2. 메서드

    • requestAuthorization(): 건강 데이터 접근 권한 요청
    • getStepCount(startDate, endDate): 특정 기간의 걸음 수 조회
    • startStepCountUpdates(interval): 실시간 걸음 수 모니터링 시작
    • stopStepCountUpdates(): 실시간 모니터링 중지
  3. 자동화된 기능

    • 컴포넌트 마운트 시 자동으로 사용 가능 여부 확인
    • 권한 상태 자동 체크
    • 이벤트 리스너 자동 등록/해제
    • 오류 처리 및 상태 업데이트

이 Context 기반 구현은 React 애플리케이션에서 CapHealthKit 플러그인을 효율적으로 사용할 수 있게 해주며, 상태 관리와 에러 처리를 포함한 모든 필요한 기능을 제공합니다.

주의사항

  1. 권한 처리

    • 항상 isHealthConnectAvailable() 결과를 먼저 확인
    • Android에서는 Health Connect 앱 설치 여부 확인 필요
    • 권한 거부 시 적절한 사용자 안내 필요
  2. 데이터 동기화

    • 실시간 업데이트 사용 시 적절한 interval 설정
    • 불필요할 때는 반드시 stopStepCountUpdates() 호출
  3. 메모리 관리

    • 리스너는 반드시 컴포넌트 언마운트 시 제거
    • 과도한 업데이트 interval은 배터리 소모 증가
  4. 에러 처리

    • 모든 API 호출에 대한 적절한 에러 처리 구현
    • 사용자에게 친화적인 에러 메시지 제공

라이선스

MIT License

0.0.4

5 months ago

0.0.3

5 months ago

0.0.2

5 months ago

0.0.1

5 months ago