Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update from base #3

Merged
merged 11 commits into from
Jan 16, 2024
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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)


Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
26 changes: 13 additions & 13 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@
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, {
Expand Down Expand Up @@ -440,7 +440,7 @@
await saveWorkoutRoute(workoutUUID, locationSamples)
}
} catch (error) {
console.error('Error Saving Activity', error)

Check warning on line 443 in example/src/App.tsx

View workflow job for this annotation

GitHub Actions / test

Unexpected console statement
}
}
}, [])
Expand Down Expand Up @@ -629,7 +629,7 @@
const [canAccessProtectedData, setAccessProtectedData] = useState<boolean>(false)

useEffect(() => {
Healthkit.canAccessProtectedData()
Healthkit.isProtectedDataAvailable()
.then(setAccessProtectedData)
.catch(() => setAccessProtectedData(false))
}, [])
Expand All @@ -651,7 +651,7 @@

anchor.current = res.newAnchor

alert(JSON.stringify(res))

Check warning on line 654 in example/src/App.tsx

View workflow job for this annotation

GitHub Actions / test

Unexpected alert
}}
>
First 2 stepCount
Expand All @@ -664,7 +664,7 @@

anchor.current = res.newAnchor

alert(JSON.stringify(res))

Check warning on line 667 in example/src/App.tsx

View workflow job for this annotation

GitHub Actions / test

Unexpected alert
}}
>
Next 2 stepCount
Expand All @@ -677,7 +677,7 @@

heartbeatsAnchor.current = res.newAnchor

alert(JSON.stringify(res))

Check warning on line 680 in example/src/App.tsx

View workflow job for this annotation

GitHub Actions / test

Unexpected alert
}}
>
Next 2 HeartbeatSeries samples
Expand Down
65 changes: 34 additions & 31 deletions ios/ReactNativeHealthkit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
12 changes: 10 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -44,7 +44,15 @@
"react-hooks"
],
"repository": "https://github.com/kingstinct/react-native-healthkit",
"author": "Robert Herber <[email protected]> (https://github.com/robertherber)",
"funding": [
"https://github.com/sponsors/kingstinct",
"https://github.com/sponsors/robertherber"
],
"author": {
"name": "Robert Herber",
"email": "[email protected]",
"url": "https://github.com/robertherber"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/kingstinct/react-native-healthkit/issues"
Expand Down
5 changes: 5 additions & 0 deletions src/hooks/useHealthkitAuthorization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<HKAuthorizationRequestStatus | null>(null)

Expand Down
3 changes: 3 additions & 0 deletions src/hooks/useMostRecentCategorySample.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
3 changes: 3 additions & 0 deletions src/hooks/useMostRecentQuantitySample.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<TIdentifier>
Expand Down
3 changes: 3 additions & 0 deletions src/hooks/useMostRecentWorkout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
57 changes: 57 additions & 0 deletions src/index.ios.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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'
Loading
Loading