@kingstinct/react-native-healthkit 4.2.0 → 4.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +1 -1
  2. package/ios/ReactNativeHealthkit.m +6 -1
  3. package/ios/ReactNativeHealthkit.swift +365 -176
  4. package/lib/commonjs/index.ios.js +129 -105
  5. package/lib/commonjs/index.ios.js.map +1 -1
  6. package/lib/commonjs/index.js +58 -44
  7. package/lib/commonjs/index.js.map +1 -1
  8. package/lib/commonjs/native-types.js +136 -106
  9. package/lib/commonjs/native-types.js.map +1 -1
  10. package/lib/commonjs/types.js +4 -0
  11. package/lib/commonjs/types.js.map +1 -1
  12. package/lib/example/App.js +197 -0
  13. package/lib/index.ios.js +310 -0
  14. package/lib/index.js +44 -0
  15. package/lib/module/index.ios.js +130 -106
  16. package/lib/module/index.ios.js.map +1 -1
  17. package/lib/module/index.js +52 -37
  18. package/lib/module/index.js.map +1 -1
  19. package/lib/module/native-types.js +127 -104
  20. package/lib/module/native-types.js.map +1 -1
  21. package/lib/module/types.js +1 -1
  22. package/lib/module/types.js.map +1 -1
  23. package/lib/native-types.js +447 -0
  24. package/lib/src/index.ios.js +314 -0
  25. package/lib/src/index.js +45 -0
  26. package/lib/src/native-types.js +453 -0
  27. package/lib/src/types.js +1 -0
  28. package/lib/types.js +1 -0
  29. package/lib/typescript/src/index.d.ts +1 -1
  30. package/lib/typescript/src/index.ios.d.ts +1 -1
  31. package/lib/typescript/src/native-types.d.ts +458 -205
  32. package/lib/typescript/src/types.d.ts +87 -81
  33. package/package.json +21 -35
  34. package/src/index.ios.tsx +303 -269
  35. package/src/index.tsx +63 -45
  36. package/src/native-types.ts +318 -290
  37. package/src/types.ts +120 -105
  38. package/.DS_Store +0 -0
  39. package/.circleci/config.yml +0 -98
  40. package/.editorconfig +0 -15
  41. package/.gitattributes +0 -3
  42. package/.github/workflows/main.yml +0 -32
  43. package/.gitignore +0 -60
  44. package/CONTRIBUTING.md +0 -184
  45. package/babel.config.js +0 -3
  46. package/example-expo/.expo/README.md +0 -15
  47. package/example-expo/.expo/devices.json +0 -8
  48. package/example-expo/.expo/packager-info.json +0 -5
  49. package/example-expo/.expo/settings.json +0 -9
  50. package/example-expo/.expo-shared/assets.json +0 -4
  51. package/example-expo/.gitignore +0 -14
  52. package/example-expo/App.tsx +0 -376
  53. package/example-expo/app.json +0 -43
  54. package/example-expo/assets/adaptive-icon.png +0 -0
  55. package/example-expo/assets/favicon.png +0 -0
  56. package/example-expo/assets/icon.png +0 -0
  57. package/example-expo/assets/splash.png +0 -0
  58. package/example-expo/babel.config.js +0 -8
  59. package/example-expo/build-1653040579600.ipa +0 -0
  60. package/example-expo/build-1653041063216.ipa +0 -0
  61. package/example-expo/eas.json +0 -18
  62. package/example-expo/package.json +0 -32
  63. package/example-expo/tsconfig.json +0 -6
  64. package/example-expo/yarn.lock +0 -6857
  65. package/lib/typescript/example-expo/App.d.ts +0 -3
  66. package/lib/typescript/src/__tests__/index.test.d.ts +0 -0
  67. package/src/__tests__/index.test.tsx +0 -1
  68. package/tsconfig.json +0 -27
  69. package/yarn.lock +0 -10241
package/src/index.ios.tsx CHANGED
@@ -1,21 +1,29 @@
1
- import { useState, useEffect, useCallback } from 'react';
1
+ import { useCallback, useEffect, useState } from 'react'
2
+
2
3
  import Native, {
3
4
  EventEmitter,
4
- HKCategorySampleRaw,
5
+ HKQuantityTypeIdentifier,
6
+ HKUnit,
5
7
  HKCategoryTypeIdentifier,
8
+ } from './native-types'
9
+
10
+ import type {
11
+ HKSampleTypeIdentifier,
12
+ HKCategorySampleRaw,
6
13
  HKCategoryValueForIdentifier,
7
14
  HKCharacteristicTypeIdentifier,
8
15
  HKCorrelationRaw,
9
16
  HKCorrelationTypeIdentifier,
10
17
  HKQuantitySampleRaw,
11
- HKQuantityTypeIdentifier,
12
- HKSampleTypeIdentifier,
13
- HKUnit,
14
18
  HKUnitSI,
15
19
  HKUnitSIPrefix,
16
20
  HKWorkoutRaw,
17
21
  MetadataMapperForCategoryIdentifier,
18
- } from './native-types';
22
+ ReadPermissions,
23
+ WritePermissions,
24
+ HealthkitAuthorization,
25
+ HKAuthorizationRequestStatus,
26
+ } from './native-types'
19
27
  import type {
20
28
  GenericQueryOptions,
21
29
  GetMostRecentCategorySampleFn,
@@ -23,6 +31,7 @@ import type {
23
31
  GetMostRecentWorkoutFn,
24
32
  GetPreferredUnitFn,
25
33
  GetPreferredUnitsFn,
34
+ GetWorkoutRoutesFn,
26
35
  HKCategorySample,
27
36
  HKCorrelation,
28
37
  HKQuantitySample,
@@ -37,76 +46,74 @@ import type {
37
46
  SaveQuantitySampleFn,
38
47
  SaveWorkoutSampleFn,
39
48
  SubscribeToChangesFn,
40
- } from './types';
49
+ } from './types'
50
+ import type { ValueOf } from 'type-fest'
41
51
 
42
52
  const getPreferredUnit: GetPreferredUnitFn = async (type) => {
43
- const [unit] = await getPreferredUnits([type]);
44
- return unit;
45
- };
53
+ const [unit] = await getPreferredUnits([type])
54
+ return unit
55
+ }
46
56
 
47
57
  const ensureUnit = async <TUnit extends HKUnit>(
48
58
  type: HKQuantityTypeIdentifier,
49
- providedUnit?: TUnit
59
+ providedUnit?: TUnit,
50
60
  ) => {
51
61
  if (providedUnit) {
52
- return providedUnit;
62
+ return providedUnit
53
63
  }
54
- const unit = await Native.getPreferredUnits([type]);
55
- return unit[type] as TUnit;
56
- };
64
+ const unit = await Native.getPreferredUnits([type])
65
+ return unit[type] as TUnit
66
+ }
57
67
 
58
68
  function deserializeSample<
59
69
  TIdentifier extends HKQuantityTypeIdentifier,
60
70
  TUnit extends HKUnit
61
71
  >(
62
- sample: HKQuantitySampleRaw<TIdentifier, TUnit>
72
+ sample: HKQuantitySampleRaw<TIdentifier, TUnit>,
63
73
  ): HKQuantitySample<TIdentifier, TUnit> {
64
74
  return {
65
75
  ...sample,
66
76
  startDate: new Date(sample.startDate),
67
77
  endDate: new Date(sample.endDate),
68
- };
78
+ }
69
79
  }
70
80
 
71
81
  function deserializeWorkout<TEnergy extends HKUnit, TDistance extends HKUnit>(
72
- sample: HKWorkoutRaw<TEnergy, TDistance>
82
+ sample: HKWorkoutRaw<TEnergy, TDistance>,
73
83
  ): HKWorkout<TEnergy, TDistance> {
74
84
  return {
75
85
  ...sample,
76
86
  startDate: new Date(sample.startDate),
77
87
  endDate: new Date(sample.endDate),
78
- };
88
+ }
79
89
  }
80
90
 
81
91
  const deserializCategorySample = <T extends HKCategoryTypeIdentifier>(
82
- sample: HKCategorySampleRaw<T>
83
- ): HKCategorySample<T> => {
84
- return {
85
- ...sample,
86
- startDate: new Date(sample.startDate),
87
- endDate: new Date(sample.endDate),
88
- };
89
- };
92
+ sample: HKCategorySampleRaw<T>,
93
+ ): HKCategorySample<T> => ({
94
+ ...sample,
95
+ startDate: new Date(sample.startDate),
96
+ endDate: new Date(sample.endDate),
97
+ })
90
98
 
91
- const serializeDate = (date?: Date | null): string => {
92
- return date ? date.toISOString() : new Date(0).toISOString();
93
- };
99
+ const serializeDate = (date?: Date | null): string => (date ? date.toISOString() : new Date(0).toISOString())
94
100
 
95
101
  const prepareOptions = (options: GenericQueryOptions) => {
96
- const limit =
97
- !options.limit || options.limit === Infinity ? 0 : options.limit;
98
- const ascending = options.ascending ?? limit === 0;
99
- const from = serializeDate(options.from);
100
- const to = serializeDate(options.to);
101
- return { limit, ascending, from, to };
102
- };
102
+ const limit = !options.limit || options.limit === Infinity ? 0 : options.limit
103
+ const ascending = options.ascending ?? limit === 0
104
+ const from = serializeDate(options.from)
105
+ const to = serializeDate(options.to)
106
+ return {
107
+ limit, ascending, from, to,
108
+ }
109
+ }
103
110
 
104
111
  const queryQuantitySamples: QueryQuantitySamplesFn = async (
105
112
  identifier,
106
- options
113
+ options,
107
114
  ) => {
108
- const unit = await ensureUnit(identifier, options.unit);
109
- const opts = prepareOptions(options);
115
+ const unit = await ensureUnit(identifier, options.unit)
116
+ const opts = prepareOptions(options)
110
117
 
111
118
  const quantitySamples = await Native.queryQuantitySamples(
112
119
  identifier,
@@ -114,121 +121,155 @@ const queryQuantitySamples: QueryQuantitySamplesFn = async (
114
121
  opts.from,
115
122
  opts.to,
116
123
  opts.limit,
117
- opts.ascending
118
- );
124
+ opts.ascending,
125
+ )
126
+
127
+ return quantitySamples.map(deserializeSample)
128
+ }
119
129
 
120
- return quantitySamples.map(deserializeSample);
121
- };
130
+ async function getPreferredUnitsTyped<
131
+ TEnergy extends HKUnit,
132
+ TDistance extends HKUnit
133
+ >(options?: { readonly energyUnit?: TEnergy; readonly distanceUnit?: TDistance }) {
134
+ let energyUnit = options?.energyUnit
135
+ let distanceUnit = options?.distanceUnit
136
+ if (!energyUnit || !distanceUnit) {
137
+ const units = await Native.getPreferredUnits([
138
+ HKQuantityTypeIdentifier.distanceWalkingRunning,
139
+ HKQuantityTypeIdentifier.activeEnergyBurned,
140
+ ])
141
+ if (!energyUnit) {
142
+ energyUnit = units[HKQuantityTypeIdentifier.distanceWalkingRunning] as
143
+ | TEnergy
144
+ | undefined
145
+ }
146
+ if (!distanceUnit) {
147
+ distanceUnit = units[HKQuantityTypeIdentifier.activeEnergyBurned] as
148
+ | TDistance
149
+ | undefined
150
+ }
151
+ }
152
+ if (!energyUnit) {
153
+ energyUnit = HKUnit.Kilocalories as TEnergy
154
+ }
155
+ if (!distanceUnit) {
156
+ distanceUnit = HKUnit.Meters as TDistance
157
+ }
158
+ return { energyUnit, distanceUnit }
159
+ }
122
160
 
123
161
  const subscribeToChanges: SubscribeToChangesFn = async (
124
162
  identifier,
125
- callback
163
+ callback,
126
164
  ) => {
127
165
  const subscription = EventEmitter.addListener(
128
166
  'onChange',
129
167
  ({ typeIdentifier }) => {
130
168
  if (typeIdentifier === identifier) {
131
- callback();
169
+ callback()
132
170
  }
133
- }
134
- );
171
+ },
172
+ )
135
173
 
136
174
  const queryId = await Native.subscribeToObserverQuery(identifier).catch(
137
- (error) => {
138
- subscription.remove();
139
- return Promise.reject(error);
140
- }
141
- );
142
-
143
- return () => {
144
- subscription.remove();
145
- return Native.unsubscribeQuery(queryId);
146
- };
147
- };
175
+ async (error) => {
176
+ subscription.remove()
177
+ return Promise.reject(error)
178
+ },
179
+ )
180
+
181
+ return async () => {
182
+ subscription.remove()
183
+ return Native.unsubscribeQuery(queryId)
184
+ }
185
+ }
148
186
 
149
187
  const getMostRecentQuantitySample: GetMostRecentQuantitySampleFn = async (
150
188
  identifier,
151
- unit
189
+ unit,
152
190
  ) => {
153
191
  const samples = await queryQuantitySamples(identifier, {
154
192
  limit: 1,
155
- unit: unit,
156
- });
157
- return samples[0];
158
- };
193
+ unit,
194
+ })
195
+ return samples[0]
196
+ }
159
197
 
160
198
  function useMostRecentWorkout<
161
199
  TEnergy extends HKUnit,
162
200
  TDistance extends HKUnit
163
- >(options?: { energyUnit?: TEnergy; distanceUnit?: TDistance }) {
201
+ >(options?: { readonly energyUnit?: TEnergy; readonly distanceUnit?: TDistance }) {
164
202
  const [workout, setWorkout] = useState<HKWorkout<TEnergy, TDistance> | null>(
165
- null
166
- );
203
+ null,
204
+ )
167
205
  useEffect(() => {
168
- let cancelSubscription: (() => Promise<boolean>) | undefined;
206
+ let cancelSubscription: (() => Promise<boolean>) | undefined
169
207
 
170
208
  const init = async () => {
171
209
  const { energyUnit, distanceUnit } = await getPreferredUnitsTyped(
172
- options
173
- );
210
+ options,
211
+ )
174
212
 
175
213
  cancelSubscription = await subscribeToChanges(
176
214
  'HKWorkoutTypeIdentifier',
177
- () => {
178
- getMostRecentWorkout({ energyUnit, distanceUnit }).then(setWorkout);
179
- }
180
- );
181
- };
182
- init();
215
+ async () => {
216
+ const w = await getMostRecentWorkout({ energyUnit, distanceUnit })
217
+ setWorkout(w)
218
+ },
219
+ )
220
+ }
221
+ void init()
183
222
  return () => {
184
- cancelSubscription && cancelSubscription();
185
- };
186
- }, [options]);
187
- return workout;
223
+ void cancelSubscription?.()
224
+ }
225
+ }, [options])
226
+ return workout
188
227
  }
189
228
 
190
229
  const getMostRecentCategorySample: GetMostRecentCategorySampleFn = async (
191
- identifier
230
+ identifier,
192
231
  ) => {
193
232
  const samples = await queryCategorySamples(identifier, {
194
233
  limit: 1,
195
234
  ascending: false,
196
- });
197
-
198
- return samples[0];
199
- };
200
-
201
- function useMostRecentCategorySample<
202
- TCategory extends HKCategoryTypeIdentifier
203
- >(identifier: TCategory) {
204
- const [category, setCategory] = useState<HKCategorySample<TCategory> | null>(
205
- null
206
- );
207
- const updater = useCallback(() => {
208
- getMostRecentCategorySample(identifier).then(setCategory);
209
- }, [identifier]);
235
+ })
210
236
 
211
- useSubscribeToChanges(identifier, updater);
212
-
213
- return category;
237
+ return samples[0]
214
238
  }
215
239
 
216
- function useSubscribeToChanges<TIdentifier extends HKSampleTypeIdentifier>(
240
+ function useSubscribeToChanges<TIdentifier extends ValueOf<typeof HKSampleTypeIdentifier>>(
217
241
  identifier: TIdentifier,
218
- onChange: () => void
242
+ onChange: () => void,
219
243
  ): void {
220
244
  useEffect(() => {
221
- let cancelSubscription: (() => Promise<boolean>) | undefined;
245
+ let cancelSubscription: (() => Promise<boolean>) | undefined
222
246
 
223
247
  const init = async () => {
224
- cancelSubscription = await subscribeToChanges(identifier, onChange);
225
- };
226
- init();
248
+ cancelSubscription = await subscribeToChanges(identifier, onChange)
249
+ }
250
+ void init()
227
251
 
228
252
  return () => {
229
- cancelSubscription && cancelSubscription();
230
- };
231
- }, [identifier, onChange]);
253
+ void cancelSubscription?.()
254
+ }
255
+ }, [identifier, onChange])
256
+ }
257
+
258
+ useSubscribeToChanges(HKCategoryTypeIdentifier.appleStandHour, () => {})
259
+
260
+ function useMostRecentCategorySample<
261
+ TCategory extends HKCategoryTypeIdentifier
262
+ >(identifier: TCategory) {
263
+ const [category, setCategory] = useState<HKCategorySample<TCategory> | null>(
264
+ null,
265
+ )
266
+ const updater = useCallback(() => {
267
+ void getMostRecentCategorySample(identifier).then(setCategory)
268
+ }, [identifier])
269
+
270
+ useSubscribeToChanges(identifier, updater)
271
+
272
+ return category
232
273
  }
233
274
 
234
275
  function useMostRecentQuantitySample<
@@ -236,41 +277,40 @@ function useMostRecentQuantitySample<
236
277
  TUnit extends HKUnit = HKUnit
237
278
  >(identifier: TIdentifier, unit?: TUnit) {
238
279
  const [lastSample, setLastSample] = useState<HKQuantitySample<
239
- TIdentifier,
240
- TUnit
241
- > | null>(null);
280
+ TIdentifier,
281
+ TUnit
282
+ > | null>(null)
242
283
 
243
284
  useEffect(() => {
244
- let cancelSubscription: (() => Promise<boolean>) | undefined;
285
+ let cancelSubscription: (() => Promise<boolean>) | undefined
245
286
 
246
287
  const init = async () => {
247
- const actualUnit = await ensureUnit(identifier, unit);
288
+ const actualUnit = await ensureUnit(identifier, unit)
248
289
 
249
- cancelSubscription = await subscribeToChanges(identifier, () => {
250
- getMostRecentQuantitySample(identifier, actualUnit).then((value) => {
251
- setLastSample(value);
252
- });
253
- });
254
- };
255
- init();
290
+ cancelSubscription = await subscribeToChanges(identifier, async () => {
291
+ const value = await getMostRecentQuantitySample(identifier, actualUnit)
292
+ setLastSample(value)
293
+ })
294
+ }
295
+ void init()
256
296
 
257
297
  return () => {
258
- cancelSubscription && cancelSubscription();
259
- };
260
- }, [identifier, unit]);
298
+ void cancelSubscription?.()
299
+ }
300
+ }, [identifier, unit])
261
301
 
262
- return lastSample;
302
+ return lastSample
263
303
  }
264
304
 
265
- const saveQuantitySample: SaveQuantitySampleFn = (
305
+ const saveQuantitySample: SaveQuantitySampleFn = async (
266
306
  identifier,
267
307
  unit,
268
308
  value,
269
- options
309
+ options,
270
310
  ) => {
271
- const start = options?.start || options?.end || new Date();
272
- const end = options?.end || options?.start || new Date();
273
- const metadata = options?.metadata || {};
311
+ const start = options?.start || options?.end || new Date()
312
+ const end = options?.end || options?.start || new Date()
313
+ const metadata = options?.metadata || {}
274
314
 
275
315
  return Native.saveQuantitySample(
276
316
  identifier,
@@ -278,131 +318,91 @@ const saveQuantitySample: SaveQuantitySampleFn = (
278
318
  value,
279
319
  start.toISOString(),
280
320
  end.toISOString(),
281
- metadata
282
- );
283
- };
321
+ metadata,
322
+ )
323
+ }
284
324
 
285
325
  const queryStatisticsForQuantity: QueryStatisticsForQuantityFn = async (
286
326
  identifier,
287
327
  options,
288
328
  from,
289
329
  to,
290
- unit
330
+ unit,
291
331
  ) => {
292
- const actualUnit = await ensureUnit(identifier, unit);
293
- const toDate = to || new Date();
294
- const { mostRecentQuantityDateInterval, ...rawResponse } =
295
- await Native.queryStatisticsForQuantity(
296
- identifier,
297
- actualUnit,
298
- from.toISOString(),
299
- toDate.toISOString(),
300
- options
301
- );
332
+ const actualUnit = await ensureUnit(identifier, unit)
333
+ const toDate = to || new Date()
334
+ const { mostRecentQuantityDateInterval, ...rawResponse } = await Native.queryStatisticsForQuantity(
335
+ identifier,
336
+ actualUnit,
337
+ from.toISOString(),
338
+ toDate.toISOString(),
339
+ options,
340
+ )
302
341
 
303
342
  const response = {
304
343
  ...rawResponse,
305
344
  ...(mostRecentQuantityDateInterval
306
345
  ? {
307
- mostRecentQuantityDateInterval: {
308
- from: new Date(mostRecentQuantityDateInterval.from),
309
- to: new Date(mostRecentQuantityDateInterval.to),
310
- },
311
- }
346
+ mostRecentQuantityDateInterval: {
347
+ from: new Date(mostRecentQuantityDateInterval.from),
348
+ to: new Date(mostRecentQuantityDateInterval.to),
349
+ },
350
+ }
312
351
  : {}),
313
- };
352
+ }
314
353
 
315
- return response;
316
- };
354
+ return response
355
+ }
317
356
 
318
- const requestAuthorization = (
319
- read: (HKCharacteristicTypeIdentifier | HKSampleTypeIdentifier)[],
320
- write: HKSampleTypeIdentifier[] = []
357
+ const requestAuthorization = async (
358
+ read: readonly ValueOf<typeof HealthkitAuthorization>[],
359
+ write: readonly ValueOf<typeof HKSampleTypeIdentifier>[] = [],
321
360
  ): Promise<boolean> => {
322
- const readPermissions = read.reduce((obj, cur) => {
323
- return { ...obj, [cur]: true };
324
- }, {});
361
+ const readPermissions = read.reduce((obj, cur) => ({ ...obj, [cur]: true }), {} as ReadPermissions)
325
362
 
326
- const writePermissions = write.reduce((obj, cur) => {
327
- return { ...obj, [cur]: true };
328
- }, {});
363
+ const writePermissions = write.reduce((obj, cur) => ({ ...obj, [cur]: true }), {} as WritePermissions)
329
364
 
330
- return Native.requestAuthorization(writePermissions, readPermissions);
331
- };
365
+ return Native.requestAuthorization(writePermissions, readPermissions)
366
+ }
332
367
 
333
368
  const getDateOfBirth = async () => {
334
- const dateOfBirth = await Native.getDateOfBirth();
335
- return new Date(dateOfBirth);
336
- };
369
+ const dateOfBirth = await Native.getDateOfBirth()
370
+ return new Date(dateOfBirth)
371
+ }
337
372
 
338
- const getRequestStatusForAuthorization = (
339
- read: (HKCharacteristicTypeIdentifier | HKSampleTypeIdentifier)[],
340
- write: HKSampleTypeIdentifier[] = []
373
+ const getRequestStatusForAuthorization = async (
374
+ read: readonly (HKCharacteristicTypeIdentifier | ValueOf<typeof HKSampleTypeIdentifier>)[],
375
+ write: readonly (ValueOf<typeof HKSampleTypeIdentifier>)[] = [],
341
376
  ) => {
342
- const readPermissions = read.reduce((obj, cur) => {
343
- return { ...obj, [cur]: true };
344
- }, {});
377
+ const readPermissions = read.reduce((obj, cur) => ({ ...obj, [cur]: true }), {} as ReadPermissions)
345
378
 
346
- const writePermissions = write.reduce((obj, cur) => {
347
- return { ...obj, [cur]: true };
348
- }, {});
379
+ const writePermissions = write.reduce((obj, cur) => ({ ...obj, [cur]: true }), {} as WritePermissions)
349
380
 
350
381
  return Native.getRequestStatusForAuthorization(
351
382
  writePermissions,
352
- readPermissions
353
- );
354
- };
383
+ readPermissions,
384
+ )
385
+ }
355
386
 
356
387
  const queryCategorySamples: QueryCategorySamplesFn = async (
357
388
  identifier,
358
- options
389
+ options,
359
390
  ) => {
360
- const opts = prepareOptions(options);
391
+ const opts = prepareOptions(options)
361
392
  const results = await Native.queryCategorySamples(
362
393
  identifier,
363
394
  opts.from,
364
395
  opts.to,
365
396
  opts.limit,
366
- opts.ascending
367
- );
397
+ opts.ascending,
398
+ )
368
399
 
369
- return results.map(deserializCategorySample);
370
- };
371
-
372
- async function getPreferredUnitsTyped<
373
- TEnergy extends HKUnit,
374
- TDistance extends HKUnit
375
- >(options?: { energyUnit?: TEnergy; distanceUnit?: TDistance }) {
376
- let energyUnit = options?.energyUnit;
377
- let distanceUnit = options?.distanceUnit;
378
- if (!energyUnit || !distanceUnit) {
379
- const units = await Native.getPreferredUnits([
380
- HKQuantityTypeIdentifier.distanceWalkingRunning,
381
- HKQuantityTypeIdentifier.activeEnergyBurned,
382
- ]);
383
- if (!energyUnit) {
384
- energyUnit = units[HKQuantityTypeIdentifier.distanceWalkingRunning] as
385
- | TEnergy
386
- | undefined;
387
- }
388
- if (!distanceUnit) {
389
- distanceUnit = units[HKQuantityTypeIdentifier.activeEnergyBurned] as
390
- | TDistance
391
- | undefined;
392
- }
393
- }
394
- if (!energyUnit) {
395
- energyUnit = HKUnit.Kilocalories as TEnergy;
396
- }
397
- if (!distanceUnit) {
398
- distanceUnit = HKUnit.Meters as TDistance;
399
- }
400
- return { energyUnit, distanceUnit };
400
+ return results.map(deserializCategorySample)
401
401
  }
402
402
 
403
403
  const queryWorkouts: QueryWorkoutsFn = async (options) => {
404
- const { energyUnit, distanceUnit } = await getPreferredUnitsTyped(options);
405
- const opts = prepareOptions(options);
404
+ const { energyUnit, distanceUnit } = await getPreferredUnitsTyped(options)
405
+ const opts = prepareOptions(options)
406
406
 
407
407
  const workouts = await Native.queryWorkoutSamples(
408
408
  energyUnit,
@@ -410,11 +410,11 @@ const queryWorkouts: QueryWorkoutsFn = async (options) => {
410
410
  opts.from,
411
411
  opts.to,
412
412
  opts.limit,
413
- opts.ascending
414
- );
413
+ opts.ascending,
414
+ )
415
415
 
416
- return workouts.map(deserializeWorkout);
417
- };
416
+ return workouts.map(deserializeWorkout)
417
+ }
418
418
 
419
419
  const getMostRecentWorkout: GetMostRecentWorkoutFn = async (options) => {
420
420
  const workouts = await queryWorkouts({
@@ -422,41 +422,39 @@ const getMostRecentWorkout: GetMostRecentWorkoutFn = async (options) => {
422
422
  ascending: false,
423
423
  energyUnit: options?.energyUnit,
424
424
  distanceUnit: options?.distanceUnit,
425
- });
425
+ })
426
426
 
427
- return workouts[0];
428
- };
427
+ return workouts[0]
428
+ }
429
429
 
430
- function saveCategorySample<T extends HKCategoryTypeIdentifier>(
430
+ async function saveCategorySample<T extends HKCategoryTypeIdentifier>(
431
431
  identifier: T,
432
432
  value: HKCategoryValueForIdentifier<T>,
433
433
  options?: {
434
- start?: Date;
435
- end?: Date;
436
- metadata?: MetadataMapperForCategoryIdentifier<T>;
437
- }
434
+ readonly start?: Date;
435
+ readonly end?: Date;
436
+ readonly metadata?: MetadataMapperForCategoryIdentifier<T>;
437
+ },
438
438
  ) {
439
- const start = options?.start || options?.end || new Date();
440
- const end = options?.end || options?.start || new Date();
441
- const metadata = options?.metadata || {};
439
+ const start = options?.start || options?.end || new Date()
440
+ const end = options?.end || options?.start || new Date()
441
+ const metadata = options?.metadata || {}
442
442
 
443
443
  return Native.saveCategorySample(
444
444
  identifier,
445
445
  value,
446
446
  start.toISOString(),
447
447
  end.toISOString(),
448
- metadata || {}
449
- );
448
+ metadata || {},
449
+ )
450
450
  }
451
451
 
452
452
  const getPreferredUnits: GetPreferredUnitsFn = async (identifiers) => {
453
- const units = await Native.getPreferredUnits(identifiers);
454
- return identifiers.map((i) => units[i]);
455
- };
453
+ const units = await Native.getPreferredUnits(identifiers)
454
+ return identifiers.map((i) => units[i])
455
+ }
456
456
 
457
- const buildUnitWithPrefix = (prefix: HKUnitSIPrefix, unit: HKUnitSI) => {
458
- return `${prefix}${unit}` as HKUnit;
459
- };
457
+ const buildUnitWithPrefix = (prefix: HKUnitSIPrefix, unit: HKUnitSI) => `${prefix}${unit}` as HKUnit
460
458
 
461
459
  function deserializeCorrelation<
462
460
  TIdentifier extends HKCorrelationTypeIdentifier
@@ -464,75 +462,78 @@ function deserializeCorrelation<
464
462
  return {
465
463
  ...s,
466
464
  objects: s.objects.map((o) => {
465
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
467
466
  // @ts-ignore
468
467
  if (o.quantity !== undefined) {
469
- return deserializeSample(o as HKQuantitySampleRaw);
468
+ return deserializeSample(o as HKQuantitySampleRaw)
470
469
  }
471
470
 
472
- return deserializCategorySample(o as HKCategorySampleRaw);
471
+ return deserializCategorySample(o as HKCategorySampleRaw)
473
472
  }),
474
473
  endDate: new Date(s.endDate),
475
474
  startDate: new Date(s.startDate),
476
- };
475
+ }
477
476
  }
478
477
 
479
478
  function ensureMetadata<TMetadata>(metadata?: TMetadata) {
480
- return metadata || ({} as TMetadata);
479
+ return metadata || ({} as TMetadata)
481
480
  }
482
481
 
483
482
  const queryCorrelationSamples: QueryCorrelationSamplesFn = async (
484
483
  typeIdentifier,
485
- options
484
+ options,
486
485
  ) => {
487
- const opts = prepareOptions(options);
486
+ const opts = prepareOptions(options)
488
487
  const correlations = await Native.queryCorrelationSamples(
489
488
  typeIdentifier,
490
489
  opts.from,
491
- opts.to
492
- );
490
+ opts.to,
491
+ )
493
492
 
494
- return correlations.map(deserializeCorrelation);
495
- };
493
+ return correlations.map(deserializeCorrelation)
494
+ }
496
495
 
497
496
  const saveCorrelationSample: SaveCorrelationSampleFn = async (
498
497
  typeIdentifier,
499
498
  samples,
500
- options
499
+ options,
501
500
  ) => {
502
- const start = (options?.start || new Date()).toISOString();
503
- const end = (options?.end || new Date()).toISOString();
501
+ const start = (options?.start || new Date()).toISOString()
502
+ const end = (options?.end || new Date()).toISOString()
504
503
 
505
504
  return Native.saveCorrelationSample(
506
505
  typeIdentifier,
507
506
  samples,
508
507
  start,
509
508
  end,
510
- ensureMetadata(options?.metadata)
511
- );
512
- };
509
+ ensureMetadata(options?.metadata),
510
+ )
511
+ }
513
512
 
514
- const saveWorkoutSample: SaveWorkoutSampleFn = (
513
+ const saveWorkoutSample: SaveWorkoutSampleFn = async (
515
514
  typeIdentifier,
516
515
  quantities,
517
516
  _start,
518
- options
517
+ options,
519
518
  ) => {
520
- const start = _start.toISOString();
521
- const end = (options?.end || new Date()).toISOString();
519
+ const start = _start.toISOString()
520
+ const end = (options?.end || new Date()).toISOString()
522
521
 
523
522
  return Native.saveWorkoutSample(
524
523
  typeIdentifier,
525
524
  quantities,
526
525
  start,
527
526
  end,
528
- ensureMetadata(options?.metadata)
529
- );
530
- };
527
+ ensureMetadata(options?.metadata),
528
+ )
529
+ }
530
+
531
+ const getWorkoutRoutes: GetWorkoutRoutesFn = async (workoutUUID: string) => Native.getWorkoutRoutes(workoutUUID)
531
532
 
532
533
  const Healthkit: ReactNativeHealthkit = {
533
- authorizationStatusFor: Native.authorizationStatusFor,
534
+ authorizationStatusFor: Native.authorizationStatusFor.bind(Native),
534
535
 
535
- isHealthDataAvailable: Native.isHealthDataAvailable,
536
+ isHealthDataAvailable: Native.isHealthDataAvailable.bind(Native),
536
537
 
537
538
  buildUnitWithPrefix,
538
539
 
@@ -541,10 +542,10 @@ const Healthkit: ReactNativeHealthkit = {
541
542
  enableBackgroundDelivery: Native.enableBackgroundDelivery,
542
543
 
543
544
  // simple convenience getters
544
- getBiologicalSex: Native.getBiologicalSex,
545
- getFitzpatrickSkinType: Native.getFitzpatrickSkinType,
545
+ getBiologicalSex: Native.getBiologicalSex.bind(Native),
546
+ getFitzpatrickSkinType: Native.getFitzpatrickSkinType.bind(Native),
546
547
  getWheelchairUse: Native.getWheelchairUse,
547
- getBloodType: Native.getBloodType,
548
+ getBloodType: Native.getBloodType.bind(Native),
548
549
  getDateOfBirth,
549
550
 
550
551
  getMostRecentQuantitySample,
@@ -555,6 +556,8 @@ const Healthkit: ReactNativeHealthkit = {
555
556
  getPreferredUnits,
556
557
  getRequestStatusForAuthorization,
557
558
 
559
+ getWorkoutRoutes,
560
+
558
561
  // query methods
559
562
  queryCategorySamples,
560
563
  queryCorrelationSamples,
@@ -580,9 +583,40 @@ const Healthkit: ReactNativeHealthkit = {
580
583
  useMostRecentWorkout,
581
584
 
582
585
  useSubscribeToChanges,
583
- };
584
586
 
585
- export * from './types';
586
- export * from './native-types';
587
+ useIsHealthDataAvailable: () => {
588
+ const [isAvailable, setIsAvailable] = useState<boolean | null>(null)
589
+ useEffect(() => {
590
+ const init = async () => {
591
+ const res = await Native.isHealthDataAvailable()
592
+ setIsAvailable(res)
593
+ }
594
+ void init()
595
+ }, [])
596
+ return isAvailable
597
+ },
598
+ useHealthkitAuthorization: (read, write) => {
599
+ const [status, setStatus] = useState<HKAuthorizationRequestStatus | null>(null)
600
+ const refreshAuthStatus = useCallback(async () => {
601
+ const auth = await getRequestStatusForAuthorization(read, write)
602
+ setStatus(auth)
603
+ return auth
604
+ }, [])
605
+
606
+ const request = useCallback(async () => {
607
+ await requestAuthorization(read, write)
608
+ return refreshAuthStatus()
609
+ }, [])
610
+
611
+ useEffect(() => {
612
+ void refreshAuthStatus()
613
+ }, [])
614
+
615
+ return [status, request]
616
+ },
617
+ }
618
+
619
+ export * from './native-types'
620
+ export * from './types'
587
621
 
588
- export default Healthkit;
622
+ export default Healthkit