@kingstinct/react-native-healthkit 13.3.0 → 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.
- package/ios/CoreModule.swift +65 -50
- package/ios/ElectrocardiogramModule.swift +21 -19
- package/ios/HeartbeatSeriesModule.swift +16 -14
- package/ios/Helpers.swift +34 -28
- package/ios/QuantityTypeModule.swift +11 -9
- package/ios/WorkoutProxy.swift +14 -12
- package/ios/WorkoutsModule.swift +20 -15
- package/package.json +1 -1
package/ios/CoreModule.swift
CHANGED
|
@@ -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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
DispatchQueue.main.async {
|
|
63
|
+
if let error = error {
|
|
64
|
+
return continuation.resume(throwing: error)
|
|
65
|
+
}
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
211
|
+
DispatchQueue.main.async {
|
|
212
|
+
if let error = error {
|
|
213
|
+
continuation.resume(throwing: error)
|
|
214
|
+
return
|
|
215
|
+
}
|
|
210
216
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
-
|
|
223
|
+
let serializedSources = sources.map { source -> SourceProxy in
|
|
218
224
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
225
|
+
return SourceProxy(
|
|
226
|
+
source: source
|
|
227
|
+
)
|
|
228
|
+
}
|
|
223
229
|
|
|
224
|
-
|
|
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
|
-
|
|
244
|
-
|
|
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
|
-
|
|
266
|
-
|
|
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
|
-
|
|
279
|
-
|
|
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
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
let
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
58
|
-
|
|
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
|
-
|
|
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
|
-
|
|
112
|
-
|
|
113
|
-
|
|
113
|
+
DispatchQueue.main.async {
|
|
114
|
+
if let error = error {
|
|
115
|
+
return continuation.resume(throwing: error)
|
|
116
|
+
}
|
|
114
117
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
+
if let samples = samples {
|
|
119
|
+
return continuation.resume(returning: samples)
|
|
120
|
+
}
|
|
118
121
|
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
return
|
|
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
|
-
|
|
173
|
-
|
|
174
|
-
|
|
173
|
+
guard let statistics = results else {
|
|
174
|
+
return continuation.resume(throwing: runtimeErrorWithPrefix("queryStatisticsCollectionForQuantityInternal: unexpected empty results"))
|
|
175
|
+
}
|
|
175
176
|
|
|
176
|
-
|
|
177
|
+
return continuation.resume(returning: statistics)
|
|
178
|
+
}
|
|
177
179
|
}
|
|
178
180
|
|
|
179
181
|
store.execute(query)
|
package/ios/WorkoutProxy.swift
CHANGED
|
@@ -57,21 +57,23 @@ func getRouteLocations(
|
|
|
57
57
|
let query = HKWorkoutRouteQuery(route: route) {
|
|
58
58
|
(_, locationsOrNil, done, errorOrNil) in
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
DispatchQueue.main.async {
|
|
61
|
+
if let error = errorOrNil {
|
|
62
|
+
continuation.resume(throwing: error)
|
|
63
|
+
return
|
|
64
|
+
}
|
|
64
65
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
guard let currentLocationBatch = locationsOrNil else {
|
|
67
|
+
return continuation.resume(
|
|
68
|
+
throwing: runtimeErrorWithPrefix("Unexpected empty response")
|
|
69
|
+
)
|
|
70
|
+
}
|
|
70
71
|
|
|
71
|
-
|
|
72
|
+
allLocations.append(contentsOf: currentLocationBatch)
|
|
72
73
|
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
if done {
|
|
75
|
+
continuation.resume(returning: allLocations)
|
|
76
|
+
}
|
|
75
77
|
}
|
|
76
78
|
}
|
|
77
79
|
|
package/ios/WorkoutsModule.swift
CHANGED
|
@@ -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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
|