@kingstinct/react-native-healthkit 9.0.0 → 9.0.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.
package/README.md ADDED
@@ -0,0 +1,192 @@
1
+ # @kingstinct/react-native-healthkit
2
+
3
+ [![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)
4
+ [![Latest version on NPM](https://img.shields.io/npm/v/@kingstinct/react-native-healthkit)](https://www.npmjs.com/package/@kingstinct/react-native-healthkit)
5
+ [![Downloads on NPM](https://img.shields.io/npm/dt/@kingstinct/react-native-healthkit)](https://www.npmjs.com/package/@kingstinct/react-native-healthkit)
6
+ [![Discord](https://dcbadge.vercel.app/api/server/hrgnETpsJA?style=flat)](https://discord.gg/hrgnETpsJA)
7
+
8
+
9
+ 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).
10
+
11
+ | Data Types | Query | Save | Subscribe | Examples |
12
+ | ----------------------------|:------|:------|:----------|:---------------------------------------|
13
+ | 100+ Quantity Types | ✅ | ✅ | ✅ | Steps, energy burnt, blood glucose etc.. |
14
+ | 63 Category Types | ✅ | ✅ | ✅ | Sleep analysis, mindful sessions etc.. |
15
+ | 75+ Workout Activity Types | ✅ | ✅ | ✅ | Swimming, running, table tennis etc.. |
16
+ | Correlation Types | ✅ | ✅ | ✅ | Food and blood pressure |
17
+ | Document Types | ✅ | ❌ | ✅ | [CDA documents](https://developer.apple.com/documentation/healthkit/hkcdadocument) exposed as Base64 data |
18
+ | Clinical Records | ⚠️ | ❌ | ⚠️ | Lab results etc in [FHIR JSON format](https://www.hl7.org/fhir/json.html) (see [Clinical Records](https://github.com/kingstinct/react-native-healthkit#clinical-records)) |
19
+
20
+ ### Disclaimer
21
+
22
+ This library is provided as-is without any warranty and is not affiliated with Apple in any way. The data might be incomplete or inaccurate.
23
+
24
+ ## Installation
25
+
26
+ ### Expo
27
+ Usage with Expo is possible - just keep in mind it will not work in Expo Go and [you'll need to roll your own Dev Client](https://docs.expo.dev/development/getting-started/).
28
+
29
+ 1. `yarn add @kingstinct/react-native-healthkit`
30
+ 2. Update your app.json with the config plugin:
31
+ ```json
32
+ {
33
+ "expo": {
34
+ "plugins": ["@kingstinct/react-native-healthkit"]
35
+ }
36
+ }
37
+ ```
38
+ this will give you defaults that make the app build without any further configuration. If you want, you can override the defaults:
39
+ ```json
40
+ {
41
+ "expo": {
42
+ "plugins": [
43
+ ["@kingstinct/react-native-healthkit", {
44
+ "NSHealthShareUsageDescription": "Your own custom usage description",
45
+ "NSHealthUpdateUsageDescription": false, // if you have no plans to update data, you could skip adding it to your info.plist
46
+ "background": false // if you have no plans to use it in background mode, you could skip adding it to the entitlements
47
+ }]
48
+ ]
49
+ }
50
+ }
51
+ ```
52
+ 3. Build a new Dev Client
53
+
54
+ ### Native or Expo Bare Workflow
55
+ 1. `yarn add @kingstinct/react-native-healthkit`
56
+ 2. `npx pod-install`
57
+ 3. Set `NSHealthUpdateUsageDescription` and `NSHealthShareUsageDescription` in your `Info.plist`
58
+ 4. Enable the HealthKit capability for the project in Xcode.
59
+ 5. Since this package is using Swift you might also need to add a bridging header in your project if you haven't already, you can [find more about that in the official React Native docs](https://reactnative.dev/docs/native-modules-ios#exporting-swift)
60
+
61
+ ## Usage
62
+
63
+ During runtime check and request permissions with `requestAuthorization`. Failing to request authorization, or requesting a permission you haven't requested yet, will result in the app crashing. This is easy to miss - for example by requesting authorization in the same component where you have a hook trying to fetch data right away.. :)
64
+
65
+ Some hook examples:
66
+ ```TypeScript
67
+ import { useHealthkitAuthorization, saveQuantitySample } from '@kingstinct/react-native-healthkit';
68
+
69
+ const [authorizationStatus, requestAuthorization] = useHealthkitAuthorization(['HKQuantityTypeIdentifierBloodGlucose'])
70
+
71
+ // make sure that you've requested authorization before requesting data, otherwise your app will crash
72
+ import { useMostRecentQuantitySample, HKQuantityTypeIdentifier, useMostRecentCategorySample } from '@kingstinct/react-native-healthkit';
73
+
74
+ const mostRecentBloodGlucoseSample = useMostRecentQuantitySample('HKQuantityTypeIdentifierBloodGlucose')
75
+ const lastBodyFatSample = useMostRecentQuantitySample('HKQuantityTypeIdentifierBodyFatPercentage')
76
+ const lastMindfulSession = useMostRecentCategorySample('HKCategoryTypeIdentifierMindfulSession')
77
+ const lastWorkout = useMostRecentWorkout()
78
+ ```
79
+
80
+ Some imperative examples:
81
+ ```TypeScript
82
+ import { isHealthDataAvailable, requestAuthorization, subscribeToChanges, saveQuantitySample, getMostRecentQuantitySample } from '@kingstinct/react-native-healthkit';
83
+
84
+ const isAvailable = await isHealthDataAvailable();
85
+
86
+ /* Read latest sample of any data */
87
+ await requestAuthorization(['HKQuantityTypeIdentifierBodyFatPercentage']); // request read permission for bodyFatPercentage
88
+
89
+ const { quantity, unit, startDate, endDate } = await getMostRecentQuantitySample('HKQuantityTypeIdentifierBodyFatPercentage'); // read latest sample
90
+
91
+ console.log(quantity) // 17.5
92
+ console.log(unit) // %
93
+
94
+ await requestAuthorization(['HKQuantityTypeIdentifierHeartRate']); // request read permission for heart rate
95
+
96
+ /* Subscribe to data (Make sure to request permissions before subscribing to changes) */
97
+ const [hasRequestedAuthorization, setHasRequestedAuthorization] = useState(false);
98
+
99
+ useEffect(() => {
100
+ requestAuthorization(['HKQuantityTypeIdentifierHeartRate']).then(() => {
101
+ setHasRequestedAuthorization(true);
102
+ });
103
+ }, []);
104
+
105
+ useEffect(() => {
106
+ if (hasRequestedAuthorization) {
107
+ const unsubscribe = subscribeToChanges(HKQuantityTypeIdentifier.heartRate, () => {
108
+ // refetch data as needed
109
+ });
110
+
111
+ return () => unsubscribe();
112
+ }
113
+ }, [hasRequestedAuthorization]);
114
+
115
+ /* write data */
116
+ await requestAuthorization([], [HKQuantityTypeIdentifier.insulinDelivery]); // request write permission for insulin delivery
117
+
118
+ saveQuantitySample(
119
+ 'HKQuantityTypeIdentifierInsulinDelivery',
120
+ 'IU',
121
+ 5.5,
122
+ {
123
+ metadata: {
124
+ // Metadata keys could be arbirtary string to store app-specific data.
125
+ // To use built-in types from https://developer.apple.com/documentation/healthkit/samples/metadata_keys
126
+ // you need to specify string values instead of variable names (by dropping MetadataKey from the name).
127
+ HKInsulinDeliveryReason: HKInsulinDeliveryReason.basal,
128
+ },
129
+ }
130
+ );
131
+ ```
132
+
133
+ ### HealthKit Anchors
134
+ In 6.0 you can use HealthKit anchors to get changes and deleted items which is very useful for syncing. This is a breaking change - but a very easy one to handle that TypeScript should help you with. Most queries now return an object containing samples which is what was returned as only an array before.
135
+
136
+ ```newAnchor``` is a base64-encoded string returned from HealthKit that contain sync information. After each successful sync, store the anchor for the next time your anchor query is called to only return the values that have changed.
137
+
138
+ ```limit``` will indicate how many records to consider when sycning data, you can set this value to 0 indicate no limit.
139
+
140
+ Example:
141
+
142
+ ```TypeScript
143
+ const { newAnchor, samples, deletedSamples } = await queryQuantitySamplesWithAnchor('HKQuantityTypeIdentifierStepCount', {
144
+ limit: 2,
145
+ })
146
+
147
+ const nextResult = await queryQuantitySamplesWithAnchor('HKQuantityTypeIdentifierStepCount', {
148
+ limit: 2,
149
+ anchor: newAnchor,
150
+ })
151
+
152
+ // etc..
153
+ ```
154
+
155
+ ## Migration to 9.0.0
156
+
157
+ There are a lot of under-the-hood changes in version 9.0.0, some of them are breaking (although I've tried to reduce it as much as possible).
158
+ - Most of all - the library has been migrated to use react-native-nitro-modules. This improves performance, type-safety and gets rid of a lot of boilerplate code that made it harder to maintain and add features to the library.
159
+ - Naming conventions have changed - most of the HK-prefixed stuff has been removed to avoid conflicts on the native side and also make the library more beautiful to look at. As an example the type previously called HKQuantityTypeIdentifier is not just QuantityTypeIdentifier on the library level.
160
+ - Less required params by default - making it easier to start using the library, for example calling `queryQuantitySamples('HKQuantityTypeIdentifierStepCount')` will simply return the last 20 samples.
161
+ - Flexible filters that map closer to the native constructs. This can easily be extended.
162
+ - `deleteObjects` replaces all previous deletion methods, using the new flexible filters.
163
+ - Workouts are returned as proxies containing not only data but also functions, for example `getWorkoutRoutes`.
164
+ - Identifiers are now just strings, and more strictly typed.
165
+ - Units are now just strings.
166
+
167
+ ## A note on Apple Documentation
168
+
169
+ We're striving to do as straight a mapping as possible to the Native Libraries. This means that in most cases the Apple Documentation makes sense. However, when it comes to the Healthkit [Metadata Keys](https://developer.apple.com/documentation/healthkit/samples/metadata_keys) the documentation doesn't actually reflect the serialized values. For example HKMetadataKeyExternalUUID in the documentation serializes to HKExternalUUID - which is what we use.
170
+
171
+ ## Clinical Records
172
+
173
+ For accessing Clinical Records use old version (3.x) or use specific branch "including-clinical-records". The reason is we cannot refer to this code natively in apps without getting approval from Apple, this could probably be solved by the config plugin but we haven't had time to look into it yet.
174
+
175
+ ## Android alternatives
176
+
177
+ For a similar library for Android, check out [react-native-health-connect](https://github.com/matinzd/react-native-health-connect/) that works with the new Health Connect. For Google Fit [react-native-google-fit](https://www.npmjs.com/package/react-native-google-fit) seems to be the most popular option, and and another possible option is to work directly with the Google Fit REST API which I've some experience with.
178
+
179
+ ## Contributing
180
+
181
+ See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
182
+
183
+ ## Sponsorship and enterprise-grade support
184
+
185
+ If you're using @kingstinct/react-native-healthkit to build your production app [please consider funding its continued development](https://github.com/sponsors/Kingstinct). It helps us spend more time on keeping this library as good as it can be.
186
+
187
+ At Kingstinct we're also able to provide enterprise-grade support for this package, [find us here](https://kingstinct.com) or [drop an email](mailto:healthkit@kingstinct.com) for more information. Also feel free to join our [Discord community](https://discord.gg/EHScS93v).
188
+
189
+ ## License
190
+
191
+ MIT
192
+
package/ios/Helpers.swift CHANGED
@@ -11,25 +11,24 @@ import NitroModules
11
11
 
12
12
  func dateOrNilIfZero(_ timestamp: Double?) -> Date? {
13
13
  if let timestamp = timestamp {
14
- if(timestamp == 0){
14
+ if timestamp == 0 {
15
15
  return nil
16
16
  }
17
17
  return Date.init(timeIntervalSince1970: timestamp)
18
18
  }
19
19
  return nil
20
-
21
-
20
+
22
21
  }
23
22
 
24
23
  func getQueryLimit(_ limit: Double?) -> Int {
25
24
  if let limit = limit {
26
- if(limit == .infinity || limit <= 0){
25
+ if limit == .infinity || limit <= 0 {
27
26
  return HKObjectQueryNoLimit
28
27
  }
29
-
28
+
30
29
  return Int(limit)
31
30
  }
32
-
31
+
33
32
  return DEFAULT_QUERY_LIMIT
34
33
  }
35
34
 
@@ -45,7 +44,7 @@ func createPredicateForWorkout(filter: PredicateForWorkouts) throws -> NSPredica
45
44
  return createDatePredicate(dateFilter: dateFilter)
46
45
  case .fifth(let w):
47
46
  if let w = w.workout as? WorkoutProxy {
48
- return HKQuery.predicateForObjects(from: w.workout)
47
+ return w.workoutPredicate
49
48
  }
50
49
  throw RuntimeError.error(withMessage: "[react-native-healthkit] Failed to initialize workout for filter")
51
50
  case .sixth(let activityType):
@@ -55,7 +54,7 @@ func createPredicateForWorkout(filter: PredicateForWorkouts) throws -> NSPredica
55
54
  throw RuntimeError.error(withMessage: "[react-native-healthkit] Failed to initialize unrecognized workoutActivityType with identifier \(activityType.workoutActivityType.rawValue)")
56
55
  }
57
56
  case .seventh(let durationPredicate):
58
- if let op = NSComparisonPredicate.Operator(rawValue: UInt(durationPredicate.predicateOperator.rawValue)){
57
+ if let op = NSComparisonPredicate.Operator(rawValue: UInt(durationPredicate.predicateOperator.rawValue)) {
59
58
  return HKQuery.predicateForWorkouts(with: op, duration: durationPredicate.durationInSeconds)
60
59
  }
61
60
  throw RuntimeError.error(withMessage: "[react-native-healthkit] Failed to initialize unrecognized workout duration predicate operator \(durationPredicate.predicateOperator.rawValue)")
@@ -75,7 +74,7 @@ func createPredicateForWorkout(filter: Variant_PredicateWithUUID_PredicateWithUU
75
74
  return createDatePredicate(dateFilter: dateFilter)
76
75
  case .fifth(let w):
77
76
  if let w = w.workout as? WorkoutProxy {
78
- return HKQuery.predicateForObjects(from: w.workout)
77
+ return w.workoutPredicate
79
78
  }
80
79
  throw RuntimeError.error(withMessage: "[react-native-healthkit] Failed to initialize workout for filter")
81
80
  case .sixth(let activityType):
@@ -85,11 +84,11 @@ func createPredicateForWorkout(filter: Variant_PredicateWithUUID_PredicateWithUU
85
84
  throw RuntimeError.error(withMessage: "[react-native-healthkit] Failed to initialize unrecognized workoutActivityType with identifier \(activityType.workoutActivityType.rawValue)")
86
85
  }
87
86
  case .seventh(let durationPredicate):
88
- if let op = NSComparisonPredicate.Operator(rawValue: UInt(durationPredicate.predicateOperator.rawValue)){
87
+ if let op = NSComparisonPredicate.Operator(rawValue: UInt(durationPredicate.predicateOperator.rawValue)) {
89
88
  return HKQuery.predicateForWorkouts(with: op, duration: durationPredicate.durationInSeconds)
90
89
  }
91
90
  throw RuntimeError.error(withMessage: "[react-native-healthkit] Failed to initialize unrecognized workout duration predicate operator \(durationPredicate.predicateOperator.rawValue)")
92
-
91
+
93
92
  case .eigth(let or):
94
93
  return NSCompoundPredicate.init(andPredicateWithSubpredicates: try or.OR.map { predicate in
95
94
  return try createPredicateForWorkout(filter: predicate)
@@ -101,14 +100,14 @@ func createPredicateForWorkout(filter: Variant_PredicateWithUUID_PredicateWithUU
101
100
  })
102
101
  }
103
102
  }
104
-
103
+
105
104
  return nil
106
105
  }
107
106
 
108
107
  func createDatePredicate(dateFilter: PredicateWithStartAndEnd) -> NSPredicate {
109
108
  let strictStartDate = dateFilter.strictStartDate ?? false
110
109
  let strictEndDate = dateFilter.strictEndDate ?? false
111
-
110
+
112
111
  let options: HKQueryOptions = strictStartDate && strictEndDate
113
112
  ? [.strictStartDate, .strictEndDate]
114
113
  : strictEndDate
@@ -116,7 +115,7 @@ func createDatePredicate(dateFilter: PredicateWithStartAndEnd) -> NSPredicate {
116
115
  : strictStartDate
117
116
  ? .strictStartDate
118
117
  : []
119
-
118
+
120
119
  return HKQuery.predicateForSamples(
121
120
  withStart: dateFilter.startDate,
122
121
  end: dateFilter.endDate,
@@ -129,8 +128,7 @@ func createUUIDsPredicate(uuidsWrapper: PredicateWithUUIDs) -> NSPredicate {
129
128
  do {
130
129
  let uuid = try initializeUUID(uuidStr)
131
130
  return uuid
132
- }
133
- catch {
131
+ } catch {
134
132
  print(error.localizedDescription)
135
133
  return nil
136
134
  }
@@ -151,7 +149,7 @@ func createPredicate(filter: Variant_PredicateWithUUID_PredicateWithUUIDs_Predic
151
149
  return createDatePredicate(dateFilter: dateFilter)
152
150
  case .fifth(let w):
153
151
  if let w = w.workout as? WorkoutProxy {
154
- return HKQuery.predicateForObjects(from: w.workout)
152
+ return w.workoutPredicate
155
153
  }
156
154
  throw RuntimeError.error(withMessage: "[react-native-healthkit] Failed to initialize workout for filter")
157
155
  }
@@ -172,7 +170,7 @@ func createPredicate(filter: Variant_PredicateWithUUID_PredicateWithUUIDs_Predic
172
170
  return createDatePredicate(dateFilter: dateFilter)
173
171
  case .fifth(let w):
174
172
  if let w = w.workout as? WorkoutProxy {
175
- return HKQuery.predicateForObjects(from: w.workout)
173
+ return w.workoutPredicate
176
174
  }
177
175
  throw RuntimeError.error(withMessage: "[react-native-healthkit] Failed to initialize workout for filter")
178
176
  case .sixth(let and):
@@ -201,7 +199,7 @@ func createPredicateForSamples(filter: PredicateForSamples) throws -> NSPredicat
201
199
  return createDatePredicate(dateFilter: dateFilter)
202
200
  case .fifth(let w):
203
201
  if let w = w.workout as? WorkoutProxy {
204
- return HKQuery.predicateForObjects(from: w.workout)
202
+ return w.workoutPredicate
205
203
  }
206
204
  throw RuntimeError.error(withMessage: "[react-native-healthkit] Failed to initialize workout for filter")
207
205
  }
@@ -219,7 +217,7 @@ func createPredicateForSamples(filter: FilterForSamples) throws -> NSPredicate {
219
217
  return createDatePredicate(dateFilter: dateFilter)
220
218
  case .fifth(let w):
221
219
  if let w = w.workout as? WorkoutProxy {
222
- return HKQuery.predicateForObjects(from: w.workout)
220
+ return w.workoutPredicate
223
221
  }
224
222
  throw RuntimeError.error(withMessage: "[react-native-healthkit] Failed to initialize workout for filter")
225
223
  case .sixth(let and):
@@ -246,7 +244,7 @@ func createPredicateForSamples(filter: Variant_PredicateWithUUID_PredicateWithUU
246
244
  return createDatePredicate(dateFilter: dateFilter)
247
245
  case .fifth(let w):
248
246
  if let w = w.workout as? WorkoutProxy {
249
- return HKQuery.predicateForObjects(from: w.workout)
247
+ return w.workoutPredicate
250
248
  }
251
249
  throw RuntimeError.error(withMessage: "[react-native-healthkit] Failed to initialize workout for filter")
252
250
  case .sixth(let and):
@@ -274,7 +272,7 @@ func deserializeHKQueryAnchor(base64String: String?) throws -> HKQueryAnchor? {
274
272
  if base64String.isEmpty {
275
273
  return nil
276
274
  }
277
-
275
+
278
276
  // Step 1: Decode the base64 string to a Data object
279
277
  guard let data = Data(base64Encoded: base64String) else {
280
278
  throw RuntimeError.error(withMessage: "[react-native-healthkit] Invalid base64 string: \(base64String)")
@@ -294,13 +292,12 @@ func deserializeHKQueryAnchor(base64String: String?) throws -> HKQueryAnchor? {
294
292
  return nil
295
293
  }
296
294
 
297
-
298
295
  func initializeCategoryType(_ identifier: String) throws -> HKCategoryType {
299
296
  let identifier = HKCategoryTypeIdentifier(rawValue: identifier)
300
297
  if let sampleType = HKSampleType.categoryType(forIdentifier: identifier) {
301
298
  return sampleType
302
299
  }
303
-
300
+
304
301
  throw RuntimeError.error(withMessage: "[react-native-healthkit] Failed to initialize unrecognized categoryType with identifier \(identifier)")
305
302
  }
306
303
 
@@ -308,7 +305,7 @@ func initializeWorkoutActivityType(_ typeIdentifier: UInt) throws -> HKWorkoutAc
308
305
  if let type = HKWorkoutActivityType.init(rawValue: typeIdentifier) {
309
306
  return type
310
307
  }
311
-
308
+
312
309
  throw RuntimeError.error(withMessage: "[react-native-healthkit] Failed to initialize unrecognized quantityType with identifier \(typeIdentifier)")
313
310
  }
314
311
 
@@ -318,17 +315,17 @@ func initializeQuantityType(_ identifier: String) throws -> HKQuantityType {
318
315
  if let sampleType = HKSampleType.quantityType(forIdentifier: identifier) {
319
316
  return sampleType
320
317
  }
321
-
318
+
322
319
  throw RuntimeError.error(withMessage: "[react-native-healthkit] Failed to initialize unrecognized quantityType with identifier \(identifier)")
323
320
  }
324
321
 
325
322
  func initializeCorrelationType(_ identifier: String) throws -> HKCorrelationType {
326
323
  let identifier = HKCorrelationTypeIdentifier(rawValue: identifier)
327
-
324
+
328
325
  if let sampleType = HKSampleType.correlationType(forIdentifier: identifier) {
329
326
  return sampleType
330
327
  }
331
-
328
+
332
329
  throw RuntimeError.error(withMessage: "[react-native-healthkit] Failed to initialize unrecognized correlationType with identifier \(identifier)")
333
330
  }
334
331
 
@@ -336,7 +333,7 @@ func initializeSeriesType(_ identifier: String) throws -> HKSeriesType {
336
333
  if let seriesType = HKObjectType.seriesType(forIdentifier: identifier) {
337
334
  return seriesType
338
335
  }
339
-
336
+
340
337
  throw RuntimeError.error(withMessage: "[react-native-healthkit] Failed to initialize unrecognized seriesType with identifier \(identifier)")
341
338
  }
342
339
 
@@ -344,7 +341,7 @@ func sampleTypeFrom(sampleTypeIdentifier: SampleTypeIdentifier) throws -> HKSamp
344
341
  if let sampleType = try sampleTypeFromStringNullable(typeIdentifier: sampleTypeIdentifier.stringValue) {
345
342
  return sampleType
346
343
  }
347
-
344
+
348
345
  throw RuntimeError.error(withMessage: "[react-native-healthkit] Failed to initialize unrecognized sampleType with identifier \(sampleTypeIdentifier.stringValue)")
349
346
  }
350
347
 
@@ -352,7 +349,7 @@ func sampleTypeFrom(sampleTypeIdentifierWriteable: SampleTypeIdentifierWriteable
352
349
  if let sampleType = try sampleTypeFromStringNullable(typeIdentifier: sampleTypeIdentifierWriteable.stringValue) {
353
350
  return sampleType
354
351
  }
355
-
352
+
356
353
  throw RuntimeError.error(withMessage: "[react-native-healthkit] Failed to initialize unrecognized sampleType with identifier \(sampleTypeIdentifierWriteable.stringValue)")
357
354
  }
358
355
 
@@ -368,7 +365,7 @@ private func sampleTypeFromStringNullable(typeIdentifier: String) throws -> HKSa
368
365
  if typeIdentifier.starts(with: HKCorrelationTypeIdentifier_PREFIX) {
369
366
  return try initializeCorrelationType(typeIdentifier)
370
367
  }
371
-
368
+
372
369
  if typeIdentifier == HKWorkoutTypeIdentifier {
373
370
  return HKSampleType.workoutType()
374
371
  }
@@ -387,12 +384,12 @@ private func sampleTypeFromStringNullable(typeIdentifier: String) throws -> HKSa
387
384
  if typeIdentifier == HKDataTypeIdentifierHeartbeatSeries {
388
385
  return try initializeSeriesType(typeIdentifier)
389
386
  }
390
-
387
+
391
388
  if typeIdentifier == HKAudiogramTypeIdentifier {
392
389
  return HKSampleType.audiogramSampleType()
393
390
  }
394
391
  }
395
-
392
+
396
393
  #if compiler(>=6)
397
394
  if #available(iOS 18, *) {
398
395
  if typeIdentifier == HKStateOfMindTypeIdentifier {
@@ -421,7 +418,7 @@ func initializeUUID(_ uuidString: String) throws -> UUID {
421
418
  if let uuid = UUID(uuidString: uuidString) {
422
419
  return uuid
423
420
  }
424
-
421
+
425
422
  throw RuntimeError.error(withMessage: "[react-native-healthkit] Got invalid UUID: \(uuidString)")
426
423
  }
427
424
 
@@ -449,14 +446,13 @@ func sampleTypesFromArray(typeIdentifiersWriteable: [SampleTypeIdentifierWriteab
449
446
  })
450
447
  }
451
448
 
452
-
453
449
  // objectType is wider than sampleType, so it uses it under the hood
454
450
  func objectTypeFrom(objectTypeIdentifier: ObjectTypeIdentifier) throws -> HKObjectType {
455
451
  let typeIdentifier = objectTypeIdentifier.stringValue
456
452
  if let sampleType = try sampleTypeFromStringNullable(typeIdentifier: typeIdentifier) {
457
453
  return sampleType
458
454
  }
459
-
455
+
460
456
  if typeIdentifier.starts(with: HKCharacteristicTypeIdentifier_PREFIX) {
461
457
  let identifier = HKCharacteristicTypeIdentifier.init(rawValue: typeIdentifier)
462
458
  if let type = HKObjectType.characteristicType(forIdentifier: identifier) as HKObjectType? {
@@ -217,6 +217,13 @@ class WorkoutProxy: HybridWorkoutProxySpec {
217
217
  )
218
218
  }
219
219
 
220
+ var workoutPredicate: NSPredicate {
221
+ get {
222
+ let predicate = HKQuery.predicateForObjects(from: self.workout)
223
+ return predicate
224
+ }
225
+ }
226
+
220
227
  var uuid: String {
221
228
  get {
222
229
  return workout.uuid.uuidString
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kingstinct/react-native-healthkit",
3
- "version": "9.0.0",
3
+ "version": "9.0.1",
4
4
  "description": "React Native bindings for HealthKit",
5
5
  "main": "lib/index",
6
6
  "module": "lib/index",