@kingstinct/react-native-healthkit 13.2.3 → 13.3.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.
@@ -59,18 +59,20 @@ async throws -> [HKQuantityType: HKUnit] {
59
59
  return try await withCheckedThrowingContinuation { continuation in
60
60
  store.preferredUnits(for: Set(quantityTypes)) {
61
61
  (typePerUnits: [HKQuantityType: HKUnit], error: Error?) in
62
- if let error = error {
63
- return continuation.resume(throwing: error)
64
- }
62
+ DispatchQueue.main.async {
63
+ if let error = error {
64
+ return continuation.resume(throwing: error)
65
+ }
65
66
 
66
- // Thread-safe write: barrier ensures exclusive access
67
- quantityTypeCacheQueue.sync(flags: .barrier) {
68
- typePerUnits.forEach { (type: HKQuantityType, unit: HKUnit) in
69
- quantityTypeUnitCache.updateValue(unit, forKey: type)
67
+ // Thread-safe write: barrier ensures exclusive access
68
+ quantityTypeCacheQueue.sync(flags: .barrier) {
69
+ typePerUnits.forEach { (type: HKQuantityType, unit: HKUnit) in
70
+ quantityTypeUnitCache.updateValue(unit, forKey: type)
71
+ }
70
72
  }
71
- }
72
73
 
73
- return continuation.resume(returning: typePerUnits)
74
+ return continuation.resume(returning: typePerUnits)
75
+ }
74
76
  }
75
77
  }
76
78
  }
@@ -157,17 +159,18 @@ class CoreModule: HybridCoreModuleSpec {
157
159
  }
158
160
  return store.getRequestStatusForAuthorization(toShare: toShare, read: toRead) {
159
161
  status, error in
160
- if let error = error {
161
- continuation.resume(throwing: error)
162
- } else {
163
- if let authStatus = AuthorizationRequestStatus(rawValue: Int32(status.rawValue)) {
164
- continuation.resume(returning: authStatus)
162
+ DispatchQueue.main.async {
163
+ if let error = error {
164
+ continuation.resume(throwing: error)
165
165
  } else {
166
- continuation.resume(
167
- throwing: runtimeErrorWithPrefix(
168
- "Unrecognized authStatus returned: \(status.rawValue)"))
166
+ if let authStatus = AuthorizationRequestStatus(rawValue: Int32(status.rawValue)) {
167
+ continuation.resume(returning: authStatus)
168
+ } else {
169
+ continuation.resume(
170
+ throwing: runtimeErrorWithPrefix(
171
+ "Unrecognized authStatus returned: \(status.rawValue)"))
172
+ }
169
173
  }
170
-
171
174
  }
172
175
  }
173
176
  }
@@ -181,10 +184,12 @@ class CoreModule: HybridCoreModuleSpec {
181
184
 
182
185
  return try await withCheckedThrowingContinuation { continuation in
183
186
  store.requestAuthorization(toShare: share, read: toRead) { status, error in
184
- if let error = error {
185
- continuation.resume(throwing: error)
186
- } else {
187
- continuation.resume(returning: status)
187
+ DispatchQueue.main.async {
188
+ if let error = error {
189
+ continuation.resume(throwing: error)
190
+ } else {
191
+ continuation.resume(returning: status)
192
+ }
188
193
  }
189
194
  }
190
195
  }
@@ -203,25 +208,27 @@ class CoreModule: HybridCoreModuleSpec {
203
208
  sampleType: sampleType,
204
209
  samplePredicate: predicate
205
210
  ) { (_: HKSourceQuery, sources: Set<HKSource>?, error: Error?) in
206
- if let error = error {
207
- continuation.resume(throwing: error)
208
- return
209
- }
211
+ DispatchQueue.main.async {
212
+ if let error = error {
213
+ continuation.resume(throwing: error)
214
+ return
215
+ }
210
216
 
211
- guard let sources = sources else {
212
- return continuation.resume(
213
- throwing: runtimeErrorWithPrefix(
214
- "Empty response for sample type \(identifier.stringValue)"))
215
- }
217
+ guard let sources = sources else {
218
+ return continuation.resume(
219
+ throwing: runtimeErrorWithPrefix(
220
+ "Empty response for sample type \(identifier.stringValue)"))
221
+ }
216
222
 
217
- let serializedSources = sources.map { source -> SourceProxy in
223
+ let serializedSources = sources.map { source -> SourceProxy in
218
224
 
219
- return SourceProxy(
220
- source: source
221
- )
222
- }
225
+ return SourceProxy(
226
+ source: source
227
+ )
228
+ }
223
229
 
224
- continuation.resume(returning: serializedSources)
230
+ continuation.resume(returning: serializedSources)
231
+ }
225
232
  }
226
233
 
227
234
  store.execute(query)
@@ -240,10 +247,12 @@ class CoreModule: HybridCoreModuleSpec {
240
247
  for: type,
241
248
  frequency: frequency
242
249
  ) { (success, error) in
243
- if let err = error {
244
- return continuation.resume(throwing: err)
250
+ DispatchQueue.main.async {
251
+ if let err = error {
252
+ return continuation.resume(throwing: err)
253
+ }
254
+ return continuation.resume(returning: success)
245
255
  }
246
- return continuation.resume(returning: success)
247
256
  }
248
257
  }
249
258
  } else {
@@ -262,10 +271,12 @@ class CoreModule: HybridCoreModuleSpec {
262
271
  store.disableBackgroundDelivery(
263
272
  for: type
264
273
  ) { (success, error) in
265
- if let err = error {
266
- return continuation.resume(throwing: err)
274
+ DispatchQueue.main.async {
275
+ if let err = error {
276
+ return continuation.resume(throwing: err)
277
+ }
278
+ return continuation.resume(returning: success)
267
279
  }
268
- return continuation.resume(returning: success)
269
280
  }
270
281
  }
271
282
  }
@@ -275,10 +286,12 @@ class CoreModule: HybridCoreModuleSpec {
275
286
  return Promise.async {
276
287
  try await withCheckedThrowingContinuation { continuation in
277
288
  store.disableAllBackgroundDelivery(completion: { (success, error) in
278
- guard let err = error else {
279
- return continuation.resume(returning: success)
289
+ DispatchQueue.main.async {
290
+ guard let err = error else {
291
+ return continuation.resume(returning: success)
292
+ }
293
+ return continuation.resume(throwing: err)
280
294
  }
281
- return continuation.resume(throwing: err)
282
295
  })
283
296
  }
284
297
  }
@@ -345,10 +358,12 @@ class CoreModule: HybridCoreModuleSpec {
345
358
  let of = try sampleTypeFrom(sampleTypeIdentifierWriteable: objectTypeIdentifier)
346
359
  return try await withCheckedThrowingContinuation { continuation in
347
360
  store.deleteObjects(of: of, predicate: predicate) { (_, count, error) in
348
- if let error = error {
349
- continuation.resume(throwing: error)
350
- } else {
351
- continuation.resume(returning: Double(count))
361
+ DispatchQueue.main.async {
362
+ if let error = error {
363
+ continuation.resume(throwing: error)
364
+ } else {
365
+ continuation.resume(returning: Double(count))
366
+ }
352
367
  }
353
368
  }
354
369
  }
@@ -84,26 +84,28 @@ func getECGVoltages(sample: HKElectrocardiogram) async throws -> [Electrocardiog
84
84
 
85
85
  // Stream measurements
86
86
  let q = HKElectrocardiogramQuery(sample) { _, result in
87
- switch result {
88
- case .error(let error):
89
- continuation.resume(throwing: error)
90
-
91
- case .measurement(let m):
92
- // Lead I-like from Apple Watch
93
- if let v = m.quantity(for: .appleWatchSimilarToLeadI)?.doubleValue(for: .volt()) {
94
- let item = ElectrocardiogramVoltage(
95
- timeSinceSampleStart: m.timeSinceSampleStart,
96
- voltage: v,
97
- lead: ElectrocardiogramLead(fromString: "appleWatchSimilarToLeadI")!
98
- )
99
- all.append(item)
100
- }
87
+ DispatchQueue.main.async {
88
+ switch result {
89
+ case .error(let error):
90
+ continuation.resume(throwing: error)
91
+
92
+ case .measurement(let m):
93
+ // Lead I-like from Apple Watch
94
+ if let v = m.quantity(for: .appleWatchSimilarToLeadI)?.doubleValue(for: .volt()) {
95
+ let item = ElectrocardiogramVoltage(
96
+ timeSinceSampleStart: m.timeSinceSampleStart,
97
+ voltage: v,
98
+ lead: ElectrocardiogramLead(fromString: "appleWatchSimilarToLeadI")!
99
+ )
100
+ all.append(item)
101
+ }
101
102
 
102
- case .done:
103
- continuation.resume(returning: all)
104
- @unknown default:
105
- continuation.resume(
106
- throwing: runtimeErrorWithPrefix("HKElectrocardiogramQuery received unknown result type"))
103
+ case .done:
104
+ continuation.resume(returning: all)
105
+ @unknown default:
106
+ continuation.resume(
107
+ throwing: runtimeErrorWithPrefix("HKElectrocardiogramQuery received unknown result type"))
108
+ }
107
109
  }
108
110
  }
109
111
  store.execute(q)
@@ -60,20 +60,22 @@ func getHeartbeatSeriesHeartbeats(sample: HKHeartbeatSeriesSample) async throws
60
60
  done: Bool, error: Error?
61
61
  ) in
62
62
 
63
- if let error = error {
64
- continuation.resume(throwing: error)
65
- return
66
- }
67
-
68
- let heartbeat = Heartbeat(
69
- timeSinceSeriesStart: timeSinceSeriesStart,
70
- precededByGap: precededByGap
71
- )
72
-
73
- allBeats.append(heartbeat)
74
-
75
- if done {
76
- continuation.resume(returning: allBeats)
63
+ DispatchQueue.main.async {
64
+ if let error = error {
65
+ continuation.resume(throwing: error)
66
+ return
67
+ }
68
+
69
+ let heartbeat = Heartbeat(
70
+ timeSinceSeriesStart: timeSinceSeriesStart,
71
+ precededByGap: precededByGap
72
+ )
73
+
74
+ allBeats.append(heartbeat)
75
+
76
+ if done {
77
+ continuation.resume(returning: allBeats)
78
+ }
77
79
  }
78
80
  }
79
81
 
package/ios/Helpers.swift CHANGED
@@ -54,25 +54,27 @@ func sampleAnchoredQueryAsync(
54
54
  newAnchor:
55
55
  HKQueryAnchor?, error: Error?
56
56
  ) in
57
- if let error = error {
58
- return continuation.resume(throwing: error)
59
- }
57
+ DispatchQueue.main.async {
58
+ if let error = error {
59
+ return continuation.resume(throwing: error)
60
+ }
61
+
62
+ if let samples = samples, let deletedSamples = deletedSamples,
63
+ let newAnchor = serializeAnchor(anchor: newAnchor) {
64
+ return continuation.resume(
65
+ returning: AnchoredQueryResponse(
66
+ samples: samples,
67
+ deletedSamples: deletedSamples.map({ deletedSample in
68
+ return serializeDeletedSample(sample: deletedSample)
69
+ }),
70
+ newAnchor: newAnchor
71
+ )
72
+ )
73
+ }
60
74
 
61
- if let samples = samples, let deletedSamples = deletedSamples,
62
- let newAnchor = serializeAnchor(anchor: newAnchor) {
63
75
  return continuation.resume(
64
- returning: AnchoredQueryResponse(
65
- samples: samples,
66
- deletedSamples: deletedSamples.map({ deletedSample in
67
- return serializeDeletedSample(sample: deletedSample)
68
- }),
69
- newAnchor: newAnchor
70
- )
71
- )
76
+ throwing: runtimeErrorWithPrefix("Unexpected empty response"))
72
77
  }
73
-
74
- return continuation.resume(
75
- throwing: runtimeErrorWithPrefix("Unexpected empty response"))
76
78
  }
77
79
 
78
80
  store.execute(query)
@@ -108,16 +110,18 @@ func sampleQueryAsync(
108
110
  limit: limit,
109
111
  sortDescriptors: sortDescriptors,
110
112
  ) { (_: HKSampleQuery, samples: [HKSample]?, error: Error?) in
111
- if let error = error {
112
- return continuation.resume(throwing: error)
113
- }
113
+ DispatchQueue.main.async {
114
+ if let error = error {
115
+ return continuation.resume(throwing: error)
116
+ }
114
117
 
115
- if let samples = samples {
116
- return continuation.resume(returning: samples)
117
- }
118
+ if let samples = samples {
119
+ return continuation.resume(returning: samples)
120
+ }
118
121
 
119
- return continuation.resume(
120
- throwing: runtimeErrorWithPrefix("Unexpected empty response"))
122
+ return continuation.resume(
123
+ throwing: runtimeErrorWithPrefix("Unexpected empty response"))
124
+ }
121
125
  }
122
126
 
123
127
  store.execute(q)
@@ -127,10 +131,12 @@ func sampleQueryAsync(
127
131
  func saveAsync(sample: HKObject) async throws -> Bool {
128
132
  return try await withCheckedThrowingContinuation { continuation in
129
133
  store.save(sample) { (success: Bool, error: Error?) in
130
- if let error = error {
131
- continuation.resume(throwing: error)
132
- } else {
133
- continuation.resume(returning: success)
134
+ DispatchQueue.main.async {
135
+ if let error = error {
136
+ continuation.resume(throwing: error)
137
+ } else {
138
+ continuation.resume(returning: success)
139
+ }
134
140
  }
135
141
  }
136
142
  }
@@ -163,17 +163,19 @@ func queryStatisticsCollectionForQuantityInternal(
163
163
  )
164
164
 
165
165
  query.initialResultsHandler = { (_, results: HKStatisticsCollection?, error: Error?) in
166
- if let error = error {
167
- return handleHKNoDataOrThrow(error: error, continuation: continuation, noDataFallback: {
168
- return nil
169
- })
170
- }
166
+ DispatchQueue.main.async {
167
+ if let error = error {
168
+ return handleHKNoDataOrThrow(error: error, continuation: continuation, noDataFallback: {
169
+ return nil
170
+ })
171
+ }
171
172
 
172
- guard let statistics = results else {
173
- return continuation.resume(throwing: runtimeErrorWithPrefix("queryStatisticsCollectionForQuantityInternal: unexpected empty results"))
174
- }
173
+ guard let statistics = results else {
174
+ return continuation.resume(throwing: runtimeErrorWithPrefix("queryStatisticsCollectionForQuantityInternal: unexpected empty results"))
175
+ }
175
176
 
176
- return continuation.resume(returning: statistics)
177
+ return continuation.resume(returning: statistics)
178
+ }
177
179
  }
178
180
 
179
181
  store.execute(query)
@@ -57,21 +57,23 @@ func getRouteLocations(
57
57
  let query = HKWorkoutRouteQuery(route: route) {
58
58
  (_, locationsOrNil, done, errorOrNil) in
59
59
 
60
- if let error = errorOrNil {
61
- continuation.resume(throwing: error)
62
- return
63
- }
60
+ DispatchQueue.main.async {
61
+ if let error = errorOrNil {
62
+ continuation.resume(throwing: error)
63
+ return
64
+ }
64
65
 
65
- guard let currentLocationBatch = locationsOrNil else {
66
- return continuation.resume(
67
- throwing: runtimeErrorWithPrefix("Unexpected empty response")
68
- )
69
- }
66
+ guard let currentLocationBatch = locationsOrNil else {
67
+ return continuation.resume(
68
+ throwing: runtimeErrorWithPrefix("Unexpected empty response")
69
+ )
70
+ }
70
71
 
71
- allLocations.append(contentsOf: currentLocationBatch)
72
+ allLocations.append(contentsOf: currentLocationBatch)
72
73
 
73
- if done {
74
- continuation.resume(returning: allLocations)
74
+ if done {
75
+ continuation.resume(returning: allLocations)
76
+ }
75
77
  }
76
78
  }
77
79
 
@@ -43,13 +43,15 @@ class WorkoutsModule: HybridWorkoutsModuleSpec {
43
43
  return Promise.async {
44
44
  try await withCheckedThrowingContinuation { continuation in
45
45
  store.startWatchApp(with: configuration) { success, error in
46
- if let error {
47
- continuation.resume(
48
- throwing: error
49
- )
46
+ DispatchQueue.main.async {
47
+ if let error {
48
+ continuation.resume(
49
+ throwing: error
50
+ )
51
+ } else {
52
+ continuation.resume(returning: success)
53
+ }
50
54
  }
51
-
52
- continuation.resume(returning: success)
53
55
  }
54
56
  }
55
57
  }
@@ -192,19 +194,22 @@ class WorkoutsModule: HybridWorkoutsModuleSpec {
192
194
  try await withCheckedThrowingContinuation({ continuation in
193
195
  // saving workout, samples and route
194
196
  store.save(workout) { (_: Bool, error: Error?) in
195
- if let error = error {
196
- return continuation.resume(throwing: error)
197
- } else if !initializedSamples.isEmpty {
198
- store.add(initializedSamples, to: workout) { (_, error: Error?) in
199
- if let error = error {
200
- return continuation.resume(throwing: error)
197
+ DispatchQueue.main.async {
198
+ if let error = error {
199
+ return continuation.resume(throwing: error)
200
+ } else if !initializedSamples.isEmpty {
201
+ store.add(initializedSamples, to: workout) { (_, error: Error?) in
202
+ DispatchQueue.main.async {
203
+ if let error = error {
204
+ return continuation.resume(throwing: error)
205
+ }
206
+ return continuation.resume()
207
+ }
201
208
  }
209
+ } else {
202
210
  return continuation.resume()
203
211
  }
204
- } else {
205
- return continuation.resume()
206
212
  }
207
-
208
213
  }
209
214
  }) as Void
210
215
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kingstinct/react-native-healthkit",
3
- "version": "13.2.3",
3
+ "version": "13.3.1",
4
4
  "description": "React Native bindings for HealthKit",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -91,15 +91,15 @@
91
91
  "devDependencies": {
92
92
  "@testing-library/react-native": "^13.3.3",
93
93
  "@types/react": "~19.1.17",
94
- "nitrogen": "^0.33.9",
94
+ "nitrogen": "^0.35.0",
95
95
  "react": "19.1.0",
96
96
  "react-native": "0.81.5",
97
- "react-native-nitro-modules": "^0.33.9",
97
+ "react-native-nitro-modules": "^0.35.0",
98
98
  "typescript": "~5.9.3"
99
99
  },
100
100
  "peerDependencies": {
101
101
  "react": ">=19",
102
102
  "react-native": ">=0.79",
103
- "react-native-nitro-modules": ">=0.33"
103
+ "react-native-nitro-modules": ">=0.35"
104
104
  }
105
105
  }