@kingstinct/react-native-healthkit 13.0.0 → 13.0.2

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 (133) hide show
  1. package/ReactNativeHealthkit.podspec +2 -0
  2. package/ios/CoreModule.swift +1 -1
  3. package/ios/CorrelationTypeModule.swift +5 -2
  4. package/ios/ExceptionCatcher.h +17 -0
  5. package/ios/Helpers.swift +55 -33
  6. package/ios/QuantityTypeModule.swift +65 -74
  7. package/ios/WorkoutsModule.swift +1 -1
  8. package/nitrogen/generated/ios/ReactNativeHealthkit-Swift-Cxx-Bridge.cpp +1 -0
  9. package/nitrogen/generated/ios/swift/AuthDataTypes.swift +1 -0
  10. package/nitrogen/generated/ios/swift/CategorySample.swift +1 -0
  11. package/nitrogen/generated/ios/swift/CategorySampleForSaving.swift +1 -0
  12. package/nitrogen/generated/ios/swift/CategorySamplesWithAnchorResponse.swift +1 -0
  13. package/nitrogen/generated/ios/swift/CorrelationSample.swift +1 -0
  14. package/nitrogen/generated/ios/swift/DateFilter.swift +1 -0
  15. package/nitrogen/generated/ios/swift/DeletedSample.swift +1 -0
  16. package/nitrogen/generated/ios/swift/Device.swift +1 -0
  17. package/nitrogen/generated/ios/swift/ECGQueryOptionsWithAnchor.swift +1 -0
  18. package/nitrogen/generated/ios/swift/ECGQueryOptionsWithSortOrder.swift +1 -0
  19. package/nitrogen/generated/ios/swift/ElectrocardiogramSample.swift +1 -0
  20. package/nitrogen/generated/ios/swift/ElectrocardiogramSamplesWithAnchorResponse.swift +1 -0
  21. package/nitrogen/generated/ios/swift/ElectrocardiogramVoltage.swift +1 -0
  22. package/nitrogen/generated/ios/swift/FilterForSamples.swift +1 -0
  23. package/nitrogen/generated/ios/swift/FilterForSamplesBase.swift +1 -0
  24. package/nitrogen/generated/ios/swift/FilterForWorkouts.swift +1 -0
  25. package/nitrogen/generated/ios/swift/FilterForWorkoutsBase.swift +1 -0
  26. package/nitrogen/generated/ios/swift/Func_void_AuthorizationRequestStatus.swift +1 -1
  27. package/nitrogen/generated/ios/swift/Func_void_BiologicalSex.swift +1 -1
  28. package/nitrogen/generated/ios/swift/Func_void_BloodType.swift +1 -1
  29. package/nitrogen/generated/ios/swift/Func_void_CategorySamplesWithAnchorResponse.swift +1 -1
  30. package/nitrogen/generated/ios/swift/Func_void_ElectrocardiogramSamplesWithAnchorResponse.swift +1 -1
  31. package/nitrogen/generated/ios/swift/Func_void_FitzpatrickSkinType.swift +1 -1
  32. package/nitrogen/generated/ios/swift/Func_void_HeartbeatSeriesSamplesWithAnchorResponse.swift +1 -1
  33. package/nitrogen/generated/ios/swift/Func_void_MedicationDoseEventsWithAnchorResponse.swift +1 -1
  34. package/nitrogen/generated/ios/swift/Func_void_OnChangeCallbackArgs.swift +1 -1
  35. package/nitrogen/generated/ios/swift/Func_void_QuantitySamplesWithAnchorResponse.swift +1 -1
  36. package/nitrogen/generated/ios/swift/Func_void_QueryCorrelationSamplesWithAnchorResponse.swift +1 -1
  37. package/nitrogen/generated/ios/swift/Func_void_QueryStatisticsResponse.swift +1 -1
  38. package/nitrogen/generated/ios/swift/Func_void_QueryWorkoutSamplesWithAnchorResponse.swift +1 -1
  39. package/nitrogen/generated/ios/swift/Func_void_StateOfMindSamplesWithAnchorResponse.swift +1 -1
  40. package/nitrogen/generated/ios/swift/Func_void_WheelchairUse.swift +1 -1
  41. package/nitrogen/generated/ios/swift/Func_void_bool.swift +1 -1
  42. package/nitrogen/generated/ios/swift/Func_void_double.swift +1 -1
  43. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +1 -1
  44. package/nitrogen/generated/ios/swift/Func_void_std__optional_CategorySample_.swift +1 -1
  45. package/nitrogen/generated/ios/swift/Func_void_std__optional_CorrelationSample_.swift +1 -1
  46. package/nitrogen/generated/ios/swift/Func_void_std__optional_QuantitySample_.swift +1 -1
  47. package/nitrogen/generated/ios/swift/Func_void_std__optional_QueryStatisticsResponse_.swift +1 -1
  48. package/nitrogen/generated/ios/swift/Func_void_std__optional_StateOfMindSample_.swift +1 -1
  49. package/nitrogen/generated/ios/swift/Func_void_std__optional_WorkoutPlan_.swift +1 -1
  50. package/nitrogen/generated/ios/swift/Func_void_std__optional_std__chrono__system_clock__time_point_.swift +1 -1
  51. package/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_HybridWorkoutProxySpec_.swift +1 -1
  52. package/nitrogen/generated/ios/swift/Func_void_std__unordered_map_std__string__QueryStatisticsResponse_.swift +1 -1
  53. package/nitrogen/generated/ios/swift/Func_void_std__unordered_map_std__string__bool_.swift +1 -1
  54. package/nitrogen/generated/ios/swift/Func_void_std__vector_CategorySample_.swift +1 -1
  55. package/nitrogen/generated/ios/swift/Func_void_std__vector_CorrelationSample_.swift +1 -1
  56. package/nitrogen/generated/ios/swift/Func_void_std__vector_ElectrocardiogramSample_.swift +1 -1
  57. package/nitrogen/generated/ios/swift/Func_void_std__vector_HeartbeatSeriesSample_.swift +1 -1
  58. package/nitrogen/generated/ios/swift/Func_void_std__vector_IdentifierWithUnit_.swift +1 -1
  59. package/nitrogen/generated/ios/swift/Func_void_std__vector_MedicationDoseEvent_.swift +1 -1
  60. package/nitrogen/generated/ios/swift/Func_void_std__vector_QuantitySample_.swift +1 -1
  61. package/nitrogen/generated/ios/swift/Func_void_std__vector_QueryStatisticsResponseFromSingleSource_.swift +1 -1
  62. package/nitrogen/generated/ios/swift/Func_void_std__vector_QueryStatisticsResponse_.swift +1 -1
  63. package/nitrogen/generated/ios/swift/Func_void_std__vector_StateOfMindSample_.swift +1 -1
  64. package/nitrogen/generated/ios/swift/Func_void_std__vector_UserAnnotatedMedication_.swift +1 -1
  65. package/nitrogen/generated/ios/swift/Func_void_std__vector_WorkoutRoute_.swift +1 -1
  66. package/nitrogen/generated/ios/swift/Func_void_std__vector_std__shared_ptr_HybridSourceProxySpec__.swift +1 -1
  67. package/nitrogen/generated/ios/swift/Func_void_std__vector_std__shared_ptr_HybridWorkoutProxySpec__.swift +1 -1
  68. package/nitrogen/generated/ios/swift/Heartbeat.swift +1 -0
  69. package/nitrogen/generated/ios/swift/HeartbeatSeriesSample.swift +1 -0
  70. package/nitrogen/generated/ios/swift/HeartbeatSeriesSamplesWithAnchorResponse.swift +1 -0
  71. package/nitrogen/generated/ios/swift/HybridCategoryTypeModuleSpec.swift +0 -1
  72. package/nitrogen/generated/ios/swift/HybridCategoryTypeModuleSpec_cxx.swift +0 -1
  73. package/nitrogen/generated/ios/swift/HybridCharacteristicTypeModuleSpec.swift +0 -1
  74. package/nitrogen/generated/ios/swift/HybridCharacteristicTypeModuleSpec_cxx.swift +0 -1
  75. package/nitrogen/generated/ios/swift/HybridCoreModuleSpec.swift +0 -1
  76. package/nitrogen/generated/ios/swift/HybridCoreModuleSpec_cxx.swift +0 -1
  77. package/nitrogen/generated/ios/swift/HybridCorrelationTypeModuleSpec.swift +0 -1
  78. package/nitrogen/generated/ios/swift/HybridCorrelationTypeModuleSpec_cxx.swift +0 -1
  79. package/nitrogen/generated/ios/swift/HybridElectrocardiogramModuleSpec.swift +0 -1
  80. package/nitrogen/generated/ios/swift/HybridElectrocardiogramModuleSpec_cxx.swift +0 -1
  81. package/nitrogen/generated/ios/swift/HybridHeartbeatSeriesModuleSpec.swift +0 -1
  82. package/nitrogen/generated/ios/swift/HybridHeartbeatSeriesModuleSpec_cxx.swift +0 -1
  83. package/nitrogen/generated/ios/swift/HybridMedicationModuleSpec.swift +0 -1
  84. package/nitrogen/generated/ios/swift/HybridMedicationModuleSpec_cxx.swift +0 -1
  85. package/nitrogen/generated/ios/swift/HybridQuantityTypeModuleSpec.swift +0 -1
  86. package/nitrogen/generated/ios/swift/HybridQuantityTypeModuleSpec_cxx.swift +0 -1
  87. package/nitrogen/generated/ios/swift/HybridStateOfMindModuleSpec.swift +0 -1
  88. package/nitrogen/generated/ios/swift/HybridStateOfMindModuleSpec_cxx.swift +0 -1
  89. package/nitrogen/generated/ios/swift/HybridWorkoutProxySpec.swift +0 -1
  90. package/nitrogen/generated/ios/swift/HybridWorkoutProxySpec_cxx.swift +0 -1
  91. package/nitrogen/generated/ios/swift/HybridWorkoutsModuleSpec.swift +0 -1
  92. package/nitrogen/generated/ios/swift/HybridWorkoutsModuleSpec_cxx.swift +0 -1
  93. package/nitrogen/generated/ios/swift/IdentifierWithUnit.swift +1 -0
  94. package/nitrogen/generated/ios/swift/IntervalComponents.swift +1 -0
  95. package/nitrogen/generated/ios/swift/LocationForSaving.swift +1 -0
  96. package/nitrogen/generated/ios/swift/MedicationConcept.swift +1 -0
  97. package/nitrogen/generated/ios/swift/MedicationDoseEvent.swift +1 -0
  98. package/nitrogen/generated/ios/swift/MedicationDoseEventsWithAnchorResponse.swift +1 -0
  99. package/nitrogen/generated/ios/swift/OnChangeCallbackArgs.swift +1 -0
  100. package/nitrogen/generated/ios/swift/PredicateWithMetadataKey.swift +1 -0
  101. package/nitrogen/generated/ios/swift/Quantity.swift +1 -0
  102. package/nitrogen/generated/ios/swift/QuantityDateInterval.swift +1 -0
  103. package/nitrogen/generated/ios/swift/QuantitySample.swift +1 -0
  104. package/nitrogen/generated/ios/swift/QuantitySampleForSaving.swift +1 -0
  105. package/nitrogen/generated/ios/swift/QuantitySamplesWithAnchorResponse.swift +1 -0
  106. package/nitrogen/generated/ios/swift/QueryCorrelationSamplesWithAnchorResponse.swift +1 -0
  107. package/nitrogen/generated/ios/swift/QueryOptionsWithAnchor.swift +1 -0
  108. package/nitrogen/generated/ios/swift/QueryOptionsWithAnchorAndUnit.swift +1 -0
  109. package/nitrogen/generated/ios/swift/QueryOptionsWithSortOrder.swift +1 -0
  110. package/nitrogen/generated/ios/swift/QueryOptionsWithSortOrderAndUnit.swift +1 -0
  111. package/nitrogen/generated/ios/swift/QueryStatisticsResponse.swift +1 -0
  112. package/nitrogen/generated/ios/swift/QueryStatisticsResponseFromSingleSource.swift +1 -0
  113. package/nitrogen/generated/ios/swift/QueryWorkoutSamplesWithAnchorResponse.swift +1 -0
  114. package/nitrogen/generated/ios/swift/RelatedCoding.swift +1 -0
  115. package/nitrogen/generated/ios/swift/SampleType.swift +1 -0
  116. package/nitrogen/generated/ios/swift/Source.swift +1 -0
  117. package/nitrogen/generated/ios/swift/SourceRevision.swift +1 -0
  118. package/nitrogen/generated/ios/swift/StateOfMindSample.swift +1 -0
  119. package/nitrogen/generated/ios/swift/StateOfMindSamplesWithAnchorResponse.swift +1 -0
  120. package/nitrogen/generated/ios/swift/StatisticsQueryOptions.swift +1 -0
  121. package/nitrogen/generated/ios/swift/UserAnnotatedMedication.swift +1 -0
  122. package/nitrogen/generated/ios/swift/WorkoutActivity.swift +1 -0
  123. package/nitrogen/generated/ios/swift/WorkoutConfiguration.swift +1 -0
  124. package/nitrogen/generated/ios/swift/WorkoutDurationPredicate.swift +1 -0
  125. package/nitrogen/generated/ios/swift/WorkoutEvent.swift +1 -0
  126. package/nitrogen/generated/ios/swift/WorkoutPlan.swift +1 -0
  127. package/nitrogen/generated/ios/swift/WorkoutQueryOptions.swift +1 -0
  128. package/nitrogen/generated/ios/swift/WorkoutQueryOptionsWithAnchor.swift +1 -0
  129. package/nitrogen/generated/ios/swift/WorkoutRoute.swift +1 -0
  130. package/nitrogen/generated/ios/swift/WorkoutRouteLocation.swift +1 -0
  131. package/nitrogen/generated/ios/swift/WorkoutSample.swift +1 -0
  132. package/nitrogen/generated/ios/swift/WorkoutTotals.swift +1 -0
  133. package/package.json +4 -4
@@ -22,6 +22,8 @@ Pod::Spec.new do |s|
22
22
  "cpp/**/*.{hpp,cpp}",
23
23
  ]
24
24
 
25
+ s.public_header_files = "ios/**/*.h"
26
+
25
27
  s.pod_target_xcconfig = {
26
28
  # C++ compiler flags, mainly for folly.
27
29
  "GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) FOLLY_NO_CONFIG FOLLY_CFG_NO_COROUTINES"
@@ -19,7 +19,7 @@ private var quantityTypeUnitCache = [HKQuantityType: HKUnit]()
19
19
 
20
20
  func getUnitToUse(unitOverride: String?, quantityType: HKQuantityType) async throws -> HKUnit {
21
21
  if let unitOverride = unitOverride {
22
- let unit = HKUnit(from: unitOverride)
22
+ let unit = try parseUnitStringSafe(unitOverride)
23
23
 
24
24
  if !quantityType.is(compatibleWith: unit) {
25
25
  throw runtimeErrorWithPrefix(
@@ -137,8 +137,11 @@ class CorrelationTypeModule: HybridCorrelationTypeModuleSpec {
137
137
  continue
138
138
  }
139
139
 
140
- let unit = HKUnit(from: quantitySample.unit)
141
- let quantity = HKQuantity(unit: unit, doubleValue: quantitySample.quantity)
140
+ let unit = try parseUnitStringSafe(quantitySample.unit)
141
+ let quantity = HKQuantity(
142
+ unit: unit,
143
+ doubleValue: quantitySample.quantity
144
+ )
142
145
  let hkQuantitySample = HKQuantitySample(
143
146
  type: quantityType,
144
147
  quantity: quantity,
@@ -0,0 +1,17 @@
1
+ #import <Foundation/Foundation.h>
2
+ #import <HealthKit/HealthKit.h>
3
+
4
+ NS_INLINE HKUnit * _Nullable HKUnitFromStringCatchingExceptions(NSString * _Nonnull unitString, NSError * _Nullable * _Nullable outError) {
5
+ if (outError) { *outError = nil; }
6
+ @try {
7
+ HKUnit *unit = [HKUnit unitFromString:unitString];
8
+ return unit;
9
+ }
10
+ @catch (NSException *exception) {
11
+ if (outError) {
12
+ NSDictionary *userInfo = exception.userInfo ?: @{};
13
+ *outError = [NSError errorWithDomain:exception.name code:0 userInfo:userInfo];
14
+ }
15
+ return nil;
16
+ }
17
+ }
package/ios/Helpers.swift CHANGED
@@ -9,6 +9,17 @@ import Foundation
9
9
  import HealthKit
10
10
  import NitroModules
11
11
 
12
+ func parseUnitStringSafe(_ unitString: String) throws -> HKUnit {
13
+ var err: NSError?
14
+ let unitOut = HKUnitFromStringCatchingExceptions(unitString, &err)
15
+
16
+ if let hkUnit = unitOut {
17
+ return hkUnit
18
+ }
19
+
20
+ throw runtimeErrorWithPrefix("Supplied invalid '\(unitString)' as HKUnit")
21
+ }
22
+
12
23
  func getQueryLimit(_ limit: Double) -> Int {
13
24
  if limit == .infinity || limit <= 0 || limit == .nan || limit == .signalingNaN {
14
25
  return HKObjectQueryNoLimit
@@ -355,39 +366,6 @@ func objectTypeFrom(objectTypeIdentifier: ObjectTypeIdentifier) throws -> HKObje
355
366
  "Failed initializing unrecognized objectType identifier " + typeIdentifier)
356
367
  }
357
368
 
358
- func hkStatisticsOptionsFromOptions(_ options: NSArray) -> HKStatisticsOptions {
359
- var opts = HKStatisticsOptions()
360
-
361
- for o in options {
362
- guard let str = o as? String else { continue }
363
-
364
- switch str {
365
- case "cumulativeSum":
366
- opts.insert(.cumulativeSum)
367
- case "discreteAverage":
368
- opts.insert(.discreteAverage)
369
- case "discreteMax":
370
- opts.insert(.discreteMax)
371
- case "discreteMin":
372
- opts.insert(.discreteMin)
373
- case "duration":
374
- if #available(iOS 13, *) {
375
- opts.insert(.duration)
376
- }
377
- case "mostRecent":
378
- if #available(iOS 13, *) {
379
- opts.insert(.mostRecent)
380
- }
381
- case "separateBySource":
382
- opts.insert(.separateBySource)
383
- default:
384
- continue
385
- }
386
- }
387
-
388
- return opts
389
- }
390
-
391
369
  func componentsFromInterval(_ interval: NSDictionary) -> DateComponents {
392
370
  let componentKeys: [String: WritableKeyPath<DateComponents, Int?>] = [
393
371
  "minute": \.minute,
@@ -443,3 +421,47 @@ func runtimeErrorWithPrefix(_ withMessage: String) -> Error {
443
421
  func warnWithPrefix(_ withMessage: String) {
444
422
  print("[react-native-healthkit] \(withMessage)")
445
423
  }
424
+
425
+ func buildStatisticsOptions(statistics: [StatisticsOptions], quantityType: HKQuantityType) -> HKStatisticsOptions {
426
+
427
+ // Build statistics options
428
+ var opts = HKStatisticsOptions()
429
+ opts.insert(.separateBySource)
430
+ for statistic in statistics {
431
+ if statistic == .cumulativesum {
432
+ if quantityType.aggregationStyle == .cumulative {
433
+ opts.insert(HKStatisticsOptions.cumulativeSum)
434
+ } else {
435
+ warnWithPrefix("buildStatisticsOptions: cumulativesum statistic requested for discrete quantity type \(quantityType.identifier)")
436
+ }
437
+
438
+ } else if statistic == .discreteaverage {
439
+ if quantityType.aggregationStyle != .cumulative {
440
+ opts.insert(HKStatisticsOptions.discreteAverage)
441
+ } else {
442
+ warnWithPrefix("buildStatisticsOptions: discreteaverage statistic requested for cumulative quantity type \(quantityType.identifier)")
443
+ }
444
+ } else if statistic == .discretemax {
445
+ if quantityType.aggregationStyle != .cumulative {
446
+ opts.insert(HKStatisticsOptions.discreteMax)
447
+ } else {
448
+ warnWithPrefix("buildStatisticsOptions: discretemax statistic requested for cumulative quantity type \(quantityType.identifier)")
449
+ }
450
+ } else if statistic == .discretemin {
451
+ if quantityType.aggregationStyle != .cumulative {
452
+ opts.insert(HKStatisticsOptions.discreteMin)
453
+ } else {
454
+ warnWithPrefix("buildStatisticsOptions: discretemin statistic requested for cumulative quantity type \(quantityType.identifier)")
455
+ }
456
+ }
457
+ if #available(iOS 13, *) {
458
+ if statistic == .duration {
459
+ opts.insert(HKStatisticsOptions.duration)
460
+ }
461
+ if statistic == .mostrecent {
462
+ opts.insert(HKStatisticsOptions.mostRecent)
463
+ }
464
+ }
465
+ }
466
+ return opts
467
+ }
@@ -1,11 +1,12 @@
1
1
  import HealthKit
2
2
  import NitroModules
3
3
 
4
- func emptyStatisticsResponse(from: Date, to: Date) -> QueryStatisticsResponse {
4
+ func emptyStatisticsResponse(from: Date?, to: Date?) -> QueryStatisticsResponse {
5
5
  var response = QueryStatisticsResponse()
6
6
 
7
7
  response.startDate = from
8
8
  response.endDate = to
9
+ response.sources = []
9
10
 
10
11
  return response
11
12
  }
@@ -14,18 +15,20 @@ func queryStatisticsForQuantityInternal(
14
15
  quantityType: HKQuantityType,
15
16
  statistics: [StatisticsOptions],
16
17
  options: StatisticsQueryOptions?
17
- ) async throws -> HKStatistics {
18
+ ) async throws -> HKStatistics? {
18
19
  let predicate = createPredicateForSamples(options?.filter)
19
20
 
20
21
  return try await withCheckedThrowingContinuation { continuation in
21
22
  let query = HKStatisticsQuery.init(
22
23
  quantityType: quantityType,
23
24
  quantitySamplePredicate: predicate,
24
- options: buildStatisticsOptions(statistics: statistics)
25
+ options: buildStatisticsOptions(statistics: statistics, quantityType: quantityType)
25
26
  ) { (_, stats: HKStatistics?, error: Error?) in
26
27
  DispatchQueue.main.async {
27
28
  if let error = error {
28
- return continuation.resume(throwing: error)
29
+ return handleHKNoDataOrThrow(error: error, continuation: continuation, noDataFallback: {
30
+ return nil
31
+ })
29
32
  }
30
33
 
31
34
  if let stats = stats {
@@ -49,6 +52,10 @@ func serializeStatistics(gottenStats: HKStatistics, unit: HKUnit) -> QueryStatis
49
52
  response.startDate = gottenStats.startDate
50
53
  response.endDate = gottenStats.endDate
51
54
 
55
+ response.sources = gottenStats.sources?.map { source in
56
+ return serializeSource(source)
57
+ } ?? []
58
+
52
59
  if let averageQuantity = gottenStats.averageQuantity() {
53
60
  response.averageQuantity = Quantity(
54
61
  unit: unit.unitString,
@@ -109,7 +116,7 @@ func queryStatisticsCollectionForQuantityInternal(
109
116
  anchorDate: Date,
110
117
  intervalComponents: IntervalComponents,
111
118
  options: StatisticsQueryOptions?
112
- ) async throws -> HKStatisticsCollection {
119
+ ) async throws -> HKStatisticsCollection? {
113
120
  let predicate = createPredicateForSamples(options?.filter)
114
121
 
115
122
  // Create date components from interval
@@ -131,9 +138,7 @@ func queryStatisticsCollectionForQuantityInternal(
131
138
  }
132
139
 
133
140
  // Build statistics options
134
- let opts = buildStatisticsOptions(statistics: statistics)
135
-
136
- let unit = try await getUnitToUse(unitOverride: options?.unit, quantityType: quantityType)
141
+ let opts = buildStatisticsOptions(statistics: statistics, quantityType: quantityType)
137
142
 
138
143
  return try await withCheckedThrowingContinuation { continuation in
139
144
  let query = HKStatisticsCollectionQuery.init(
@@ -146,7 +151,9 @@ func queryStatisticsCollectionForQuantityInternal(
146
151
 
147
152
  query.initialResultsHandler = { (_, results: HKStatisticsCollection?, error: Error?) in
148
153
  if let error = error {
149
- return continuation.resume(throwing: error)
154
+ return handleHKNoDataOrThrow(error: error, continuation: continuation, noDataFallback: {
155
+ return nil
156
+ })
150
157
  }
151
158
 
152
159
  guard let statistics = results else {
@@ -250,31 +257,6 @@ func getAnyMapValue(_ anyMap: AnyMap, key: String) -> Any? {
250
257
  return nil
251
258
  }
252
259
 
253
- func buildStatisticsOptions(statistics: [StatisticsOptions]) -> HKStatisticsOptions {
254
- // Build statistics options
255
- var opts = HKStatisticsOptions()
256
- for statistic in statistics {
257
- if statistic == .cumulativesum {
258
- opts.insert(HKStatisticsOptions.cumulativeSum)
259
- } else if statistic == .discreteaverage {
260
- opts.insert(HKStatisticsOptions.discreteAverage)
261
- } else if statistic == .discretemax {
262
- opts.insert(HKStatisticsOptions.discreteMax)
263
- } else if statistic == .discretemin {
264
- opts.insert(HKStatisticsOptions.discreteMin)
265
- }
266
- if #available(iOS 13, *) {
267
- if statistic == .duration {
268
- opts.insert(HKStatisticsOptions.duration)
269
- }
270
- if statistic == .mostrecent {
271
- opts.insert(HKStatisticsOptions.mostRecent)
272
- }
273
- }
274
- }
275
- return opts
276
- }
277
-
278
260
  /// Handles HealthKit's `errorNoData` by resuming the continuation with a fallback value if provided,
279
261
  /// otherwise resumes with `nil` for Optional result types. For other errors, resumes by throwing.
280
262
  /// - Parameters:
@@ -303,20 +285,19 @@ class QuantityTypeModule: HybridQuantityTypeModuleSpec {
303
285
  return Promise.async {
304
286
  let quantityType = try initializeQuantityType(identifier.stringValue)
305
287
 
306
- let gottenStats = try await queryStatisticsForQuantityInternal(
288
+ if let gottenStats = try await queryStatisticsForQuantityInternal(
307
289
  quantityType: quantityType,
308
290
  statistics: statistics,
309
291
  options: options
310
- )
311
-
312
- let unit = try await getUnitToUse(
313
- unitOverride: options?.unit,
314
- quantityType: quantityType
315
- )
316
-
317
- let response = serializeStatisticsPerSource(gottenStats: gottenStats, unit: unit)
318
-
319
- return response
292
+ ) {
293
+ let unit = try await getUnitToUse(
294
+ unitOverride: options?.unit,
295
+ quantityType: quantityType
296
+ )
297
+ return serializeStatisticsPerSource(gottenStats: gottenStats, unit: unit)
298
+ } else {
299
+ return []
300
+ }
320
301
  }
321
302
  }
322
303
 
@@ -327,22 +308,24 @@ class QuantityTypeModule: HybridQuantityTypeModuleSpec {
327
308
  return Promise.async {
328
309
  let quantityType = try initializeQuantityType(identifier.stringValue)
329
310
 
330
- let statistics = try await queryStatisticsCollectionForQuantityInternal(
311
+ if let statistics = try await queryStatisticsCollectionForQuantityInternal(
331
312
  quantityType: quantityType,
332
313
  statistics: statistics,
333
314
  anchorDate: anchorDate,
334
315
  intervalComponents: intervalComponents,
335
316
  options: options
336
- )
317
+ ) {
337
318
 
338
- let unit = try await getUnitToUse(
339
- unitOverride: options?.unit,
340
- quantityType: quantityType
341
- )
319
+ let unit = try await getUnitToUse(
320
+ unitOverride: options?.unit,
321
+ quantityType: quantityType
322
+ )
342
323
 
343
- return statistics.statistics().flatMap { statistics in
344
- return serializeStatisticsPerSource(gottenStats: statistics, unit: unit)
324
+ return statistics.statistics().flatMap { statistics in
325
+ return serializeStatisticsPerSource(gottenStats: statistics, unit: unit)
326
+ }
345
327
  }
328
+ return []
346
329
  }
347
330
  }
348
331
 
@@ -361,30 +344,36 @@ class QuantityTypeModule: HybridQuantityTypeModuleSpec {
361
344
  func isQuantityCompatibleWithUnit(identifier: QuantityTypeIdentifier, unit: String) throws -> Bool {
362
345
  let sampleType = try initializeQuantityType(identifier.stringValue)
363
346
 
364
- return sampleType.is(compatibleWith: HKUnit.init(from: unit))
347
+ let hkUnit = try parseUnitStringSafe(unit)
348
+
349
+ return sampleType.is(compatibleWith: hkUnit)
365
350
  }
366
351
 
367
352
  func queryStatisticsForQuantity(
368
- identifier: QuantityTypeIdentifier, statistics: [StatisticsOptions],
353
+ identifier: QuantityTypeIdentifier,
354
+ statistics: [StatisticsOptions],
369
355
  options: StatisticsQueryOptions?
370
356
  ) -> Promise<QueryStatisticsResponse> {
371
357
  return Promise.async {
372
358
  let quantityType = try initializeQuantityType(identifier.stringValue)
373
359
 
374
- let gottenStats = try await queryStatisticsForQuantityInternal(
360
+ if let gottenStats = try await queryStatisticsForQuantityInternal(
375
361
  quantityType: quantityType,
376
362
  statistics: statistics,
377
363
  options: options
378
- )
379
-
380
- let unit = try await getUnitToUse(
381
- unitOverride: options?.unit,
382
- quantityType: quantityType
383
- )
364
+ ) {
365
+ let unit = try await getUnitToUse(
366
+ unitOverride: options?.unit,
367
+ quantityType: quantityType
368
+ )
384
369
 
385
- let response = serializeStatistics(gottenStats: gottenStats, unit: unit)
370
+ return serializeStatistics(gottenStats: gottenStats, unit: unit)
371
+ }
386
372
 
387
- return response
373
+ return emptyStatisticsResponse(
374
+ from: options?.filter?.date?.startDate,
375
+ to: options?.filter?.date?.endDate
376
+ )
388
377
  }
389
378
  }
390
379
 
@@ -395,22 +384,24 @@ class QuantityTypeModule: HybridQuantityTypeModuleSpec {
395
384
  return Promise.async {
396
385
  let quantityType = try initializeQuantityType(identifier.stringValue)
397
386
 
398
- let statistics = try await queryStatisticsCollectionForQuantityInternal(
387
+ if let statistics = try await queryStatisticsCollectionForQuantityInternal(
399
388
  quantityType: quantityType,
400
389
  statistics: statistics,
401
390
  anchorDate: anchorDate,
402
391
  intervalComponents: intervalComponents,
403
392
  options: options
404
- )
405
-
406
- let unit = try await getUnitToUse(
407
- unitOverride: options?.unit,
408
- quantityType: quantityType
409
- )
393
+ ) {
394
+ let unit = try await getUnitToUse(
395
+ unitOverride: options?.unit,
396
+ quantityType: quantityType
397
+ )
410
398
 
411
- return statistics.statistics().map { statistics in
412
- return serializeStatistics(gottenStats: statistics, unit: unit)
399
+ return statistics.statistics().map { statistics in
400
+ return serializeStatistics(gottenStats: statistics, unit: unit)
401
+ }
413
402
  }
403
+
404
+ return []
414
405
  }
415
406
  }
416
407
 
@@ -464,7 +455,7 @@ class QuantityTypeModule: HybridQuantityTypeModuleSpec {
464
455
  metadata: AnyMap?
465
456
  ) -> Promise<QuantitySample?> {
466
457
  return Promise.async {
467
- let unit = HKUnit.init(from: unit)
458
+ let unit = try parseUnitStringSafe(unit)
468
459
  let quantity = HKQuantity.init(unit: unit, doubleValue: value)
469
460
  let typeIdentifier = HKQuantityType(
470
461
  HKQuantityTypeIdentifier(rawValue: identifier.stringValue)
@@ -104,7 +104,7 @@ class WorkoutsModule: HybridWorkoutsModuleSpec {
104
104
  let quantityVal = quantity.quantity
105
105
  let quantityStart = quantity.startDate
106
106
  let quantityEnd = quantity.endDate
107
- let unit = HKUnit.init(from: unitStr)
107
+ let unit = try parseUnitStringSafe(unitStr)
108
108
  let quantity = HKQuantity.init(unit: unit, doubleValue: quantityVal)
109
109
 
110
110
  if quantity.is(compatibleWith: HKUnit.kilocalorie()) {
@@ -21,6 +21,7 @@
21
21
  #include "HybridWorkoutProxySpecSwift.hpp"
22
22
  #include "HybridWorkoutsModuleSpecSwift.hpp"
23
23
  #include "ReactNativeHealthkit-Swift-Cxx-Umbrella.hpp"
24
+ #include <NitroModules/NitroDefines.hpp>
24
25
 
25
26
  namespace margelo::nitro::healthkit::bridge::swift {
26
27
 
@@ -5,6 +5,7 @@
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import Foundation
8
9
  import NitroModules
9
10
 
10
11
  /**
@@ -5,6 +5,7 @@
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import Foundation
8
9
  import NitroModules
9
10
 
10
11
  /**
@@ -5,6 +5,7 @@
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import Foundation
8
9
  import NitroModules
9
10
 
10
11
  /**
@@ -5,6 +5,7 @@
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import Foundation
8
9
  import NitroModules
9
10
 
10
11
  /**
@@ -5,6 +5,7 @@
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import Foundation
8
9
  import NitroModules
9
10
 
10
11
  /**
@@ -5,6 +5,7 @@
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import Foundation
8
9
  import NitroModules
9
10
 
10
11
  /**
@@ -5,6 +5,7 @@
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import Foundation
8
9
  import NitroModules
9
10
 
10
11
  /**
@@ -5,6 +5,7 @@
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import Foundation
8
9
  import NitroModules
9
10
 
10
11
  /**
@@ -5,6 +5,7 @@
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import Foundation
8
9
  import NitroModules
9
10
 
10
11
  /**
@@ -5,6 +5,7 @@
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import Foundation
8
9
  import NitroModules
9
10
 
10
11
  /**
@@ -5,6 +5,7 @@
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import Foundation
8
9
  import NitroModules
9
10
 
10
11
  /**
@@ -5,6 +5,7 @@
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import Foundation
8
9
  import NitroModules
9
10
 
10
11
  /**
@@ -5,6 +5,7 @@
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import Foundation
8
9
  import NitroModules
9
10
 
10
11
  /**
@@ -5,6 +5,7 @@
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import Foundation
8
9
  import NitroModules
9
10
 
10
11
  /**
@@ -5,6 +5,7 @@
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import Foundation
8
9
  import NitroModules
9
10
 
10
11
  /**
@@ -5,6 +5,7 @@
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import Foundation
8
9
  import NitroModules
9
10
 
10
11
  /**
@@ -5,6 +5,7 @@
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import Foundation
8
9
  import NitroModules
9
10
 
10
11
  /**
@@ -5,9 +5,9 @@
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import Foundation
8
9
  import NitroModules
9
10
 
10
-
11
11
  /**
12
12
  * Wraps a Swift `(_ value: AuthorizationRequestStatus) -> Void` as a class.
13
13
  * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`.
@@ -5,9 +5,9 @@
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import Foundation
8
9
  import NitroModules
9
10
 
10
-
11
11
  /**
12
12
  * Wraps a Swift `(_ value: BiologicalSex) -> Void` as a class.
13
13
  * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`.
@@ -5,9 +5,9 @@
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import Foundation
8
9
  import NitroModules
9
10
 
10
-
11
11
  /**
12
12
  * Wraps a Swift `(_ value: BloodType) -> Void` as a class.
13
13
  * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`.
@@ -5,9 +5,9 @@
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import Foundation
8
9
  import NitroModules
9
10
 
10
-
11
11
  /**
12
12
  * Wraps a Swift `(_ value: CategorySamplesWithAnchorResponse) -> Void` as a class.
13
13
  * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`.
@@ -5,9 +5,9 @@
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import Foundation
8
9
  import NitroModules
9
10
 
10
-
11
11
  /**
12
12
  * Wraps a Swift `(_ value: ElectrocardiogramSamplesWithAnchorResponse) -> Void` as a class.
13
13
  * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`.
@@ -5,9 +5,9 @@
5
5
  /// Copyright © 2025 Marc Rousavy @ Margelo
6
6
  ///
7
7
 
8
+ import Foundation
8
9
  import NitroModules
9
10
 
10
-
11
11
  /**
12
12
  * Wraps a Swift `(_ value: FitzpatrickSkinType) -> Void` as a class.
13
13
  * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`.