@kingstinct/react-native-healthkit 12.0.0 → 12.1.0

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.
package/README.md CHANGED
@@ -84,14 +84,16 @@ Some imperative examples:
84
84
  const isAvailable = await isHealthDataAvailable();
85
85
 
86
86
  /* Read latest sample of any data */
87
- await requestAuthorization(['HKQuantityTypeIdentifierBodyFatPercentage']); // request read permission for bodyFatPercentage
87
+ await requestAuthorization({ toRead: ['HKQuantityTypeIdentifierBodyFatPercentage'] }); // request read permission for bodyFatPercentage
88
88
 
89
89
  const { quantity, unit, startDate, endDate } = await getMostRecentQuantitySample('HKQuantityTypeIdentifierBodyFatPercentage'); // read latest sample
90
90
 
91
91
  console.log(quantity) // 17.5
92
92
  console.log(unit) // %
93
93
 
94
- await requestAuthorization(['HKQuantityTypeIdentifierHeartRate']); // request read permission for heart rate
94
+ await requestAuthorization({
95
+ toRead: ['HKQuantityTypeIdentifierHeartRate']
96
+ }); // request read permission for heart rate
95
97
 
96
98
  /* Subscribe to data (Make sure to request permissions before subscribing to changes) */
97
99
  const [hasRequestedAuthorization, setHasRequestedAuthorization] = useState(false);
@@ -113,7 +115,7 @@ Some imperative examples:
113
115
  }, [hasRequestedAuthorization]);
114
116
 
115
117
  /* write data */
116
- await requestAuthorization([], ['HKQuantityTypeIdentifierInsulinDelivery']); // request write permission for insulin delivery
118
+ await requestAuthorization({ toShare: ['HKQuantityTypeIdentifierInsulinDelivery'] }); // request write permission for insulin delivery
117
119
 
118
120
  saveQuantitySample(
119
121
  'HKQuantityTypeIdentifierInsulinDelivery',
package/ios/Helpers.swift CHANGED
@@ -29,7 +29,7 @@ func getQueryLimit(_ limit: Double?) -> Int {
29
29
  return Int(limit)
30
30
  }
31
31
 
32
- return DEFAULT_QUERY_LIMIT
32
+ return HKObjectQueryNoLimit
33
33
  }
34
34
 
35
35
  func createPredicateForWorkout(filter: PredicateForWorkouts) throws -> NSPredicate {
@@ -408,16 +408,7 @@ class QuantityTypeModule: HybridQuantityTypeModuleSpec {
408
408
  let deletedSamples = deletedSamples?.map { serializeDeletedSample(sample: $0) } ?? []
409
409
  let newAnchor = serializeAnchor(anchor: newAnchor) ?? ""
410
410
 
411
- guard let samples = samples else {
412
- let response = QuantitySamplesWithAnchorResponse(
413
- samples: [],
414
- deletedSamples: deletedSamples,
415
- newAnchor: newAnchor
416
- )
417
- return continuation.resume(returning: response)
418
- }
419
-
420
- let quantitySamples = samples.compactMap { sample in
411
+ let quantitySamples = samples?.compactMap { sample in
421
412
  if let quantitySample = sample as? HKQuantitySample {
422
413
  do {
423
414
  return try serializeQuantitySample(
@@ -432,7 +423,7 @@ class QuantityTypeModule: HybridQuantityTypeModuleSpec {
432
423
  }
433
424
 
434
425
  let response = QuantitySamplesWithAnchorResponse(
435
- samples: quantitySamples,
426
+ samples: quantitySamples ?? [],
436
427
  deletedSamples: deletedSamples,
437
428
  newAnchor: newAnchor,
438
429
  )
@@ -212,15 +212,17 @@ class WorkoutsModule: HybridWorkoutsModuleSpec {
212
212
  store.save(workout) { (_: Bool, error: Error?) in
213
213
  if let error = error {
214
214
  return continuation.resume(throwing: error)
215
- }
216
- if !initializedSamples.isEmpty {
215
+ } else if !initializedSamples.isEmpty {
217
216
  store.add(initializedSamples, to: workout) { (_, error: Error?) in
218
217
  if let error = error {
219
218
  return continuation.resume(throwing: error)
220
219
  }
220
+ return continuation.resume()
221
221
  }
222
+ } else {
223
+ return continuation.resume()
222
224
  }
223
- return continuation.resume()
225
+
224
226
  }
225
227
  }) as Void
226
228
 
@@ -17,8 +17,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
17
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
18
18
  };
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.saveStateOfMindSample = exports.queryStateOfMindSamples = exports.isProtectedDataAvailable = exports.startWatchApp = exports.subscribeToChanges = exports.saveWorkoutSample = exports.saveQuantitySample = exports.saveCorrelationSample = exports.saveCategorySample = exports.deleteObjects = exports.requestAuthorization = exports.querySources = exports.queryWorkoutSamplesWithAnchor = exports.queryWorkoutSamples = exports.queryStatisticsCollectionForQuantity = exports.queryStatisticsForQuantity = exports.queryQuantitySamplesWithAnchor = exports.queryQuantitySamples = exports.queryElectrocardiogramSamplesWithAnchor = exports.queryElectrocardiogramSamples = exports.queryHeartbeatSeriesSamplesWithAnchor = exports.queryHeartbeatSeriesSamples = exports.queryCorrelationSamples = exports.queryCategorySamplesWithAnchor = exports.queryCategorySamples = exports.isHealthDataAvailableAsync = exports.isHealthDataAvailable = exports.getWheelchairUse = exports.getRequestStatusForAuthorization = exports.getPreferredUnits = exports.getFitzpatrickSkinType = exports.getDateOfBirth = exports.getBloodType = exports.getBiologicalSex = exports.enableBackgroundDelivery = exports.disableBackgroundDelivery = exports.disableAllBackgroundDelivery = exports.authorizationStatusFor = exports.useStatisticsForQuantity = exports.useSources = exports.useIsHealthDataAvailable = exports.useHealthkitAuthorization = exports.useSubscribeToChanges = exports.useMostRecentWorkout = exports.useMostRecentQuantitySample = exports.useMostRecentCategorySample = exports.getPreferredUnit = exports.getMostRecentWorkout = exports.getMostRecentQuantitySample = exports.getMostRecentCategorySample = void 0;
21
- exports.getWheelchairUseAsync = exports.getFitzpatrickSkinTypeAsync = exports.getDateOfBirthAsync = exports.getBloodTypeAsync = exports.getBiologicalSexAsync = exports.areObjectTypesAvailableAsync = exports.areObjectTypesAvailable = exports.isObjectTypeAvailableAsync = exports.isObjectTypeAvailable = exports.unsubscribeQueries = exports.isQuantityCompatibleWithUnit = void 0;
20
+ exports.isProtectedDataAvailable = exports.startWatchApp = exports.saveWorkoutSample = exports.saveQuantitySample = exports.saveCorrelationSample = exports.saveCategorySample = exports.deleteObjects = exports.requestAuthorization = exports.querySources = exports.queryWorkoutSamplesWithAnchor = exports.queryWorkoutSamples = exports.queryStatisticsCollectionForQuantity = exports.queryStatisticsForQuantity = exports.queryQuantitySamplesWithAnchor = exports.queryQuantitySamples = exports.queryElectrocardiogramSamplesWithAnchor = exports.queryElectrocardiogramSamples = exports.queryHeartbeatSeriesSamplesWithAnchor = exports.queryHeartbeatSeriesSamples = exports.queryCorrelationSamples = exports.queryCategorySamplesWithAnchor = exports.queryCategorySamples = exports.isHealthDataAvailableAsync = exports.isHealthDataAvailable = exports.getWheelchairUse = exports.getRequestStatusForAuthorization = exports.getPreferredUnits = exports.getFitzpatrickSkinType = exports.getDateOfBirth = exports.getBloodType = exports.getBiologicalSex = exports.enableBackgroundDelivery = exports.disableBackgroundDelivery = exports.disableAllBackgroundDelivery = exports.authorizationStatusFor = exports.useSubscribeToQuantitySamples = exports.useSubscribeToChanges = exports.useStatisticsForQuantity = exports.useSources = exports.useMostRecentWorkout = exports.useMostRecentQuantitySample = exports.useMostRecentCategorySample = exports.useIsHealthDataAvailable = exports.useHealthkitAuthorization = exports.subscribeToQuantitySamples = exports.subscribeToChanges = exports.getPreferredUnit = exports.getMostRecentWorkout = exports.getMostRecentQuantitySample = exports.getMostRecentCategorySample = void 0;
21
+ exports.getWheelchairUseAsync = exports.getFitzpatrickSkinTypeAsync = exports.getDateOfBirthAsync = exports.getBloodTypeAsync = exports.getBiologicalSexAsync = exports.areObjectTypesAvailableAsync = exports.areObjectTypesAvailable = exports.isObjectTypeAvailableAsync = exports.isObjectTypeAvailable = exports.isQuantityCompatibleWithUnit = exports.saveStateOfMindSample = exports.queryStateOfMindSamples = void 0;
22
22
  const react_native_1 = require("react-native");
23
23
  const useHealthkitAuthorization_1 = __importDefault(require("./hooks/useHealthkitAuthorization"));
24
24
  exports.useHealthkitAuthorization = useHealthkitAuthorization_1.default;
@@ -36,6 +36,8 @@ const useStatisticsForQuantity_1 = __importDefault(require("./hooks/useStatistic
36
36
  exports.useStatisticsForQuantity = useStatisticsForQuantity_1.default;
37
37
  const useSubscribeToChanges_1 = __importDefault(require("./hooks/useSubscribeToChanges"));
38
38
  exports.useSubscribeToChanges = useSubscribeToChanges_1.default;
39
+ const useSubscribeToQuantitySamples_1 = __importDefault(require("./hooks/useSubscribeToQuantitySamples"));
40
+ exports.useSubscribeToQuantitySamples = useSubscribeToQuantitySamples_1.default;
39
41
  const modules_1 = require("./modules");
40
42
  const getMostRecentCategorySample_1 = __importDefault(require("./utils/getMostRecentCategorySample"));
41
43
  exports.getMostRecentCategorySample = getMostRecentCategorySample_1.default;
@@ -45,6 +47,10 @@ const getMostRecentWorkout_1 = __importDefault(require("./utils/getMostRecentWor
45
47
  exports.getMostRecentWorkout = getMostRecentWorkout_1.default;
46
48
  const getPreferredUnit_1 = __importDefault(require("./utils/getPreferredUnit"));
47
49
  exports.getPreferredUnit = getPreferredUnit_1.default;
50
+ const subscribeToChanges_1 = require("./utils/subscribeToChanges");
51
+ Object.defineProperty(exports, "subscribeToChanges", { enumerable: true, get: function () { return subscribeToChanges_1.subscribeToChanges; } });
52
+ const subscribeToQuantitySamples_1 = require("./utils/subscribeToQuantitySamples");
53
+ Object.defineProperty(exports, "subscribeToQuantitySamples", { enumerable: true, get: function () { return subscribeToQuantitySamples_1.subscribeToQuantitySamples; } });
48
54
  __exportStar(require("./types"), exports);
49
55
  const currentMajorVersionIOS = react_native_1.Platform.OS === 'ios' ? Number.parseInt(react_native_1.Platform.Version, 10) : 0;
50
56
  // Named exports - all functions bound to their respective modules
@@ -81,13 +87,11 @@ exports.saveCategorySample = modules_1.CategoryTypes.saveCategorySample.bind(mod
81
87
  exports.saveCorrelationSample = modules_1.CorrelationTypes.saveCorrelationSample.bind(modules_1.CorrelationTypes);
82
88
  exports.saveQuantitySample = modules_1.QuantityTypes.saveQuantitySample.bind(modules_1.QuantityTypes);
83
89
  exports.saveWorkoutSample = modules_1.Workouts.saveWorkoutSample.bind(modules_1.Workouts);
84
- exports.subscribeToChanges = modules_1.Core.subscribeToObserverQuery.bind(modules_1.Core);
85
90
  exports.startWatchApp = modules_1.Workouts.startWatchAppWithWorkoutConfiguration.bind(modules_1.Workouts);
86
91
  exports.isProtectedDataAvailable = modules_1.Core.isProtectedDataAvailable.bind(modules_1.Core);
87
92
  exports.queryStateOfMindSamples = modules_1.StateOfMind.queryStateOfMindSamples.bind(modules_1.StateOfMind);
88
93
  exports.saveStateOfMindSample = modules_1.StateOfMind.saveStateOfMindSample.bind(modules_1.StateOfMind);
89
94
  exports.isQuantityCompatibleWithUnit = modules_1.QuantityTypes.isQuantityCompatibleWithUnit.bind(modules_1.QuantityTypes);
90
- exports.unsubscribeQueries = modules_1.Core.unsubscribeQueries.bind(modules_1.Core);
91
95
  exports.isObjectTypeAvailable = modules_1.Core.isObjectTypeAvailable.bind(modules_1.Core);
92
96
  exports.isObjectTypeAvailableAsync = modules_1.Core.isObjectTypeAvailableAsync.bind(modules_1.Core);
93
97
  exports.areObjectTypesAvailable = modules_1.Core.areObjectTypesAvailable.bind(modules_1.Core);
@@ -145,8 +149,8 @@ exports.default = {
145
149
  saveCorrelationSample: exports.saveCorrelationSample,
146
150
  saveQuantitySample: exports.saveQuantitySample,
147
151
  saveWorkoutSample: exports.saveWorkoutSample,
148
- subscribeToChanges: exports.subscribeToChanges,
149
- unsubscribeQueries: exports.unsubscribeQueries,
152
+ subscribeToChanges: subscribeToChanges_1.subscribeToChanges,
153
+ subscribeToQuantitySamples: subscribeToQuantitySamples_1.subscribeToQuantitySamples,
150
154
  startWatchApp: exports.startWatchApp,
151
155
  isProtectedDataAvailable: exports.isProtectedDataAvailable,
152
156
  queryStateOfMindSamples: exports.queryStateOfMindSamples,
@@ -156,6 +160,7 @@ exports.default = {
156
160
  useMostRecentQuantitySample: useMostRecentQuantitySample_1.default,
157
161
  useMostRecentWorkout: useMostRecentWorkout_1.default,
158
162
  useSubscribeToChanges: useSubscribeToChanges_1.default,
163
+ useSubscribeToQuantitySamples: useSubscribeToQuantitySamples_1.default,
159
164
  useHealthkitAuthorization: useHealthkitAuthorization_1.default,
160
165
  useIsHealthDataAvailable: useIsHealthDataAvailable_1.useIsHealthDataAvailable,
161
166
  useSources: useSources_1.default,
@@ -15,7 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.useSources = exports.useIsHealthDataAvailable = exports.useHealthkitAuthorization = exports.useSubscribeToChanges = exports.useMostRecentWorkout = exports.useMostRecentQuantitySample = exports.getPreferredUnit = exports.getMostRecentWorkout = exports.getMostRecentQuantitySample = exports.saveStateOfMindSample = exports.queryStateOfMindSamples = exports.startWatchApp = exports.saveWorkoutSample = exports.queryWorkoutSamplesWithAnchor = exports.queryWorkoutSamples = exports.queryElectrocardiogramSamplesWithAnchor = exports.queryElectrocardiogramSamples = exports.queryHeartbeatSeriesSamplesWithAnchor = exports.queryHeartbeatSeriesSamples = exports.saveCorrelationSample = exports.queryCorrelationSamples = exports.saveCategorySample = exports.isQuantityCompatibleWithUnit = exports.saveQuantitySample = exports.queryStatisticsCollectionForQuantity = exports.queryStatisticsForQuantity = exports.queryQuantitySamplesWithAnchor = exports.queryQuantitySamples = exports.getWheelchairUse = exports.getFitzpatrickSkinType = exports.getDateOfBirth = exports.getBloodType = exports.getBiologicalSex = exports.areObjectTypesAvailableAsync = exports.areObjectTypesAvailable = exports.isObjectTypeAvailableAsync = exports.isObjectTypeAvailable = exports.isProtectedDataAvailable = exports.subscribeToChanges = exports.deleteObjects = exports.requestAuthorization = exports.querySources = exports.isHealthDataAvailableAsync = exports.isHealthDataAvailable = exports.getRequestStatusForAuthorization = exports.getPreferredUnits = exports.enableBackgroundDelivery = exports.disableBackgroundDelivery = exports.disableAllBackgroundDelivery = exports.authorizationStatusFor = void 0;
18
- exports.unsubscribeQueries = exports.getWheelchairUseAsync = exports.getFitzpatrickSkinTypeAsync = exports.getDateOfBirthAsync = exports.getBloodTypeAsync = exports.getBiologicalSexAsync = exports.useStatisticsForQuantity = void 0;
18
+ exports.useSubscribeToQuantitySamples = exports.subscribeToQuantitySamples = exports.getWheelchairUseAsync = exports.getFitzpatrickSkinTypeAsync = exports.getDateOfBirthAsync = exports.getBloodTypeAsync = exports.getBiologicalSexAsync = exports.useStatisticsForQuantity = void 0;
19
19
  exports.queryCategorySamples = queryCategorySamples;
20
20
  exports.queryCategorySamplesWithAnchor = queryCategorySamplesWithAnchor;
21
21
  exports.getMostRecentCategorySample = getMostRecentCategorySample;
@@ -49,7 +49,9 @@ exports.isHealthDataAvailableAsync = UnavailableFnFromModule('isHealthDataAvaila
49
49
  exports.querySources = UnavailableFnFromModule('querySources', Promise.resolve([]));
50
50
  exports.requestAuthorization = UnavailableFnFromModule('requestAuthorization', Promise.resolve(false));
51
51
  exports.deleteObjects = UnavailableFnFromModule('deleteObjects', Promise.resolve(0));
52
- exports.subscribeToChanges = UnavailableFnFromModule('subscribeToChanges', 'dummy-query-uuid'); // Mocking the observer query UUID
52
+ exports.subscribeToChanges = UnavailableFnFromModule('subscribeToChanges', {
53
+ remove: () => false,
54
+ }); // Mocking the observer query UUID
53
55
  exports.isProtectedDataAvailable = UnavailableFnFromModule('isProtectedDataAvailable', false);
54
56
  exports.isObjectTypeAvailable = UnavailableFnFromModule('isObjectTypeAvailable', false);
55
57
  exports.isObjectTypeAvailableAsync = UnavailableFnFromModule('isObjectTypeAvailableAsync', Promise.resolve(false));
@@ -160,14 +162,18 @@ exports.getBloodTypeAsync = UnavailableFnFromModule('getBloodTypeAsync', Promise
160
162
  exports.getDateOfBirthAsync = UnavailableFnFromModule('getDateOfBirthAsync', Promise.resolve(new Date(0))); // Assuming string for date
161
163
  exports.getFitzpatrickSkinTypeAsync = UnavailableFnFromModule('getFitzpatrickSkinTypeAsync', Promise.resolve(Characteristics_1.FitzpatrickSkinType.notSet));
162
164
  exports.getWheelchairUseAsync = UnavailableFnFromModule('getWheelchairUseAsync', Promise.resolve(Characteristics_1.WheelchairUse.notSet));
163
- exports.unsubscribeQueries = UnavailableFnFromModule('unsubscribeQueries', 0);
165
+ const subscribeToQuantitySamples = UnavailableFnFromModule('subscribeToQuantitySamples', {
166
+ remove: () => false,
167
+ }); // Mocking the observer query UUID
168
+ exports.subscribeToQuantitySamples = subscribeToQuantitySamples;
169
+ const useSubscribeToQuantitySamples = UnavailableFnFromModule('useSubscribeToQuantitySamples', undefined); // Mocking callback structure
170
+ exports.useSubscribeToQuantitySamples = useSubscribeToQuantitySamples;
164
171
  // --- Default Export ---
165
172
  // This attempts to match the structure of the default export from index.ios.ts
166
173
  const HealthkitModule = {
167
174
  // All named exports are also part of the default export object
168
175
  authorizationStatusFor: exports.authorizationStatusFor,
169
176
  isObjectTypeAvailable: exports.isObjectTypeAvailable,
170
- unsubscribeQueries: exports.unsubscribeQueries,
171
177
  isObjectTypeAvailableAsync: exports.isObjectTypeAvailableAsync,
172
178
  areObjectTypesAvailable: exports.areObjectTypesAvailable,
173
179
  areObjectTypesAvailableAsync: exports.areObjectTypesAvailableAsync,
@@ -213,6 +219,8 @@ const HealthkitModule = {
213
219
  isProtectedDataAvailable: exports.isProtectedDataAvailable,
214
220
  queryStateOfMindSamples: exports.queryStateOfMindSamples,
215
221
  saveStateOfMindSample: exports.saveStateOfMindSample,
222
+ subscribeToQuantitySamples,
223
+ useSubscribeToQuantitySamples,
216
224
  // Hooks
217
225
  useMostRecentCategorySample,
218
226
  useMostRecentQuantitySample: exports.useMostRecentQuantitySample,
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useSubscribeToQuantitySamples = useSubscribeToQuantitySamples;
4
+ const react_1 = require("react");
5
+ const subscribeToQuantitySamples_1 = require("../utils/subscribeToQuantitySamples");
6
+ function useSubscribeToQuantitySamples(identifier, onChange) {
7
+ const onChangeRef = (0, react_1.useRef)(onChange);
8
+ (0, react_1.useEffect)(() => {
9
+ onChangeRef.current = onChange;
10
+ }, [onChange]);
11
+ (0, react_1.useEffect)(() => {
12
+ const subscription = (0, subscribeToQuantitySamples_1.subscribeToQuantitySamples)(identifier, (args) => {
13
+ onChangeRef.current(args);
14
+ });
15
+ return () => {
16
+ subscription.remove();
17
+ };
18
+ }, [identifier]);
19
+ }
20
+ exports.default = useSubscribeToQuantitySamples;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.subscribeToQuantitySamples = void 0;
4
+ const modules_1 = require("../modules");
5
+ const subscribeToChanges_1 = require("./subscribeToChanges");
6
+ const subscribeToQuantitySamples = (identifier, callback, after) => {
7
+ let anchor;
8
+ const afterDate = after ?? new Date();
9
+ const init = async () => {
10
+ // we need to do an initial query to get a handle for the deletedSamples
11
+ const { newAnchor } = await modules_1.QuantityTypes.queryQuantitySamplesWithAnchor(identifier, {
12
+ filter: {
13
+ startDate: afterDate,
14
+ },
15
+ });
16
+ anchor = newAnchor;
17
+ };
18
+ init();
19
+ return (0, subscribeToChanges_1.subscribeToChanges)(identifier, async ({ errorMessage }) => {
20
+ if (errorMessage) {
21
+ return callback({
22
+ typeIdentifier: identifier,
23
+ errorMessage,
24
+ });
25
+ }
26
+ // seems like all deletedSamples are included when no anchor is provided, so we don't want to return those for the first query
27
+ const hadAnchorWhenDoingQuery = !!anchor;
28
+ const { samples, newAnchor, deletedSamples } = await modules_1.QuantityTypes.queryQuantitySamplesWithAnchor(identifier, {
29
+ anchor,
30
+ filter: {
31
+ startDate: afterDate,
32
+ },
33
+ });
34
+ anchor = newAnchor;
35
+ const hasNewSamples = samples.length > 0 ||
36
+ (deletedSamples.length > 0 && hadAnchorWhenDoingQuery);
37
+ if (hasNewSamples) {
38
+ callback({
39
+ typeIdentifier: identifier,
40
+ samples,
41
+ anchor: newAnchor,
42
+ deletedSamples: hadAnchorWhenDoingQuery ? deletedSamples : [],
43
+ });
44
+ }
45
+ });
46
+ };
47
+ exports.subscribeToQuantitySamples = subscribeToQuantitySamples;
@@ -7,14 +7,17 @@ import useMostRecentWorkout from './hooks/useMostRecentWorkout';
7
7
  import useSources from './hooks/useSources';
8
8
  import useStatisticsForQuantity from './hooks/useStatisticsForQuantity';
9
9
  import useSubscribeToChanges from './hooks/useSubscribeToChanges';
10
+ import useSubscribeToQuantitySamples from './hooks/useSubscribeToQuantitySamples';
10
11
  import { CategoryTypes, Characteristics, Core, CorrelationTypes, Electrocardiograms, HeartbeatSeries, QuantityTypes, StateOfMind, Workouts, } from './modules';
11
12
  import getMostRecentCategorySample from './utils/getMostRecentCategorySample';
12
13
  import getMostRecentQuantitySample from './utils/getMostRecentQuantitySample';
13
14
  import getMostRecentWorkout from './utils/getMostRecentWorkout';
14
15
  import getPreferredUnit from './utils/getPreferredUnit';
16
+ import { subscribeToChanges } from './utils/subscribeToChanges';
17
+ import { subscribeToQuantitySamples } from './utils/subscribeToQuantitySamples';
15
18
  export * from './types';
16
19
  const currentMajorVersionIOS = Platform.OS === 'ios' ? Number.parseInt(Platform.Version, 10) : 0;
17
- export { getMostRecentCategorySample, getMostRecentQuantitySample, getMostRecentWorkout, getPreferredUnit, useMostRecentCategorySample, useMostRecentQuantitySample, useMostRecentWorkout, useSubscribeToChanges, useHealthkitAuthorization, useIsHealthDataAvailable, useSources, useStatisticsForQuantity, };
20
+ export { getMostRecentCategorySample, getMostRecentQuantitySample, getMostRecentWorkout, getPreferredUnit, subscribeToChanges, subscribeToQuantitySamples, useHealthkitAuthorization, useIsHealthDataAvailable, useMostRecentCategorySample, useMostRecentQuantitySample, useMostRecentWorkout, useSources, useStatisticsForQuantity, useSubscribeToChanges, useSubscribeToQuantitySamples, };
18
21
  // Named exports - all functions bound to their respective modules
19
22
  export const authorizationStatusFor = Core.authorizationStatusFor.bind(Core);
20
23
  export const disableAllBackgroundDelivery = Core.disableAllBackgroundDelivery.bind(Core);
@@ -49,13 +52,11 @@ export const saveCategorySample = CategoryTypes.saveCategorySample.bind(Category
49
52
  export const saveCorrelationSample = CorrelationTypes.saveCorrelationSample.bind(CorrelationTypes);
50
53
  export const saveQuantitySample = QuantityTypes.saveQuantitySample.bind(QuantityTypes);
51
54
  export const saveWorkoutSample = Workouts.saveWorkoutSample.bind(Workouts);
52
- export const subscribeToChanges = Core.subscribeToObserverQuery.bind(Core);
53
55
  export const startWatchApp = Workouts.startWatchAppWithWorkoutConfiguration.bind(Workouts);
54
56
  export const isProtectedDataAvailable = Core.isProtectedDataAvailable.bind(Core);
55
57
  export const queryStateOfMindSamples = StateOfMind.queryStateOfMindSamples.bind(StateOfMind);
56
58
  export const saveStateOfMindSample = StateOfMind.saveStateOfMindSample.bind(StateOfMind);
57
59
  export const isQuantityCompatibleWithUnit = QuantityTypes.isQuantityCompatibleWithUnit.bind(QuantityTypes);
58
- export const unsubscribeQueries = Core.unsubscribeQueries.bind(Core);
59
60
  export const isObjectTypeAvailable = Core.isObjectTypeAvailable.bind(Core);
60
61
  export const isObjectTypeAvailableAsync = Core.isObjectTypeAvailableAsync.bind(Core);
61
62
  export const areObjectTypesAvailable = Core.areObjectTypesAvailable.bind(Core);
@@ -114,7 +115,7 @@ export default {
114
115
  saveQuantitySample,
115
116
  saveWorkoutSample,
116
117
  subscribeToChanges,
117
- unsubscribeQueries,
118
+ subscribeToQuantitySamples,
118
119
  startWatchApp,
119
120
  isProtectedDataAvailable,
120
121
  queryStateOfMindSamples,
@@ -124,6 +125,7 @@ export default {
124
125
  useMostRecentQuantitySample,
125
126
  useMostRecentWorkout,
126
127
  useSubscribeToChanges,
128
+ useSubscribeToQuantitySamples,
127
129
  useHealthkitAuthorization,
128
130
  useIsHealthDataAvailable,
129
131
  useSources,
@@ -27,7 +27,9 @@ export const isHealthDataAvailableAsync = UnavailableFnFromModule('isHealthDataA
27
27
  export const querySources = UnavailableFnFromModule('querySources', Promise.resolve([]));
28
28
  export const requestAuthorization = UnavailableFnFromModule('requestAuthorization', Promise.resolve(false));
29
29
  export const deleteObjects = UnavailableFnFromModule('deleteObjects', Promise.resolve(0));
30
- export const subscribeToChanges = UnavailableFnFromModule('subscribeToChanges', 'dummy-query-uuid'); // Mocking the observer query UUID
30
+ export const subscribeToChanges = UnavailableFnFromModule('subscribeToChanges', {
31
+ remove: () => false,
32
+ }); // Mocking the observer query UUID
31
33
  export const isProtectedDataAvailable = UnavailableFnFromModule('isProtectedDataAvailable', false);
32
34
  export const isObjectTypeAvailable = UnavailableFnFromModule('isObjectTypeAvailable', false);
33
35
  export const isObjectTypeAvailableAsync = UnavailableFnFromModule('isObjectTypeAvailableAsync', Promise.resolve(false));
@@ -138,14 +140,18 @@ export const getBloodTypeAsync = UnavailableFnFromModule('getBloodTypeAsync', Pr
138
140
  export const getDateOfBirthAsync = UnavailableFnFromModule('getDateOfBirthAsync', Promise.resolve(new Date(0))); // Assuming string for date
139
141
  export const getFitzpatrickSkinTypeAsync = UnavailableFnFromModule('getFitzpatrickSkinTypeAsync', Promise.resolve(FitzpatrickSkinType.notSet));
140
142
  export const getWheelchairUseAsync = UnavailableFnFromModule('getWheelchairUseAsync', Promise.resolve(WheelchairUse.notSet));
141
- export const unsubscribeQueries = UnavailableFnFromModule('unsubscribeQueries', 0);
143
+ const subscribeToQuantitySamples = UnavailableFnFromModule('subscribeToQuantitySamples', {
144
+ remove: () => false,
145
+ }); // Mocking the observer query UUID
146
+ export { subscribeToQuantitySamples };
147
+ const useSubscribeToQuantitySamples = UnavailableFnFromModule('useSubscribeToQuantitySamples', undefined); // Mocking callback structure
148
+ export { useSubscribeToQuantitySamples };
142
149
  // --- Default Export ---
143
150
  // This attempts to match the structure of the default export from index.ios.ts
144
151
  const HealthkitModule = {
145
152
  // All named exports are also part of the default export object
146
153
  authorizationStatusFor,
147
154
  isObjectTypeAvailable,
148
- unsubscribeQueries,
149
155
  isObjectTypeAvailableAsync,
150
156
  areObjectTypesAvailable,
151
157
  areObjectTypesAvailableAsync,
@@ -191,6 +197,8 @@ const HealthkitModule = {
191
197
  isProtectedDataAvailable,
192
198
  queryStateOfMindSamples,
193
199
  saveStateOfMindSample,
200
+ subscribeToQuantitySamples,
201
+ useSubscribeToQuantitySamples,
194
202
  // Hooks
195
203
  useMostRecentCategorySample,
196
204
  useMostRecentQuantitySample,
@@ -0,0 +1,17 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import { subscribeToQuantitySamples } from '../utils/subscribeToQuantitySamples';
3
+ export function useSubscribeToQuantitySamples(identifier, onChange) {
4
+ const onChangeRef = useRef(onChange);
5
+ useEffect(() => {
6
+ onChangeRef.current = onChange;
7
+ }, [onChange]);
8
+ useEffect(() => {
9
+ const subscription = subscribeToQuantitySamples(identifier, (args) => {
10
+ onChangeRef.current(args);
11
+ });
12
+ return () => {
13
+ subscription.remove();
14
+ };
15
+ }, [identifier]);
16
+ }
17
+ export default useSubscribeToQuantitySamples;
@@ -0,0 +1,43 @@
1
+ import { QuantityTypes } from '../modules';
2
+ import { subscribeToChanges } from './subscribeToChanges';
3
+ export const subscribeToQuantitySamples = (identifier, callback, after) => {
4
+ let anchor;
5
+ const afterDate = after ?? new Date();
6
+ const init = async () => {
7
+ // we need to do an initial query to get a handle for the deletedSamples
8
+ const { newAnchor } = await QuantityTypes.queryQuantitySamplesWithAnchor(identifier, {
9
+ filter: {
10
+ startDate: afterDate,
11
+ },
12
+ });
13
+ anchor = newAnchor;
14
+ };
15
+ init();
16
+ return subscribeToChanges(identifier, async ({ errorMessage }) => {
17
+ if (errorMessage) {
18
+ return callback({
19
+ typeIdentifier: identifier,
20
+ errorMessage,
21
+ });
22
+ }
23
+ // seems like all deletedSamples are included when no anchor is provided, so we don't want to return those for the first query
24
+ const hadAnchorWhenDoingQuery = !!anchor;
25
+ const { samples, newAnchor, deletedSamples } = await QuantityTypes.queryQuantitySamplesWithAnchor(identifier, {
26
+ anchor,
27
+ filter: {
28
+ startDate: afterDate,
29
+ },
30
+ });
31
+ anchor = newAnchor;
32
+ const hasNewSamples = samples.length > 0 ||
33
+ (deletedSamples.length > 0 && hadAnchorWhenDoingQuery);
34
+ if (hasNewSamples) {
35
+ callback({
36
+ typeIdentifier: identifier,
37
+ samples,
38
+ anchor: newAnchor,
39
+ deletedSamples: hadAnchorWhenDoingQuery ? deletedSamples : [],
40
+ });
41
+ }
42
+ });
43
+ };
@@ -18,7 +18,9 @@ export declare const isHealthDataAvailableAsync: () => Promise<boolean>;
18
18
  export declare const querySources: (identifier: import("./types").SampleTypeIdentifier) => Promise<readonly import("./specs/SourceProxy.nitro").SourceProxy[]>;
19
19
  export declare const requestAuthorization: (toRequest: import("./specs/CoreModule.nitro").AuthDataTypes) => Promise<boolean>;
20
20
  export declare const deleteObjects: (objectTypeIdentifier: import("./types").ObjectTypeIdentifier, filter: import("./types").FilterForSamples) => Promise<number>;
21
- export declare const subscribeToChanges: (typeIdentifier: import("./types").SampleTypeIdentifier, callback: (args: import("./types").OnChangeCallbackArgs) => void) => string;
21
+ export declare const subscribeToChanges: (identifier: import("./types").SampleTypeIdentifier, callback: (args: import("./types").OnChangeCallbackArgs) => void) => {
22
+ remove: () => boolean;
23
+ };
22
24
  export declare const isProtectedDataAvailable: () => boolean;
23
25
  export declare const isObjectTypeAvailable: (objectTypeIdentifier: import("./types").ObjectTypeIdentifier) => boolean;
24
26
  export declare const isObjectTypeAvailableAsync: (objectTypeIdentifier: import("./types").ObjectTypeIdentifier) => Promise<boolean>;
@@ -70,6 +72,11 @@ export declare const getBloodTypeAsync: () => Promise<BloodType>;
70
72
  export declare const getDateOfBirthAsync: () => Promise<Date | undefined>;
71
73
  export declare const getFitzpatrickSkinTypeAsync: () => Promise<FitzpatrickSkinType>;
72
74
  export declare const getWheelchairUseAsync: () => Promise<WheelchairUse>;
73
- export declare const unsubscribeQueries: (queryIds: string[]) => number;
75
+ declare const subscribeToQuantitySamples: (identifier: import("./types").QuantityTypeIdentifier, callback: (args: import("./types").OnQuantitySamplesCallback) => void, after?: Date) => {
76
+ remove: () => boolean;
77
+ };
78
+ export { subscribeToQuantitySamples };
79
+ declare const useSubscribeToQuantitySamples: typeof import("./healthkit.ios").useSubscribeToQuantitySamples;
80
+ export { useSubscribeToQuantitySamples };
74
81
  declare const _default: typeof ReactNativeHealthkit;
75
82
  export default _default;
@@ -6,11 +6,14 @@ import useMostRecentWorkout from './hooks/useMostRecentWorkout';
6
6
  import useSources from './hooks/useSources';
7
7
  import useStatisticsForQuantity from './hooks/useStatisticsForQuantity';
8
8
  import useSubscribeToChanges from './hooks/useSubscribeToChanges';
9
+ import useSubscribeToQuantitySamples from './hooks/useSubscribeToQuantitySamples';
9
10
  import type { QuantityTypeIdentifier } from './types/QuantityTypeIdentifier';
10
11
  import getMostRecentCategorySample from './utils/getMostRecentCategorySample';
11
12
  import getMostRecentQuantitySample from './utils/getMostRecentQuantitySample';
12
13
  import getMostRecentWorkout from './utils/getMostRecentWorkout';
13
14
  import getPreferredUnit from './utils/getPreferredUnit';
15
+ import { subscribeToChanges } from './utils/subscribeToChanges';
16
+ import { subscribeToQuantitySamples } from './utils/subscribeToQuantitySamples';
14
17
  export * from './types';
15
18
  declare const currentMajorVersionIOS: number;
16
19
  /**
@@ -21,7 +24,7 @@ type QuantityTypesIOS17Plus = 'HKQuantityTypeIdentifierCyclingCadence' | 'HKQuan
21
24
  * Available quantity types for iOS versions before iOS 17
22
25
  */
23
26
  export type AvailableQuantityTypesBeforeIOS17 = Exclude<QuantityTypeIdentifier, QuantityTypesIOS17Plus>;
24
- export { getMostRecentCategorySample, getMostRecentQuantitySample, getMostRecentWorkout, getPreferredUnit, useMostRecentCategorySample, useMostRecentQuantitySample, useMostRecentWorkout, useSubscribeToChanges, useHealthkitAuthorization, useIsHealthDataAvailable, useSources, useStatisticsForQuantity, };
27
+ export { getMostRecentCategorySample, getMostRecentQuantitySample, getMostRecentWorkout, getPreferredUnit, subscribeToChanges, subscribeToQuantitySamples, useHealthkitAuthorization, useIsHealthDataAvailable, useMostRecentCategorySample, useMostRecentQuantitySample, useMostRecentWorkout, useSources, useStatisticsForQuantity, useSubscribeToChanges, useSubscribeToQuantitySamples, };
25
28
  /**
26
29
  * Available quantity types for iOS 17 and later (all quantity types)
27
30
  */
@@ -65,13 +68,11 @@ export declare const saveCategorySample: <T extends import("./types").CategoryTy
65
68
  export declare const saveCorrelationSample: (typeIdentifier: import("./types").CorrelationTypeIdentifier, samples: import("./types").SampleForSaving[], start: Date, end: Date, metadata: import("react-native-nitro-modules").AnyMap) => Promise<boolean>;
66
69
  export declare const saveQuantitySample: (identifier: QuantityTypeIdentifier, unit: string, value: number, start: Date, end: Date, metadata: import("react-native-nitro-modules").AnyMap) => Promise<boolean>;
67
70
  export declare const saveWorkoutSample: (workoutActivityType: import("./types").WorkoutActivityType, quantities: readonly import("./types").QuantitySampleForSaving[], startDate: Date, endDate: Date, totals?: import("./types").WorkoutTotals, metadata?: import("react-native-nitro-modules").AnyMap) => Promise<import("./specs/WorkoutProxy.nitro").WorkoutProxy>;
68
- export declare const subscribeToChanges: (typeIdentifier: import("./types").SampleTypeIdentifier, callback: (args: import("./types").OnChangeCallbackArgs) => void) => string;
69
71
  export declare const startWatchApp: (workoutConfiguration: import("./types").WorkoutConfiguration) => Promise<boolean>;
70
72
  export declare const isProtectedDataAvailable: () => boolean;
71
73
  export declare const queryStateOfMindSamples: (options?: import("./types").QueryOptionsWithSortOrder) => Promise<readonly import("./types").StateOfMindSample[]>;
72
74
  export declare const saveStateOfMindSample: (date: Date, kind: import("./types").StateOfMindKind, valence: number, labels: readonly import("./types").StateOfMindLabel[], associations: readonly import("./types").StateOfMindAssociation[], metadata?: import("react-native-nitro-modules").AnyMap) => Promise<boolean>;
73
75
  export declare const isQuantityCompatibleWithUnit: (identifier: QuantityTypeIdentifier, unit: string) => boolean;
74
- export declare const unsubscribeQueries: (queryIds: string[]) => number;
75
76
  export declare const isObjectTypeAvailable: (objectTypeIdentifier: import("./types").ObjectTypeIdentifier) => boolean;
76
77
  export declare const isObjectTypeAvailableAsync: (objectTypeIdentifier: import("./types").ObjectTypeIdentifier) => Promise<boolean>;
77
78
  export declare const areObjectTypesAvailable: (objectTypeIdentifiers: readonly import("./types").ObjectTypeIdentifier[]) => Record<string, boolean>;
@@ -129,8 +130,12 @@ declare const _default: {
129
130
  saveCorrelationSample: (typeIdentifier: import("./types").CorrelationTypeIdentifier, samples: import("./types").SampleForSaving[], start: Date, end: Date, metadata: import("react-native-nitro-modules").AnyMap) => Promise<boolean>;
130
131
  saveQuantitySample: (identifier: QuantityTypeIdentifier, unit: string, value: number, start: Date, end: Date, metadata: import("react-native-nitro-modules").AnyMap) => Promise<boolean>;
131
132
  saveWorkoutSample: (workoutActivityType: import("./types").WorkoutActivityType, quantities: readonly import("./types").QuantitySampleForSaving[], startDate: Date, endDate: Date, totals?: import("./types").WorkoutTotals, metadata?: import("react-native-nitro-modules").AnyMap) => Promise<import("./specs/WorkoutProxy.nitro").WorkoutProxy>;
132
- subscribeToChanges: (typeIdentifier: import("./types").SampleTypeIdentifier, callback: (args: import("./types").OnChangeCallbackArgs) => void) => string;
133
- unsubscribeQueries: (queryIds: string[]) => number;
133
+ subscribeToChanges: (identifier: import("./types").SampleTypeIdentifier, callback: (args: import("./types").OnChangeCallbackArgs) => void) => {
134
+ remove: () => boolean;
135
+ };
136
+ subscribeToQuantitySamples: (identifier: QuantityTypeIdentifier, callback: (args: import("./types").OnQuantitySamplesCallback) => void, after?: Date) => {
137
+ remove: () => boolean;
138
+ };
134
139
  startWatchApp: (workoutConfiguration: import("./types").WorkoutConfiguration) => Promise<boolean>;
135
140
  isProtectedDataAvailable: () => boolean;
136
141
  queryStateOfMindSamples: (options?: import("./types").QueryOptionsWithSortOrder) => Promise<readonly import("./types").StateOfMindSample[]>;
@@ -139,6 +144,7 @@ declare const _default: {
139
144
  useMostRecentQuantitySample: typeof useMostRecentQuantitySample;
140
145
  useMostRecentWorkout: typeof useMostRecentWorkout;
141
146
  useSubscribeToChanges: typeof useSubscribeToChanges;
147
+ useSubscribeToQuantitySamples: typeof useSubscribeToQuantitySamples;
142
148
  useHealthkitAuthorization: ({ toWrite, toRead, }: {
143
149
  toRead?: readonly import("./types").ObjectTypeIdentifier[];
144
150
  toWrite?: readonly import("./types").SampleTypeIdentifierWriteable[];
@@ -0,0 +1,3 @@
1
+ import type { OnQuantitySamplesCallback, QuantityTypeIdentifier } from '../types';
2
+ export declare function useSubscribeToQuantitySamples<TIdentifier extends QuantityTypeIdentifier>(identifier: TIdentifier, onChange: (args: OnQuantitySamplesCallback) => void): void;
3
+ export default useSubscribeToQuantitySamples;
@@ -1,4 +1,6 @@
1
- import type { SampleTypeIdentifier } from './Shared';
1
+ import type { QuantitySample } from './QuantitySample';
2
+ import type { QuantityTypeIdentifier } from './QuantityTypeIdentifier';
3
+ import type { DeletedSample, SampleTypeIdentifier } from './Shared';
2
4
  export interface EmitterSubscription {
3
5
  remove: () => void;
4
6
  }
@@ -6,3 +8,14 @@ export interface OnChangeCallbackArgs {
6
8
  readonly typeIdentifier: SampleTypeIdentifier;
7
9
  readonly errorMessage?: string;
8
10
  }
11
+ export interface OnQuantitySamplesCallbackError {
12
+ readonly typeIdentifier: QuantityTypeIdentifier;
13
+ readonly errorMessage: string;
14
+ }
15
+ export interface OnQuantitySamplesCallbackSuccess {
16
+ readonly typeIdentifier: QuantityTypeIdentifier;
17
+ readonly anchor: string;
18
+ readonly samples: readonly QuantitySample[];
19
+ readonly deletedSamples: readonly DeletedSample[];
20
+ }
21
+ export type OnQuantitySamplesCallback = OnQuantitySamplesCallbackError | OnQuantitySamplesCallbackSuccess;
@@ -0,0 +1,5 @@
1
+ import type { QuantityTypeIdentifier } from '../types';
2
+ import type { OnQuantitySamplesCallback } from '../types/Subscriptions';
3
+ export declare const subscribeToQuantitySamples: (identifier: QuantityTypeIdentifier, callback: (args: OnQuantitySamplesCallback) => void, after?: Date) => {
4
+ remove: () => boolean;
5
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kingstinct/react-native-healthkit",
3
- "version": "12.0.0",
3
+ "version": "12.1.0",
4
4
  "description": "React Native bindings for HealthKit",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -7,6 +7,7 @@ import useMostRecentWorkout from './hooks/useMostRecentWorkout'
7
7
  import useSources from './hooks/useSources'
8
8
  import useStatisticsForQuantity from './hooks/useStatisticsForQuantity'
9
9
  import useSubscribeToChanges from './hooks/useSubscribeToChanges'
10
+ import useSubscribeToQuantitySamples from './hooks/useSubscribeToQuantitySamples'
10
11
  import {
11
12
  CategoryTypes,
12
13
  Characteristics,
@@ -23,6 +24,8 @@ import getMostRecentCategorySample from './utils/getMostRecentCategorySample'
23
24
  import getMostRecentQuantitySample from './utils/getMostRecentQuantitySample'
24
25
  import getMostRecentWorkout from './utils/getMostRecentWorkout'
25
26
  import getPreferredUnit from './utils/getPreferredUnit'
27
+ import { subscribeToChanges } from './utils/subscribeToChanges'
28
+ import { subscribeToQuantitySamples } from './utils/subscribeToQuantitySamples'
26
29
 
27
30
  export * from './types'
28
31
 
@@ -53,14 +56,17 @@ export {
53
56
  getMostRecentQuantitySample,
54
57
  getMostRecentWorkout,
55
58
  getPreferredUnit,
59
+ subscribeToChanges,
60
+ subscribeToQuantitySamples,
61
+ useHealthkitAuthorization,
62
+ useIsHealthDataAvailable,
56
63
  useMostRecentCategorySample,
57
64
  useMostRecentQuantitySample,
58
65
  useMostRecentWorkout,
59
- useSubscribeToChanges,
60
- useHealthkitAuthorization,
61
- useIsHealthDataAvailable,
62
66
  useSources,
63
67
  useStatisticsForQuantity,
68
+ useSubscribeToChanges,
69
+ useSubscribeToQuantitySamples,
64
70
  }
65
71
 
66
72
  /**
@@ -138,7 +144,6 @@ export const saveCorrelationSample =
138
144
  export const saveQuantitySample =
139
145
  QuantityTypes.saveQuantitySample.bind(QuantityTypes)
140
146
  export const saveWorkoutSample = Workouts.saveWorkoutSample.bind(Workouts)
141
- export const subscribeToChanges = Core.subscribeToObserverQuery.bind(Core)
142
147
  export const startWatchApp =
143
148
  Workouts.startWatchAppWithWorkoutConfiguration.bind(Workouts)
144
149
  export const isProtectedDataAvailable = Core.isProtectedDataAvailable.bind(Core)
@@ -148,7 +153,6 @@ export const saveStateOfMindSample =
148
153
  StateOfMind.saveStateOfMindSample.bind(StateOfMind)
149
154
  export const isQuantityCompatibleWithUnit =
150
155
  QuantityTypes.isQuantityCompatibleWithUnit.bind(QuantityTypes)
151
- export const unsubscribeQueries = Core.unsubscribeQueries.bind(Core)
152
156
 
153
157
  export const isObjectTypeAvailable = Core.isObjectTypeAvailable.bind(Core)
154
158
  export const isObjectTypeAvailableAsync =
@@ -217,7 +221,7 @@ export default {
217
221
  saveQuantitySample,
218
222
  saveWorkoutSample,
219
223
  subscribeToChanges,
220
- unsubscribeQueries,
224
+ subscribeToQuantitySamples,
221
225
  startWatchApp,
222
226
  isProtectedDataAvailable,
223
227
  queryStateOfMindSamples,
@@ -228,6 +232,7 @@ export default {
228
232
  useMostRecentQuantitySample,
229
233
  useMostRecentWorkout,
230
234
  useSubscribeToChanges,
235
+ useSubscribeToQuantitySamples,
231
236
  useHealthkitAuthorization,
232
237
  useIsHealthDataAvailable,
233
238
  useSources,
package/src/healthkit.ts CHANGED
@@ -97,7 +97,9 @@ export const deleteObjects = UnavailableFnFromModule(
97
97
  )
98
98
  export const subscribeToChanges = UnavailableFnFromModule(
99
99
  'subscribeToChanges',
100
- 'dummy-query-uuid',
100
+ {
101
+ remove: () => false,
102
+ },
101
103
  ) // Mocking the observer query UUID
102
104
  export const isProtectedDataAvailable = UnavailableFnFromModule(
103
105
  'isProtectedDataAvailable',
@@ -363,18 +365,27 @@ export const getWheelchairUseAsync = UnavailableFnFromModule(
363
365
  Promise.resolve(WheelchairUse.notSet),
364
366
  )
365
367
 
366
- export const unsubscribeQueries = UnavailableFnFromModule(
367
- 'unsubscribeQueries',
368
- 0,
369
- )
368
+ const subscribeToQuantitySamples = UnavailableFnFromModule(
369
+ 'subscribeToQuantitySamples',
370
+ {
371
+ remove: () => false,
372
+ },
373
+ ) // Mocking the observer query UUID
374
+
375
+ export { subscribeToQuantitySamples }
376
+
377
+ const useSubscribeToQuantitySamples = UnavailableFnFromModule(
378
+ 'useSubscribeToQuantitySamples',
379
+ undefined,
380
+ ) // Mocking callback structure
370
381
 
382
+ export { useSubscribeToQuantitySamples }
371
383
  // --- Default Export ---
372
384
  // This attempts to match the structure of the default export from index.ios.ts
373
385
  const HealthkitModule = {
374
386
  // All named exports are also part of the default export object
375
387
  authorizationStatusFor,
376
388
  isObjectTypeAvailable,
377
- unsubscribeQueries,
378
389
  isObjectTypeAvailableAsync,
379
390
  areObjectTypesAvailable,
380
391
  areObjectTypesAvailableAsync,
@@ -420,6 +431,8 @@ const HealthkitModule = {
420
431
  isProtectedDataAvailable,
421
432
  queryStateOfMindSamples,
422
433
  saveStateOfMindSample,
434
+ subscribeToQuantitySamples,
435
+ useSubscribeToQuantitySamples,
423
436
 
424
437
  // Hooks
425
438
  useMostRecentCategorySample,
@@ -0,0 +1,31 @@
1
+ import { useEffect, useRef } from 'react'
2
+ import type {
3
+ OnQuantitySamplesCallback,
4
+ QuantityTypeIdentifier,
5
+ } from '../types'
6
+ import { subscribeToQuantitySamples } from '../utils/subscribeToQuantitySamples'
7
+
8
+ export function useSubscribeToQuantitySamples<
9
+ TIdentifier extends QuantityTypeIdentifier,
10
+ >(
11
+ identifier: TIdentifier,
12
+ onChange: (args: OnQuantitySamplesCallback) => void,
13
+ ): void {
14
+ const onChangeRef = useRef(onChange)
15
+
16
+ useEffect(() => {
17
+ onChangeRef.current = onChange
18
+ }, [onChange])
19
+
20
+ useEffect(() => {
21
+ const subscription = subscribeToQuantitySamples(identifier, (args) => {
22
+ onChangeRef.current(args)
23
+ })
24
+
25
+ return () => {
26
+ subscription.remove()
27
+ }
28
+ }, [identifier])
29
+ }
30
+
31
+ export default useSubscribeToQuantitySamples
@@ -1,4 +1,6 @@
1
- import type { SampleTypeIdentifier } from './Shared'
1
+ import type { QuantitySample } from './QuantitySample'
2
+ import type { QuantityTypeIdentifier } from './QuantityTypeIdentifier'
3
+ import type { DeletedSample, SampleTypeIdentifier } from './Shared'
2
4
 
3
5
  export interface EmitterSubscription {
4
6
  remove: () => void
@@ -8,3 +10,19 @@ export interface OnChangeCallbackArgs {
8
10
  readonly typeIdentifier: SampleTypeIdentifier
9
11
  readonly errorMessage?: string
10
12
  }
13
+
14
+ export interface OnQuantitySamplesCallbackError {
15
+ readonly typeIdentifier: QuantityTypeIdentifier
16
+ readonly errorMessage: string
17
+ }
18
+
19
+ export interface OnQuantitySamplesCallbackSuccess {
20
+ readonly typeIdentifier: QuantityTypeIdentifier
21
+ readonly anchor: string
22
+ readonly samples: readonly QuantitySample[]
23
+ readonly deletedSamples: readonly DeletedSample[]
24
+ }
25
+
26
+ export type OnQuantitySamplesCallback =
27
+ | OnQuantitySamplesCallbackError
28
+ | OnQuantitySamplesCallbackSuccess
@@ -0,0 +1,63 @@
1
+ import { QuantityTypes } from '../modules'
2
+ import type { QuantityTypeIdentifier } from '../types'
3
+ import type { OnQuantitySamplesCallback } from '../types/Subscriptions'
4
+ import { subscribeToChanges } from './subscribeToChanges'
5
+
6
+ export const subscribeToQuantitySamples = (
7
+ identifier: QuantityTypeIdentifier,
8
+ callback: (args: OnQuantitySamplesCallback) => void,
9
+ after?: Date,
10
+ ) => {
11
+ let anchor: string | undefined
12
+ const afterDate = after ?? new Date()
13
+
14
+ const init = async () => {
15
+ // we need to do an initial query to get a handle for the deletedSamples
16
+ const { newAnchor } = await QuantityTypes.queryQuantitySamplesWithAnchor(
17
+ identifier,
18
+ {
19
+ filter: {
20
+ startDate: afterDate,
21
+ },
22
+ },
23
+ )
24
+ anchor = newAnchor
25
+ }
26
+
27
+ init()
28
+
29
+ return subscribeToChanges(identifier, async ({ errorMessage }) => {
30
+ if (errorMessage) {
31
+ return callback({
32
+ typeIdentifier: identifier,
33
+ errorMessage,
34
+ })
35
+ }
36
+
37
+ // seems like all deletedSamples are included when no anchor is provided, so we don't want to return those for the first query
38
+ const hadAnchorWhenDoingQuery = !!anchor
39
+
40
+ const { samples, newAnchor, deletedSamples } =
41
+ await QuantityTypes.queryQuantitySamplesWithAnchor(identifier, {
42
+ anchor,
43
+ filter: {
44
+ startDate: afterDate,
45
+ },
46
+ })
47
+
48
+ anchor = newAnchor
49
+
50
+ const hasNewSamples =
51
+ samples.length > 0 ||
52
+ (deletedSamples.length > 0 && hadAnchorWhenDoingQuery)
53
+
54
+ if (hasNewSamples) {
55
+ callback({
56
+ typeIdentifier: identifier,
57
+ samples,
58
+ anchor: newAnchor,
59
+ deletedSamples: hadAnchorWhenDoingQuery ? deletedSamples : [],
60
+ })
61
+ }
62
+ })
63
+ }