diff --git a/CHANGELOG.md b/CHANGELOG.md index 18006be..aa05465 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,33 @@ +## [7.3.2](https://github.com/kingstinct/react-native-healthkit/compare/v7.3.1...v7.3.2) (2023-10-23) + + +### Bug Fixes + +* platform exports gone wrong ([95e9a84](https://github.com/kingstinct/react-native-healthkit/commit/95e9a84014e261cc21e62d8f9826ead656d48737)) + +## [7.3.1](https://github.com/kingstinct/react-native-healthkit/compare/v7.3.0...v7.3.1) (2023-10-19) + +# [7.3.0](https://github.com/kingstinct/react-native-healthkit/compare/v7.2.0...v7.3.0) (2023-09-13) + + +### Bug Fixes + +* handle when enddate is nil ([1146385](https://github.com/kingstinct/react-native-healthkit/commit/11463857c493091ac0b916d1ee7694f77c7dfe9a)) +* return uuid string ([9679e8a](https://github.com/kingstinct/react-native-healthkit/commit/9679e8a12507ba51a6ea4088d421ec9c4e56dfe8)) +* swift placeholder for workoutPlanId ([3389e17](https://github.com/kingstinct/react-native-healthkit/commit/3389e17d423926ea6b9b8513822a5510cf1370a8)) + + +### Features + +* add more types ([c100a64](https://github.com/kingstinct/react-native-healthkit/commit/c100a6419a766a1aa8126202acd7ad2b98576bb5)) +* allow totals in saveWorkoutSamples ([89c57db](https://github.com/kingstinct/react-native-healthkit/commit/89c57dbd5c8d312345f2fbf9c9e28db2d676a733)) +* create getWorkoutPlanId function ([ec3d7ef](https://github.com/kingstinct/react-native-healthkit/commit/ec3d7ef62aa6ab1a2fff2a8e34ffd13be5976a71)) +* handle when function is called by OS less than 17 ([81a5e01](https://github.com/kingstinct/react-native-healthkit/commit/81a5e01a5f8bbb55e9188c027e9330ad59b0b199)) +* move getWorkoutPlan into its own async function ([d72d3e5](https://github.com/kingstinct/react-native-healthkit/commit/d72d3e59f982d0cde5602488fb926e2a7109abcb)) +* rename function to getWorkoutPlanById and return id and activity type ([edfecf2](https://github.com/kingstinct/react-native-healthkit/commit/edfecf2b40f6b9f94e8d5fd2995b8ee0ea1b8d7b)) +* rename function to getWorkoutPlanById and return id and activity type ([ff252ac](https://github.com/kingstinct/react-native-healthkit/commit/ff252acbf85967952c40780298bdbbe352774b7b)) +* saveWorkoutRoute function ([e9eaee8](https://github.com/kingstinct/react-native-healthkit/commit/e9eaee83a556ff0112b19384bc945b4e9ccddade)) + # [7.2.0](https://github.com/kingstinct/react-native-healthkit/compare/v7.1.1...v7.2.0) (2023-09-04) diff --git a/README.md b/README.md index bf2f3ff..7037fd2 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Test Status](https://github.com/Kingstinct/react-native-healthkit/actions/workflows/test.yml/badge.svg)](https://github.com/Kingstinct/react-native-healthkit/actions/workflows/test.yml) [![Latest version on NPM](https://img.shields.io/npm/v/@kingstinct/react-native-healthkit)](https://www.npmjs.com/package/@kingstinct/react-native-healthkit) [![Downloads on NPM](https://img.shields.io/npm/dt/@kingstinct/react-native-healthkit)](https://www.npmjs.com/package/@kingstinct/react-native-healthkit) -[![Discord](https://dcbadge.vercel.app/api/server/EHScS93v?style=flat)](https://discord.gg/EHScS93v) +[![Discord](https://dcbadge.vercel.app/api/server/hrgnETpsJA?style=flat)](https://discord.gg/hrgnETpsJA) React Native bindings for HealthKit with full TypeScript and Promise support covering about any kind of data. Keeping TypeScript mappings as close as possible to HealthKit - both in regards to naming and serialization. This will make it easier to keep this library up-to-date with HealthKit as well as browsing [the official documentation](https://developer.apple.com/documentation/healthkit) (and if something - metadata properties for example - is not typed it will still be accessible). diff --git a/example/src/App.tsx b/example/src/App.tsx index 894d8ce..e37d530 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -5,19 +5,19 @@ import Healthkit, { HKStatisticsOptions, HKWorkoutActivityType, HKCategoryTypeIdentifier, + useMostRecentQuantitySample, + useStatisticsForQuantity, + useSources, + useMostRecentWorkout, + useHealthkitAuthorization, + deleteQuantitySample, + deleteSamples, + queryHeartbeatSeriesSamplesWithAnchor, + queryQuantitySamplesWithAnchor, + saveQuantitySample, + saveWorkoutRoute, + saveWorkoutSample, } from '@kingstinct/react-native-healthkit' -import useHealthkitAuthorization from '@kingstinct/react-native-healthkit/hooks/useHealthkitAuthorization' -import useMostRecentQuantitySample from '@kingstinct/react-native-healthkit/hooks/useMostRecentQuantitySample' -import useMostRecentWorkout from '@kingstinct/react-native-healthkit/hooks/useMostRecentWorkout' -import useSources from '@kingstinct/react-native-healthkit/hooks/useSources' -import useStatisticsForQuantity from '@kingstinct/react-native-healthkit/hooks/useStatisticsForQuantity' -import deleteQuantitySample from '@kingstinct/react-native-healthkit/utils/deleteQuantitySample' -import deleteSamples from '@kingstinct/react-native-healthkit/utils/deleteSamples' -import queryHeartbeatSeriesSamplesWithAnchor from '@kingstinct/react-native-healthkit/utils/queryHeartbeatSeriesSamplesWithAnchor' -import queryQuantitySamplesWithAnchor from '@kingstinct/react-native-healthkit/utils/queryQuantitySamplesWithAnchor' -import saveQuantitySample from '@kingstinct/react-native-healthkit/utils/saveQuantitySample' -import saveWorkoutRoute from '@kingstinct/react-native-healthkit/utils/saveWorkoutRoute' -import saveWorkoutSample from '@kingstinct/react-native-healthkit/utils/saveWorkoutSample' import dayjs from 'dayjs' import relativeTime from 'dayjs/plugin/relativeTime' import React, { @@ -629,7 +629,7 @@ const App = () => { const [canAccessProtectedData, setAccessProtectedData] = useState(false) useEffect(() => { - Healthkit.canAccessProtectedData() + Healthkit.isProtectedDataAvailable() .then(setAccessProtectedData) .catch(() => setAccessProtectedData(false)) }, []) diff --git a/ios/ReactNativeHealthkit.swift b/ios/ReactNativeHealthkit.swift index 06f436a..eba7026 100644 --- a/ios/ReactNativeHealthkit.swift +++ b/ios/ReactNativeHealthkit.swift @@ -673,42 +673,45 @@ class ReactNativeHealthkit: RCTEventEmitter { } let query = HKStatisticsQuery.init(quantityType: quantityType, quantitySamplePredicate: predicate, options: opts) { (_, stats: HKStatistics?, _: Error?) in - if let gottenStats = stats { - var dic = [String: [String: Any]?]() - let unit = HKUnit.init(from: unitString) - if let averageQuantity = gottenStats.averageQuantity() { - dic.updateValue(serializeQuantity(unit: unit, quantity: averageQuantity), forKey: "averageQuantity") - } - if let maximumQuantity = gottenStats.maximumQuantity() { - dic.updateValue(serializeQuantity(unit: unit, quantity: maximumQuantity), forKey: "maximumQuantity") - } - if let minimumQuantity = gottenStats.minimumQuantity() { - dic.updateValue(serializeQuantity(unit: unit, quantity: minimumQuantity), forKey: "minimumQuantity") - } - if let sumQuantity = gottenStats.sumQuantity() { - dic.updateValue(serializeQuantity(unit: unit, quantity: sumQuantity), forKey: "sumQuantity") + var dic = [String: [String: Any]?]() + + guard let gottenStats = stats else { + return resolve(dic) + } + + let unit = HKUnit.init(from: unitString) + if let averageQuantity = gottenStats.averageQuantity() { + dic.updateValue(serializeQuantity(unit: unit, quantity: averageQuantity), forKey: "averageQuantity") + } + if let maximumQuantity = gottenStats.maximumQuantity() { + dic.updateValue(serializeQuantity(unit: unit, quantity: maximumQuantity), forKey: "maximumQuantity") + } + if let minimumQuantity = gottenStats.minimumQuantity() { + dic.updateValue(serializeQuantity(unit: unit, quantity: minimumQuantity), forKey: "minimumQuantity") + } + if let sumQuantity = gottenStats.sumQuantity() { + dic.updateValue(serializeQuantity(unit: unit, quantity: sumQuantity), forKey: "sumQuantity") + } + if #available(iOS 12, *) { + if let mostRecent = gottenStats.mostRecentQuantity() { + dic.updateValue(serializeQuantity(unit: unit, quantity: mostRecent), forKey: "mostRecentQuantity") } - if #available(iOS 12, *) { - if let mostRecent = gottenStats.mostRecentQuantity() { - dic.updateValue(serializeQuantity(unit: unit, quantity: mostRecent), forKey: "mostRecentQuantity") - } - if let mostRecentDateInterval = gottenStats.mostRecentQuantityDateInterval() { - dic.updateValue([ - "start": self._dateFormatter.string(from: mostRecentDateInterval.start), - "end": self._dateFormatter.string(from: mostRecentDateInterval.end) - ], forKey: "mostRecentQuantityDateInterval") - } + if let mostRecentDateInterval = gottenStats.mostRecentQuantityDateInterval() { + dic.updateValue([ + "start": self._dateFormatter.string(from: mostRecentDateInterval.start), + "end": self._dateFormatter.string(from: mostRecentDateInterval.end) + ], forKey: "mostRecentQuantityDateInterval") } - if #available(iOS 13, *) { - let durationUnit = HKUnit.second() - if let duration = gottenStats.duration() { - dic.updateValue(serializeQuantity(unit: durationUnit, quantity: duration), forKey: "duration") - } + } + if #available(iOS 13, *) { + let durationUnit = HKUnit.second() + if let duration = gottenStats.duration() { + dic.updateValue(serializeQuantity(unit: durationUnit, quantity: duration), forKey: "duration") } - - resolve(dic) } + + resolve(dic) } store.execute(query) diff --git a/package.json b/package.json index 5a0d789..bc56abb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kingstinct/react-native-healthkit", - "version": "7.2.0", + "version": "7.3.2", "description": "React Native bindings for HealthKit", "main": "lib/commonjs/index", "module": "lib/module/index", @@ -44,7 +44,15 @@ "react-hooks" ], "repository": "https://github.com/kingstinct/react-native-healthkit", - "author": "Robert Herber (https://github.com/robertherber)", + "funding": [ + "https://github.com/sponsors/kingstinct", + "https://github.com/sponsors/robertherber" + ], + "author": { + "name": "Robert Herber", + "email": "robert@kingstinct.com", + "url": "https://github.com/robertherber" + }, "license": "MIT", "bugs": { "url": "https://github.com/kingstinct/react-native-healthkit/issues" diff --git a/src/hooks/useHealthkitAuthorization.ts b/src/hooks/useHealthkitAuthorization.ts index cd54e34..f3f60fa 100644 --- a/src/hooks/useHealthkitAuthorization.ts +++ b/src/hooks/useHealthkitAuthorization.ts @@ -7,6 +7,11 @@ import requestAuthorization from '../utils/requestAuthorization' import type { HealthkitReadAuthorization, HealthkitWriteAuthorization, HKAuthorizationRequestStatus } from '../native-types' +/** + * @description Hook to retrieve the current authorization status for the given types, and request authorization if needed. + * @see {@link https://developer.apple.com/documentation/healthkit/hkhealthstore/1614152-requestauthorization Apple Docs - requestAuthorization} + * @see {@link https://developer.apple.com/documentation/healthkit/authorizing_access_to_health_data Apple Docs - Authorizing access to health data} + */ const useHealthkitAuthorization = (read: readonly HealthkitReadAuthorization[], write?: readonly HealthkitWriteAuthorization[]) => { const [status, setStatus] = useState(null) diff --git a/src/hooks/useMostRecentCategorySample.ts b/src/hooks/useMostRecentCategorySample.ts index 069f071..dbb2653 100644 --- a/src/hooks/useMostRecentCategorySample.ts +++ b/src/hooks/useMostRecentCategorySample.ts @@ -6,6 +6,9 @@ import getMostRecentCategorySample from '../utils/getMostRecentCategorySample' import type { HKCategoryTypeIdentifier } from '../native-types' import type { HKCategorySample } from '../types' +/** + * @returns the most recent sample for the given category type. + */ function useMostRecentCategorySample< TCategory extends HKCategoryTypeIdentifier >(identifier: TCategory) { diff --git a/src/hooks/useMostRecentQuantitySample.ts b/src/hooks/useMostRecentQuantitySample.ts index c33adc2..f3a9ef1 100644 --- a/src/hooks/useMostRecentQuantitySample.ts +++ b/src/hooks/useMostRecentQuantitySample.ts @@ -7,6 +7,9 @@ import subscribeToChanges from '../utils/subscribeToChanges' import type { HKQuantityTypeIdentifier, UnitForIdentifier } from '../native-types' import type { HKQuantitySample } from '../types' +/** + * @returns the most recent sample for the given quantity type. + */ function useMostRecentQuantitySample< TIdentifier extends HKQuantityTypeIdentifier, TUnit extends UnitForIdentifier diff --git a/src/hooks/useMostRecentWorkout.ts b/src/hooks/useMostRecentWorkout.ts index ec20ee3..14bbcd5 100644 --- a/src/hooks/useMostRecentWorkout.ts +++ b/src/hooks/useMostRecentWorkout.ts @@ -9,6 +9,9 @@ import subscribeToChanges from '../utils/subscribeToChanges' import type { EnergyUnit, LengthUnit } from '../native-types' import type { HKWorkout } from '../types' +/** + * @returns the most recent workout sample. + */ function useMostRecentWorkout< TEnergy extends EnergyUnit, TDistance extends LengthUnit diff --git a/src/index.ios.tsx b/src/index.ios.tsx index 094ab45..7f60ef5 100644 --- a/src/index.ios.tsx +++ b/src/index.ios.tsx @@ -5,6 +5,8 @@ import useIsHealthDataAvailable from './hooks/useIsHealthDataAvailable' import useMostRecentCategorySample from './hooks/useMostRecentCategorySample' import useMostRecentQuantitySample from './hooks/useMostRecentQuantitySample' import useMostRecentWorkout from './hooks/useMostRecentWorkout' +import useSources from './hooks/useSources' +import useStatisticsForQuantity from './hooks/useStatisticsForQuantity' import useSubscribeToChanges from './hooks/useSubscribeToChanges' import Native, { HKQuantityTypeIdentifier } from './native-types' import deleteQuantitySample from './utils/deleteQuantitySample' @@ -214,6 +216,61 @@ export default { * @see {@link https://developer.apple.com/documentation/healthkit/authorizing_access_to_health_data Apple Docs - Authorizing access to health data} */ useHealthkitAuthorization, + useSources, + useStatisticsForQuantity, +} + +const isProtectedDataAvailable = canAccessProtectedData + +export { + authorizationStatusFor, + availableQuantityTypes, + disableAllBackgroundDelivery, + disableBackgroundDelivery, + enableBackgroundDelivery, + getBiologicalSex, + getBloodType, + getDateOfBirth, + getFitzpatrickSkinType, + getMostRecentCategorySample, + getMostRecentQuantitySample, + getMostRecentWorkout, + getPreferredUnit, + getPreferredUnits, + getRequestStatusForAuthorization, + getWheelchairUse, + getWorkoutRoutes, + isHealthDataAvailable, + queryCategorySamples, + queryCategorySamplesWithAnchor, + queryCorrelationSamples, + queryHeartbeatSeriesSamples, + queryHeartbeatSeriesSamplesWithAnchor, + queryQuantitySamples, + queryQuantitySamplesWithAnchor, + queryStatisticsForQuantity, + queryWorkouts, + querySources, + requestAuthorization, + deleteQuantitySample, + deleteSamples, + getWorkoutPlanById, + saveCategorySample, + saveCorrelationSample, + saveQuantitySample, + saveWorkoutSample, + saveWorkoutRoute, + subscribeToChanges, + useMostRecentCategorySample, + useMostRecentQuantitySample, + useMostRecentWorkout, + useSubscribeToChanges, + useHealthkitAuthorization, + useIsHealthDataAvailable, + useSources, + useStatisticsForQuantity, + canAccessProtectedData, + isProtectedDataAvailable, } export * from './types' diff --git a/src/index.native.tsx b/src/index.native.tsx new file mode 100644 index 0000000..5ae9b11 --- /dev/null +++ b/src/index.native.tsx @@ -0,0 +1,201 @@ +import { Platform } from 'react-native' + +import { + HKAuthorizationRequestStatus, HKAuthorizationStatus, HKBiologicalSex, HKBloodType, HKFitzpatrickSkinType, HKUnits, HKWheelchairUse, +} from './native-types' + +import type ReactNativeHealthkit from './index.ios' +import type { QueryCategorySamplesFn } from './utils/queryCategorySamples' +import type { QueryQuantitySamplesFn } from './utils/queryQuantitySamples' + +const notAvailableError = `[@kingstinct/react-native-healthkit] Platform "${ + Platform.OS +}" not supported` + +let hasWarned = false + +function UnavailableFn(retVal: T) { + return () => { + if (!hasWarned) { + // eslint-disable-next-line no-console + console.warn(notAvailableError) + hasWarned = true + } + return retVal + } +} + +const authorizationStatusFor = UnavailableFn(Promise.resolve(HKAuthorizationStatus.notDetermined)), + availableQuantityTypes = UnavailableFn([]), + disableAllBackgroundDelivery = UnavailableFn(Promise.resolve(false)), + disableBackgroundDelivery = UnavailableFn(Promise.resolve(false)), + enableBackgroundDelivery = UnavailableFn(Promise.resolve(false)), + getBiologicalSex = UnavailableFn(Promise.resolve(HKBiologicalSex.notSet)), + getBloodType = UnavailableFn(Promise.resolve(HKBloodType.notSet)), + getDateOfBirth = UnavailableFn(Promise.resolve(new Date(0))), + getFitzpatrickSkinType = UnavailableFn(Promise.resolve(HKFitzpatrickSkinType.notSet)), + getMostRecentCategorySample = UnavailableFn(Promise.resolve(null)), + getMostRecentQuantitySample = UnavailableFn(Promise.resolve(null)), + getMostRecentWorkout = UnavailableFn(Promise.resolve(null)), + getPreferredUnit = UnavailableFn(Promise.resolve(HKUnits.Count)), + getPreferredUnits = UnavailableFn(Promise.resolve([])), + getRequestStatusForAuthorization = UnavailableFn(Promise.resolve(HKAuthorizationRequestStatus.unknown)), + getWheelchairUse = UnavailableFn(Promise.resolve(HKWheelchairUse.notSet)), + getWorkoutRoutes = UnavailableFn(Promise.resolve([])), + isHealthDataAvailable = async () => Promise.resolve(false), + useSources = UnavailableFn(null), + useStatisticsForQuantity = UnavailableFn(null), + queryCategorySamples = UnavailableFn(Promise.resolve([])) as unknown as QueryCategorySamplesFn, + queryCategorySamplesWithAnchor = UnavailableFn(Promise.resolve({ + samples: [], + deletedSamples: [], + newAnchor: '', + })), + queryCorrelationSamples = UnavailableFn(Promise.resolve([])), + queryHeartbeatSeriesSamples = UnavailableFn(Promise.resolve([])), + queryHeartbeatSeriesSamplesWithAnchor = UnavailableFn(Promise.resolve({ + samples: [], + deletedSamples: [], + newAnchor: '', + })), + queryQuantitySamples = UnavailableFn(Promise.resolve([])) as unknown as QueryQuantitySamplesFn, + queryQuantitySamplesWithAnchor = UnavailableFn(Promise.resolve({ + samples: [], + deletedSamples: [], + newAnchor: '', + })), + queryStatisticsForQuantity = UnavailableFn(Promise.resolve({ + averageQuantity: undefined, + maximumQuantity: undefined, + minimumQuantity: undefined, + sumQuantity: undefined, + mostRecentQuantity: undefined, + mostRecentQuantityDateInterval: undefined, + duration: undefined, + })), + queryWorkouts = UnavailableFn(Promise.resolve([])), + querySources = UnavailableFn(Promise.resolve([])), + requestAuthorization = UnavailableFn(Promise.resolve(false)), + deleteQuantitySample = UnavailableFn(Promise.resolve(false)), + deleteSamples = UnavailableFn(Promise.resolve(false)), + getWorkoutPlanById = UnavailableFn(Promise.resolve(null)), + saveCategorySample = UnavailableFn(Promise.resolve(false)), + saveCorrelationSample = UnavailableFn(Promise.resolve(false)), + saveQuantitySample = UnavailableFn(Promise.resolve(false)), + saveWorkoutSample = UnavailableFn(Promise.resolve(null)), + saveWorkoutRoute = UnavailableFn(Promise.resolve(false)), + subscribeToChanges = UnavailableFn(Promise.resolve(async () => Promise.resolve(false))), + useMostRecentCategorySample = UnavailableFn(null), + useMostRecentQuantitySample = UnavailableFn(null), + useMostRecentWorkout = UnavailableFn(null), + useSubscribeToChanges = UnavailableFn([null, () => null]), + useHealthkitAuthorization = UnavailableFn([null, async () => Promise.resolve(HKAuthorizationRequestStatus.unknown)] as const), + useIsHealthDataAvailable = () => false, + canAccessProtectedData = async () => Promise.resolve(false), + isProtectedDataAvailable = async () => Promise.resolve(false) + +const Healthkit: typeof ReactNativeHealthkit = { + authorizationStatusFor, + availableQuantityTypes, + useSources, + useStatisticsForQuantity, + disableAllBackgroundDelivery, + disableBackgroundDelivery, + enableBackgroundDelivery, + getBiologicalSex, + getBloodType, + getDateOfBirth, + getFitzpatrickSkinType, + getMostRecentCategorySample, + getMostRecentQuantitySample, + getMostRecentWorkout, + getPreferredUnit, + getPreferredUnits, + getRequestStatusForAuthorization, + getWheelchairUse, + getWorkoutRoutes, + isHealthDataAvailable, + queryCategorySamples, + queryCategorySamplesWithAnchor, + queryCorrelationSamples, + queryHeartbeatSeriesSamples, + queryHeartbeatSeriesSamplesWithAnchor, + queryQuantitySamples, + queryQuantitySamplesWithAnchor, + queryStatisticsForQuantity, + queryWorkouts, + querySources, + requestAuthorization, + deleteQuantitySample, + deleteSamples, + getWorkoutPlanById, + saveCategorySample, + saveCorrelationSample, + saveQuantitySample, + saveWorkoutSample, + saveWorkoutRoute, + subscribeToChanges, + useMostRecentCategorySample, + useMostRecentQuantitySample, + useMostRecentWorkout, + useSubscribeToChanges, + useHealthkitAuthorization, + useIsHealthDataAvailable, + canAccessProtectedData, + isProtectedDataAvailable, +} + +export { + authorizationStatusFor, + availableQuantityTypes, + disableAllBackgroundDelivery, + disableBackgroundDelivery, + enableBackgroundDelivery, + useSources, + useStatisticsForQuantity, + getBiologicalSex, + getBloodType, + getDateOfBirth, + getFitzpatrickSkinType, + getMostRecentCategorySample, + getMostRecentQuantitySample, + getMostRecentWorkout, + getPreferredUnit, + getPreferredUnits, + getRequestStatusForAuthorization, + getWheelchairUse, + getWorkoutRoutes, + isHealthDataAvailable, + queryCategorySamples, + queryCategorySamplesWithAnchor, + queryCorrelationSamples, + queryHeartbeatSeriesSamples, + queryHeartbeatSeriesSamplesWithAnchor, + queryQuantitySamples, + queryQuantitySamplesWithAnchor, + queryStatisticsForQuantity, + queryWorkouts, + querySources, + requestAuthorization, + deleteQuantitySample, + deleteSamples, + getWorkoutPlanById, + saveCategorySample, + saveCorrelationSample, + saveQuantitySample, + saveWorkoutSample, + saveWorkoutRoute, + subscribeToChanges, + useMostRecentCategorySample, + useMostRecentQuantitySample, + useMostRecentWorkout, + useSubscribeToChanges, + useHealthkitAuthorization, + useIsHealthDataAvailable, + canAccessProtectedData, + isProtectedDataAvailable, +} + +export * from './types' + +export default Healthkit diff --git a/src/index.tsx b/src/index.tsx index c7d0747..85985a0 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,99 +1,5 @@ -import { Platform } from 'react-native' +import Healthkit from './index.ios' -import { - HKAuthorizationRequestStatus, HKAuthorizationStatus, HKBiologicalSex, HKBloodType, HKFitzpatrickSkinType, HKUnits, HKWheelchairUse, -} from './native-types' +export * from './index.ios' -import type ReactNativeHealthkit from './index.ios' -import type { QueryCategorySamplesFn } from './utils/queryCategorySamples' -import type { QueryQuantitySamplesFn } from './utils/queryQuantitySamples' - -const notAvailableError = `[@kingstinct/react-native-healthkit] Platform "${ - Platform.OS -}" not supported` - -let hasWarned = false - -function UnavailableFn(retVal: T) { - return () => { - if (!hasWarned) { - // eslint-disable-next-line no-console - console.warn(notAvailableError) - hasWarned = true - } - return retVal - } -} - -const Healthkit = { - authorizationStatusFor: UnavailableFn(Promise.resolve(HKAuthorizationStatus.notDetermined)), - availableQuantityTypes: UnavailableFn([]), - disableAllBackgroundDelivery: UnavailableFn(Promise.resolve(false)), - disableBackgroundDelivery: UnavailableFn(Promise.resolve(false)), - enableBackgroundDelivery: UnavailableFn(Promise.resolve(false)), - getBiologicalSex: UnavailableFn(Promise.resolve(HKBiologicalSex.notSet)), - getBloodType: UnavailableFn(Promise.resolve(HKBloodType.notSet)), - getDateOfBirth: UnavailableFn(Promise.resolve(new Date(0))), - getFitzpatrickSkinType: UnavailableFn(Promise.resolve(HKFitzpatrickSkinType.notSet)), - getMostRecentCategorySample: UnavailableFn(Promise.resolve(null)), - getMostRecentQuantitySample: UnavailableFn(Promise.resolve(null)), - getMostRecentWorkout: UnavailableFn(Promise.resolve(null)), - getPreferredUnit: UnavailableFn(Promise.resolve(HKUnits.Count)), - getPreferredUnits: UnavailableFn(Promise.resolve([])), - getRequestStatusForAuthorization: UnavailableFn(Promise.resolve(HKAuthorizationRequestStatus.unknown)), - getWheelchairUse: UnavailableFn(Promise.resolve(HKWheelchairUse.notSet)), - getWorkoutRoutes: UnavailableFn(Promise.resolve([])), - isHealthDataAvailable: async () => Promise.resolve(false), - queryCategorySamples: UnavailableFn(Promise.resolve([])) as unknown as QueryCategorySamplesFn, - queryCategorySamplesWithAnchor: UnavailableFn(Promise.resolve({ - samples: [], - deletedSamples: [], - newAnchor: '', - })), - queryCorrelationSamples: UnavailableFn(Promise.resolve([])), - queryHeartbeatSeriesSamples: UnavailableFn(Promise.resolve([])), - queryHeartbeatSeriesSamplesWithAnchor: UnavailableFn(Promise.resolve({ - samples: [], - deletedSamples: [], - newAnchor: '', - })), - queryQuantitySamples: UnavailableFn(Promise.resolve([])) as unknown as QueryQuantitySamplesFn, - queryQuantitySamplesWithAnchor: UnavailableFn(Promise.resolve({ - samples: [], - deletedSamples: [], - newAnchor: '', - })), - queryStatisticsForQuantity: UnavailableFn(Promise.resolve({ - averageQuantity: undefined, - maximumQuantity: undefined, - minimumQuantity: undefined, - sumQuantity: undefined, - mostRecentQuantity: undefined, - mostRecentQuantityDateInterval: undefined, - duration: undefined, - })), - queryWorkouts: UnavailableFn(Promise.resolve([])), - querySources: UnavailableFn(Promise.resolve([])), - requestAuthorization: UnavailableFn(Promise.resolve(false)), - deleteQuantitySample: UnavailableFn(Promise.resolve(false)), - deleteSamples: UnavailableFn(Promise.resolve(false)), - getWorkoutPlanById: UnavailableFn(Promise.resolve(null)), - saveCategorySample: UnavailableFn(Promise.resolve(false)), - saveCorrelationSample: UnavailableFn(Promise.resolve(false)), - saveQuantitySample: UnavailableFn(Promise.resolve(false)), - saveWorkoutSample: UnavailableFn(Promise.resolve(null)), - saveWorkoutRoute: UnavailableFn(Promise.resolve(false)), - subscribeToChanges: UnavailableFn(Promise.resolve(async () => Promise.resolve(false))), - useMostRecentCategorySample: UnavailableFn(null), - useMostRecentQuantitySample: UnavailableFn(null), - useMostRecentWorkout: UnavailableFn(null), - useSubscribeToChanges: UnavailableFn([null, () => null]), - useHealthkitAuthorization: UnavailableFn([null, async () => Promise.resolve(HKAuthorizationRequestStatus.unknown)] as const), - useIsHealthDataAvailable: () => false, - canAccessProtectedData: async () => Promise.resolve(false), - isProtectedDataAvailable: async () => Promise.resolve(false), -} as typeof ReactNativeHealthkit - -export * from './types' - -export default Healthkit as typeof ReactNativeHealthkit +export default Healthkit diff --git a/src/index.web.tsx b/src/index.web.tsx new file mode 100644 index 0000000..28a86a7 --- /dev/null +++ b/src/index.web.tsx @@ -0,0 +1,5 @@ +import Healthkit from './index.native' + +export * from './index.native' + +export default Healthkit diff --git a/src/utils/saveCategorySample.ts b/src/utils/saveCategorySample.ts index 0d0ae76..0a5b126 100644 --- a/src/utils/saveCategorySample.ts +++ b/src/utils/saveCategorySample.ts @@ -2,6 +2,10 @@ import Native from '../native-types' import type { HKCategoryTypeIdentifier, HKCategoryValueForIdentifier, MetadataMapperForCategoryIdentifier } from '../native-types' +/** + * @see {@link https://developer.apple.com/documentation/healthkit/hkhealthstore/1614168-savecategorysample save(_:withCompletion:) (Apple Docs)} + * @see {@link https://developer.apple.com/documentation/healthkit/saving_data_to_healthkit Saving data to HealthKit (Apple Docs)} + */ async function saveCategorySample( identifier: T, value: HKCategoryValueForIdentifier,