@kingstinct/react-native-healthkit 8.1.1 → 8.2.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.
@@ -8,747 +8,964 @@ import WorkoutKit
8
8
  @objc(ReactNativeHealthkit)
9
9
  @available(iOS 10.0, *)
10
10
  class ReactNativeHealthkit: RCTEventEmitter {
11
- var _store: HKHealthStore?
12
- var _runningQueries: [String: HKQuery]
13
- var _dateFormatter: ISO8601DateFormatter
14
- var _hasListeners = false
15
-
16
- override init() {
17
- self._runningQueries = [String: HKQuery]()
18
- self._dateFormatter = ISO8601DateFormatter()
19
- self._dateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
20
- if HKHealthStore.isHealthDataAvailable() {
21
- self._store = HKHealthStore.init()
22
- }
23
- super.init()
11
+ var _store: HKHealthStore?
12
+ var _runningQueries: [String: HKQuery]
13
+ var _dateFormatter: ISO8601DateFormatter
14
+ var _hasListeners = false
15
+
16
+ override init() {
17
+ self._runningQueries = [String: HKQuery]()
18
+ self._dateFormatter = ISO8601DateFormatter()
19
+ self._dateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
20
+ if HKHealthStore.isHealthDataAvailable() {
21
+ self._store = HKHealthStore.init()
24
22
  }
23
+ super.init()
24
+ }
25
25
 
26
- deinit {
27
- if let store = _store {
28
- for query in self._runningQueries {
29
- store.stop(query.value)
30
- }
31
- }
26
+ deinit {
27
+ if let store = _store {
28
+ for query in self._runningQueries {
29
+ store.stop(query.value)
30
+ }
32
31
  }
32
+ }
33
33
 
34
- override func stopObserving() {
35
- self._hasListeners = false
36
- if let store = _store {
37
- for query in self._runningQueries {
38
- store.stop(query.value)
39
- }
40
- }
34
+ override func stopObserving() {
35
+ self._hasListeners = false
36
+ if let store = _store {
37
+ for query in self._runningQueries {
38
+ store.stop(query.value)
39
+ }
41
40
  }
41
+ }
42
42
 
43
- override func startObserving() {
44
- self._hasListeners = true
45
- }
43
+ override func startObserving() {
44
+ self._hasListeners = true
45
+ }
46
+
47
+ @objc(isProtectedDataAvailable:withRejecter:)
48
+ func isProtectedDataAvailable(
49
+ resolve: RCTPromiseResolveBlock,
50
+ reject: RCTPromiseRejectBlock
51
+ ) {
52
+ resolve(UIApplication.shared.isProtectedDataAvailable)
53
+ }
54
+
55
+ @objc(isHealthDataAvailable:withRejecter:)
56
+ func isHealthDataAvailable(
57
+ resolve: RCTPromiseResolveBlock,
58
+ reject: RCTPromiseRejectBlock
59
+ ) {
60
+ resolve(HKHealthStore.isHealthDataAvailable())
61
+ }
46
62
 
47
- @objc(isProtectedDataAvailable:withRejecter:)
48
- func isProtectedDataAvailable(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
49
- resolve(UIApplication.shared.isProtectedDataAvailable)
63
+ @available(iOS 12.0, *)
64
+ @objc(supportsHealthRecords:withRejecter:)
65
+ func supportsHealthRecords(
66
+ resolve: RCTPromiseResolveBlock,
67
+ reject: RCTPromiseRejectBlock
68
+ ) {
69
+ guard let store = _store else {
70
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
50
71
  }
72
+ resolve(store.supportsHealthRecords())
73
+ }
51
74
 
52
- @objc(isHealthDataAvailable:withRejecter:)
53
- func isHealthDataAvailable(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
54
- resolve(HKHealthStore.isHealthDataAvailable())
75
+ @available(iOS 12.0, *)
76
+ @objc(getRequestStatusForAuthorization:read:resolve:withRejecter:)
77
+ func getRequestStatusForAuthorization(
78
+ toShare: NSDictionary,
79
+ read: NSDictionary,
80
+ resolve: @escaping RCTPromiseResolveBlock,
81
+ reject: @escaping RCTPromiseRejectBlock
82
+ ) {
83
+ guard let store = _store else {
84
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
55
85
  }
56
86
 
57
- @available(iOS 12.0, *)
58
- @objc(supportsHealthRecords:withRejecter:)
59
- func supportsHealthRecords(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
60
- guard let store = _store else {
61
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
62
- }
63
- resolve(store.supportsHealthRecords())
87
+ let share = sampleTypesFromDictionary(typeIdentifiers: toShare)
88
+ let toRead = objectTypesFromDictionary(typeIdentifiers: read)
89
+
90
+ store.getRequestStatusForAuthorization(toShare: share, read: toRead) { (
91
+ status: HKAuthorizationRequestStatus,
92
+ error: Error?
93
+ ) in
94
+ guard let err = error else {
95
+ return resolve(status.rawValue)
96
+ }
97
+ reject(GENERIC_ERROR, err.localizedDescription, err)
64
98
  }
99
+ }
65
100
 
66
- @available(iOS 12.0, *)
67
- @objc(getRequestStatusForAuthorization:read:resolve:withRejecter:)
68
- func getRequestStatusForAuthorization(toShare: NSDictionary, read: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
69
- guard let store = _store else {
70
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
71
- }
72
- let share = sampleTypesFromDictionary(typeIdentifiers: toShare)
73
- let toRead = objectTypesFromDictionary(typeIdentifiers: read)
74
- store.getRequestStatusForAuthorization(toShare: share, read: toRead) { (status: HKAuthorizationRequestStatus, error: Error?) in
75
- guard let err = error else {
76
- return resolve(status.rawValue)
77
- }
78
- reject(GENERIC_ERROR, err.localizedDescription, err)
79
- }
101
+ @objc(getPreferredUnits:resolve:reject:)
102
+ func getPreferredUnits(
103
+ forIdentifiers: NSArray,
104
+ resolve: @escaping RCTPromiseResolveBlock,
105
+ reject: @escaping RCTPromiseRejectBlock
106
+ ) {
107
+ guard let store = _store else {
108
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
109
+ }
110
+ var quantityTypes = Set<HKQuantityType>()
111
+ for identifierString in forIdentifiers {
112
+ let identifier = HKQuantityTypeIdentifier.init(rawValue: identifierString as! String)
113
+ let type = HKSampleType.quantityType(forIdentifier: identifier)
114
+ if type != nil {
115
+ quantityTypes.insert(type!)
116
+ }
80
117
  }
81
118
 
82
- @objc(getPreferredUnits:resolve:reject:)
83
- func getPreferredUnits(forIdentifiers: NSArray, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
84
- guard let store = _store else {
85
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
86
- }
87
- var quantityTypes = Set<HKQuantityType>()
88
- for identifierString in forIdentifiers {
89
- let identifier = HKQuantityTypeIdentifier.init(rawValue: identifierString as! String)
90
- let type = HKSampleType.quantityType(forIdentifier: identifier)
91
- if type != nil {
92
- quantityTypes.insert(type!)
93
- }
94
- }
119
+ store.preferredUnits(for: quantityTypes) { (typePerUnits: [HKQuantityType: HKUnit], _: Error?) in
120
+ let dic: NSMutableDictionary = NSMutableDictionary()
95
121
 
96
- store.preferredUnits(for: quantityTypes) { (typePerUnits: [HKQuantityType: HKUnit], _: Error?) in
97
- let dic: NSMutableDictionary = NSMutableDictionary()
122
+ for typePerUnit in typePerUnits {
123
+ dic.setObject(typePerUnit.value.unitString, forKey: typePerUnit.key.identifier as NSCopying)
124
+ }
98
125
 
99
- for typePerUnit in typePerUnits {
100
- dic.setObject(typePerUnit.value.unitString, forKey: typePerUnit.key.identifier as NSCopying)
101
- }
126
+ resolve(dic)
127
+ }
128
+ }
102
129
 
103
- resolve(dic)
104
- }
130
+ @objc(getBiologicalSex:withRejecter:)
131
+ func getBiologicalSex(
132
+ resolve: @escaping RCTPromiseResolveBlock,
133
+ reject: @escaping RCTPromiseRejectBlock
134
+ ) {
135
+ guard let store = _store else {
136
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
105
137
  }
106
138
 
107
- @objc(getBiologicalSex:withRejecter:)
108
- func getBiologicalSex(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
109
- guard let store = _store else {
110
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
111
- }
139
+ do {
140
+ let bioSex = try store.biologicalSex()
141
+ resolve(bioSex.biologicalSex.rawValue)
142
+ } catch {
143
+ reject(GENERIC_ERROR, error.localizedDescription, error)
144
+ }
145
+ }
112
146
 
113
- do {
114
- let bioSex = try store.biologicalSex()
115
- resolve(bioSex.biologicalSex.rawValue)
116
- } catch {
117
- reject(GENERIC_ERROR, error.localizedDescription, error)
118
- }
147
+ @objc(getDateOfBirth:withRejecter:)
148
+ func getDateOfBirth(
149
+ resolve: @escaping RCTPromiseResolveBlock,
150
+ reject: @escaping RCTPromiseRejectBlock
151
+ ) {
152
+ guard let store = _store else {
153
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
119
154
  }
120
155
 
121
- @objc(getDateOfBirth:withRejecter:)
122
- func getDateOfBirth(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
123
- guard let store = _store else {
124
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
125
- }
156
+ do {
157
+ let dateOfBirth = try store.dateOfBirthComponents()
126
158
 
127
- do {
128
- let dateOfBirth = try store.dateOfBirthComponents()
159
+ resolve(_dateFormatter.string(from: dateOfBirth.date!))
160
+ } catch {
161
+ reject(GENERIC_ERROR, error.localizedDescription, error)
162
+ }
163
+ }
129
164
 
130
- resolve(_dateFormatter.string(from: dateOfBirth.date!))
131
- } catch {
132
- reject(GENERIC_ERROR, error.localizedDescription, error)
133
- }
165
+ @objc(getBloodType:withRejecter:)
166
+ func getBloodType(
167
+ resolve: @escaping RCTPromiseResolveBlock,
168
+ reject: @escaping RCTPromiseRejectBlock
169
+ ) {
170
+ guard let store = _store else {
171
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
134
172
  }
135
173
 
136
- @objc(getBloodType:withRejecter:)
137
- func getBloodType(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
138
- guard let store = _store else {
139
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
140
- }
174
+ do {
175
+ let bloodType = try store.bloodType()
176
+ resolve(bloodType.bloodType.rawValue)
177
+ } catch {
178
+ reject(GENERIC_ERROR, error.localizedDescription, error)
179
+ }
180
+ }
141
181
 
142
- do {
143
- let bloodType = try store.bloodType()
144
- resolve(bloodType.bloodType.rawValue)
145
- } catch {
146
- reject(GENERIC_ERROR, error.localizedDescription, error)
147
- }
182
+ @objc(getFitzpatrickSkinType:withRejecter:)
183
+ func getFitzpatrickSkinType(
184
+ resolve: @escaping RCTPromiseResolveBlock,
185
+ reject: @escaping RCTPromiseRejectBlock
186
+ ) {
187
+ guard let store = _store else {
188
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
148
189
  }
149
190
 
150
- @objc(getFitzpatrickSkinType:withRejecter:)
151
- func getFitzpatrickSkinType(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
152
- guard let store = _store else {
153
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
154
- }
191
+ do {
192
+ let fitzpatrickSkinType = try store.fitzpatrickSkinType()
193
+ resolve(fitzpatrickSkinType.skinType.rawValue)
194
+ } catch {
195
+ reject(GENERIC_ERROR, error.localizedDescription, error)
196
+ }
197
+ }
155
198
 
156
- do {
157
- let fitzpatrickSkinType = try store.fitzpatrickSkinType()
158
- resolve(fitzpatrickSkinType.skinType.rawValue)
159
- } catch {
160
- reject(GENERIC_ERROR, error.localizedDescription, error)
161
- }
199
+ @available(iOS 10.0, *)
200
+ @objc(getWheelchairUse:withRejecter:)
201
+ func getWheelchairUse(
202
+ resolve: @escaping RCTPromiseResolveBlock,
203
+ reject: @escaping RCTPromiseRejectBlock
204
+ ) {
205
+ guard let store = _store else {
206
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
162
207
  }
163
208
 
164
- @available(iOS 10.0, *)
165
- @objc(getWheelchairUse:withRejecter:)
166
- func getWheelchairUse(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
167
- guard let store = _store else {
168
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
169
- }
209
+ do {
210
+ let wheelchairUse = try store.wheelchairUse()
211
+ resolve(wheelchairUse.wheelchairUse.rawValue)
212
+ } catch {
213
+ reject(GENERIC_ERROR, error.localizedDescription, error)
214
+ }
215
+ }
170
216
 
171
- do {
172
- let wheelchairUse = try store.wheelchairUse()
173
- resolve(wheelchairUse.wheelchairUse.rawValue)
174
- } catch {
175
- reject(GENERIC_ERROR, error.localizedDescription, error)
176
- }
217
+ @objc(authorizationStatusFor:withResolver:withRejecter:)
218
+ func authorizationStatusFor(
219
+ typeIdentifier: String,
220
+ resolve: @escaping RCTPromiseResolveBlock,
221
+ reject: @escaping RCTPromiseRejectBlock
222
+ ) {
223
+ guard let store = _store else {
224
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
177
225
  }
178
226
 
179
- @objc(authorizationStatusFor:withResolver:withRejecter:)
180
- func authorizationStatusFor(typeIdentifier: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
181
- guard let store = _store else {
182
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
183
- }
227
+ guard let objectType = objectTypeFromString(typeIdentifier: typeIdentifier) else {
228
+ return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
229
+ }
184
230
 
185
- guard let objectType = objectTypeFromString(typeIdentifier: typeIdentifier) else {
186
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
187
- }
231
+ let authStatus = store.authorizationStatus(for: objectType)
232
+ resolve(authStatus.rawValue)
233
+ }
188
234
 
189
- let authStatus = store.authorizationStatus(for: objectType)
190
- resolve(authStatus.rawValue)
235
+ @objc(saveQuantitySample:unitString:value:start:end:metadata:resolve:reject:)
236
+ func saveQuantitySample(
237
+ typeIdentifier: String,
238
+ unitString: String,
239
+ value: Double,
240
+ start: Date,
241
+ end: Date,
242
+ metadata: [String: Any],
243
+ resolve: @escaping RCTPromiseResolveBlock,
244
+ reject: @escaping RCTPromiseRejectBlock
245
+ ) {
246
+ guard let store = _store else {
247
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
191
248
  }
192
249
 
193
- @objc(saveQuantitySample:unitString:value:start:end:metadata:resolve:reject:)
194
- func saveQuantitySample(typeIdentifier: String, unitString: String, value: Double, start: Date, end: Date, metadata: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
195
- guard let store = _store else {
196
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
197
- }
250
+ let identifier = HKQuantityTypeIdentifier.init(rawValue: typeIdentifier)
198
251
 
199
- let identifier = HKQuantityTypeIdentifier.init(rawValue: typeIdentifier)
200
-
201
- guard let type = HKObjectType.quantityType(forIdentifier: identifier) else {
202
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
203
- }
252
+ guard let type = HKObjectType.quantityType(forIdentifier: identifier) else {
253
+ return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
254
+ }
204
255
 
205
- let unit = HKUnit.init(from: unitString)
206
- let quantity = HKQuantity.init(unit: unit, doubleValue: value)
207
- let sample = HKQuantitySample.init(
208
- type: type,
209
- quantity: quantity,
210
- start: start,
211
- end: end,
212
- metadata: metadata
213
- )
256
+ let unit = HKUnit.init(from: unitString)
257
+ let quantity = HKQuantity.init(unit: unit, doubleValue: value)
258
+ let sample = HKQuantitySample.init(
259
+ type: type,
260
+ quantity: quantity,
261
+ start: start,
262
+ end: end,
263
+ metadata: metadata
264
+ )
265
+
266
+ store.save(sample) { (success: Bool, error: Error?) in
267
+ guard let err = error else {
268
+ return resolve(success)
269
+ }
270
+ reject(GENERIC_ERROR, err.localizedDescription, error)
271
+ }
272
+ }
214
273
 
215
- store.save(sample) { (success: Bool, error: Error?) in
216
- guard let err = error else {
217
- return resolve(success)
218
- }
219
- reject(GENERIC_ERROR, err.localizedDescription, error)
220
- }
274
+ @objc(deleteQuantitySample:uuid:resolve:reject:)
275
+ func deleteQuantitySample(
276
+ typeIdentifier: String,
277
+ uuid: String,
278
+ resolve: @escaping RCTPromiseResolveBlock,
279
+ reject: @escaping RCTPromiseRejectBlock
280
+ ) {
281
+ guard let store = _store else {
282
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
221
283
  }
222
284
 
223
- @objc(deleteQuantitySample:uuid:resolve:reject:)
224
- func deleteQuantitySample(typeIdentifier: String, uuid: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
225
- guard let store = _store else {
226
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
227
- }
285
+ let identifier = HKQuantityTypeIdentifier.init(rawValue: typeIdentifier)
286
+ let sampleUuid = UUID.init(uuidString: uuid)!
228
287
 
229
- let identifier = HKQuantityTypeIdentifier.init(rawValue: typeIdentifier)
230
- let sampleUuid = UUID.init(uuidString: uuid)!
288
+ guard let sampleType = HKObjectType.quantityType(forIdentifier: identifier) else {
289
+ return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
290
+ }
231
291
 
232
- guard let sampleType = HKObjectType.quantityType(forIdentifier: identifier) else {
233
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
234
- }
292
+ let samplePredicate = HKQuery.predicateForObject(with: sampleUuid)
235
293
 
236
- let samplePredicate = HKQuery.predicateForObject(with: sampleUuid)
294
+ store.deleteObjects(of: sampleType, predicate: samplePredicate) { (success: Bool, _: Int, error: Error?) in
295
+ guard let err = error else {
296
+ return resolve(success)
297
+ }
298
+ reject(GENERIC_ERROR, err.localizedDescription, error)
299
+ }
300
+ }
237
301
 
238
- store.deleteObjects(of: sampleType, predicate: samplePredicate) { (success: Bool, _: Int, error: Error?) in
239
- guard let err = error else {
240
- return resolve(success)
241
- }
242
- reject(GENERIC_ERROR, err.localizedDescription, error)
243
- }
302
+ @objc(deleteSamples:start:end:resolve:reject:)
303
+ func deleteSamples(
304
+ typeIdentifier: String,
305
+ start: Date,
306
+ end: Date,
307
+ resolve: @escaping RCTPromiseResolveBlock,
308
+ reject: @escaping RCTPromiseRejectBlock
309
+ ) {
310
+ guard let store = _store else {
311
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
244
312
  }
245
313
 
246
- @objc(deleteSamples:start:end:resolve:reject:)
247
- func deleteSamples(typeIdentifier: String, start: Date, end: Date, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
248
- guard let store = _store else {
249
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
250
- }
314
+ let identifier = HKQuantityTypeIdentifier.init(rawValue: typeIdentifier)
251
315
 
252
- let identifier = HKQuantityTypeIdentifier.init(rawValue: typeIdentifier)
316
+ guard let sampleType = HKObjectType.quantityType(forIdentifier: identifier) else {
317
+ return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
318
+ }
253
319
 
254
- guard let sampleType = HKObjectType.quantityType(forIdentifier: identifier) else {
255
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
256
- }
320
+ let samplePredicate = HKQuery.predicateForSamples(
321
+ withStart: start,
322
+ end: end,
323
+ options: HKQueryOptions.strictStartDate
324
+ )
257
325
 
258
- let samplePredicate = HKQuery.predicateForSamples(withStart: start, end: end, options: HKQueryOptions.strictStartDate)
326
+ store.deleteObjects(of: sampleType, predicate: samplePredicate) { (success: Bool, _: Int, error: Error?) in
327
+ guard let err = error else {
328
+ return resolve(success)
329
+ }
330
+ reject(GENERIC_ERROR, err.localizedDescription, error)
331
+ }
332
+ }
259
333
 
260
- store.deleteObjects(of: sampleType, predicate: samplePredicate) { (success: Bool, _: Int, error: Error?) in
261
- guard let err = error else {
262
- return resolve(success)
263
- }
264
- reject(GENERIC_ERROR, err.localizedDescription, error)
265
- }
334
+ @objc(saveCorrelationSample:samples:start:end:metadata:resolve:reject:)
335
+ func saveCorrelationSample(
336
+ typeIdentifier: String,
337
+ samples: [[String: Any]],
338
+ start: Date,
339
+ end: Date,
340
+ metadata: [String: Any],
341
+ resolve: @escaping RCTPromiseResolveBlock,
342
+ reject: @escaping RCTPromiseRejectBlock
343
+ ) {
344
+ guard let store = _store else {
345
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
266
346
  }
267
347
 
268
- @objc(saveCorrelationSample:samples:start:end:metadata:resolve:reject:)
269
- func saveCorrelationSample(typeIdentifier: String, samples: [[String: Any]], start: Date, end: Date, metadata: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
270
- guard let store = _store else {
271
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
272
- }
348
+ let identifier = HKCorrelationTypeIdentifier.init(rawValue: typeIdentifier)
273
349
 
274
- let identifier = HKCorrelationTypeIdentifier.init(rawValue: typeIdentifier)
350
+ guard let type = HKObjectType.correlationType(forIdentifier: identifier) else {
351
+ return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
352
+ }
275
353
 
276
- guard let type = HKObjectType.correlationType(forIdentifier: identifier) else {
277
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
354
+ var initializedSamples = Set<HKSample>()
355
+ for sample in samples {
356
+ if sample.keys.contains("quantityType") {
357
+ let typeId = HKQuantityTypeIdentifier.init(rawValue: sample["quantityType"] as! String)
358
+ if let type = HKSampleType.quantityType(forIdentifier: typeId) {
359
+ let unitStr = sample["unit"] as! String
360
+ let quantityVal = sample["quantity"] as! Double
361
+ let metadata = sample["metadata"] as? [String: Any]
362
+
363
+ let unit = HKUnit.init(from: unitStr)
364
+ let quantity = HKQuantity.init(unit: unit, doubleValue: quantityVal)
365
+ let quantitySample = HKQuantitySample.init(
366
+ type: type,
367
+ quantity: quantity,
368
+ start: start,
369
+ end: end,
370
+ metadata: metadata
371
+ )
372
+ initializedSamples.insert(quantitySample)
373
+ }
374
+ } else if sample.keys.contains("categoryType") {
375
+ let typeId = HKCategoryTypeIdentifier.init(rawValue: sample["categoryType"] as! String)
376
+ if let type = HKSampleType.categoryType(forIdentifier: typeId) {
377
+ let value = sample["value"] as! Int
378
+ let metadata = sample["metadata"] as? [String: Any]
379
+ let categorySample = HKCategorySample.init(
380
+ type: type,
381
+ value: value,
382
+ start: start,
383
+ end: end,
384
+ metadata: metadata
385
+ )
386
+ initializedSamples.insert(categorySample)
278
387
  }
388
+ }
279
389
 
280
- var initializedSamples = Set<HKSample>()
281
- for sample in samples {
282
- if sample.keys.contains("quantityType") {
283
- let typeId = HKQuantityTypeIdentifier.init(rawValue: sample["quantityType"] as! String)
284
- if let type = HKSampleType.quantityType(forIdentifier: typeId) {
285
- let unitStr = sample["unit"] as! String
286
- let quantityVal = sample["quantity"] as! Double
287
- let metadata = sample["metadata"] as? [String: Any]
288
-
289
- let unit = HKUnit.init(from: unitStr)
290
- let quantity = HKQuantity.init(unit: unit, doubleValue: quantityVal)
291
- let quantitySample = HKQuantitySample.init(type: type, quantity: quantity, start: start, end: end, metadata: metadata)
292
- initializedSamples.insert(quantitySample)
293
- }
294
- } else if sample.keys.contains("categoryType") {
295
- let typeId = HKCategoryTypeIdentifier.init(rawValue: sample["categoryType"] as! String)
296
- if let type = HKSampleType.categoryType(forIdentifier: typeId) {
297
- let value = sample["value"] as! Int
298
- let metadata = sample["metadata"] as? [String: Any]
299
- let categorySample = HKCategorySample.init(type: type, value: value, start: start, end: end, metadata: metadata)
300
- initializedSamples.insert(categorySample)
301
- }
302
- }
303
-
304
- }
390
+ }
305
391
 
306
- let correlation = HKCorrelation.init(type: type, start: start, end: end, objects: initializedSamples, metadata: metadata)
392
+ let correlation = HKCorrelation.init(
393
+ type: type,
394
+ start: start,
395
+ end: end,
396
+ objects: initializedSamples,
397
+ metadata: metadata
398
+ )
307
399
 
308
- store.save(correlation) { (success: Bool, error: Error?) in
309
- guard let err = error else {
310
- return resolve(success)
311
- }
312
- reject(GENERIC_ERROR, err.localizedDescription, error)
313
- }
400
+ store.save(correlation) { (success: Bool, error: Error?) in
401
+ guard let err = error else {
402
+ return resolve(success)
403
+ }
404
+ reject(GENERIC_ERROR, err.localizedDescription, error)
314
405
  }
406
+ }
315
407
 
316
- @objc(saveWorkoutSample:quantities:start:end:totals:metadata:resolve:reject:)
317
- func saveWorkoutSample(typeIdentifier: UInt, quantities: [[String: Any]], start: Date, end: Date, totals: [String: Any], metadata: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
318
- guard let store = _store else {
319
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
320
- }
408
+ @objc(saveWorkoutSample:quantities:start:end:totals:metadata:resolve:reject:)
409
+ func saveWorkoutSample(
410
+ typeIdentifier: UInt,
411
+ quantities: [[String: Any]],
412
+ start: Date,
413
+ end: Date,
414
+ totals: [String: Any],
415
+ metadata: [String: Any],
416
+ resolve: @escaping RCTPromiseResolveBlock,
417
+ reject: @escaping RCTPromiseRejectBlock
418
+ ) {
419
+ guard let store = _store else {
420
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
421
+ }
321
422
 
322
- guard let type = HKWorkoutActivityType.init(rawValue: typeIdentifier) else {
323
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize HKWorkoutActivityType " + typeIdentifier.description, nil)
324
- }
423
+ guard let type = HKWorkoutActivityType.init(rawValue: typeIdentifier) else {
424
+ return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize HKWorkoutActivityType " + typeIdentifier.description, nil)
425
+ }
325
426
 
326
- // if start and end both exist, ensure that start date is before end date
327
- if let startDate = start as Date?, let endDate = end as Date? {
328
- if startDate > endDate {
329
- return reject(GENERIC_ERROR, "Start date must be before end date", nil)
330
- }
331
- }
427
+ // if start and end both exist, ensure that start date is before end date
428
+ if let startDate = start as Date?, let endDate = end as Date? {
429
+ if startDate > endDate {
430
+ return reject(GENERIC_ERROR, "Start date must be before end date", nil)
431
+ }
432
+ }
332
433
 
333
- var initializedSamples = [HKSample]()
334
- var totalEnergyBurned: HKQuantity?
335
- var totalDistance: HKQuantity?
336
- var totalSwimmingStrokeCount: HKQuantity?
337
- var totalFlightsClimbed: HKQuantity?
338
- // generating quantity samples
339
- for quantity in quantities {
340
- let typeId = HKQuantityTypeIdentifier.init(rawValue: quantity["quantityType"] as! String)
341
- if let type = HKSampleType.quantityType(forIdentifier: typeId) {
342
- let unitStr = quantity["unit"] as! String
343
- let quantityVal = quantity["quantity"] as! Double
344
- let metadata = quantity["metadata"] as? [String: Any]
345
- let quantityStart = quantity["startDate"] as? String
346
- let quantityEnd = quantity["endDate"] as? String
347
- let unit = HKUnit.init(from: unitStr)
348
- let quantity = HKQuantity.init(unit: unit, doubleValue: quantityVal)
349
-
350
- if quantity.is(compatibleWith: HKUnit.kilocalorie()) {
351
- totalEnergyBurned = quantity
352
- }
353
- if quantity.is(compatibleWith: HKUnit.meter()) {
354
- totalDistance = quantity
355
- }
356
- if typeId == HKQuantityTypeIdentifier.swimmingStrokeCount {
357
- totalSwimmingStrokeCount = quantity
358
- }
359
- if typeId == HKQuantityTypeIdentifier.flightsClimbed {
360
- totalFlightsClimbed = quantity
361
- }
362
- if let quantityStart, let quantityEnd {
363
- let quantityStartDate = self._dateFormatter.date(from: quantityStart) ?? start
364
- let quantityEndDate = self._dateFormatter.date(from: quantityEnd) ?? end
365
- let quantitySample = HKQuantitySample.init(type: type, quantity: quantity, start: quantityStartDate, end: quantityEndDate, metadata: metadata)
366
- initializedSamples.append(quantitySample)
367
- } else {
368
- // Handle the case where either startDate or endDate is nil
369
- let quantitySample = HKQuantitySample.init(type: type, quantity: quantity, start: start, end: end, metadata: metadata)
370
- initializedSamples.append(quantitySample)
371
- }
372
- }
434
+ var initializedSamples = [HKSample]()
435
+ var totalEnergyBurned: HKQuantity?
436
+ var totalDistance: HKQuantity?
437
+ var totalSwimmingStrokeCount: HKQuantity?
438
+ var totalFlightsClimbed: HKQuantity?
439
+ // generating quantity samples
440
+ for quantity in quantities {
441
+ let typeId = HKQuantityTypeIdentifier.init(rawValue: quantity["quantityType"] as! String)
442
+ if let type = HKSampleType.quantityType(forIdentifier: typeId) {
443
+ let unitStr = quantity["unit"] as! String
444
+ let quantityVal = quantity["quantity"] as! Double
445
+ let metadata = quantity["metadata"] as? [String: Any]
446
+ let quantityStart = quantity["startDate"] as? String
447
+ let quantityEnd = quantity["endDate"] as? String
448
+ let unit = HKUnit.init(from: unitStr)
449
+ let quantity = HKQuantity.init(unit: unit, doubleValue: quantityVal)
450
+
451
+ if quantity.is(compatibleWith: HKUnit.kilocalorie()) {
452
+ totalEnergyBurned = quantity
453
+ }
454
+ if quantity.is(compatibleWith: HKUnit.meter()) {
455
+ totalDistance = quantity
456
+ }
457
+ if typeId == HKQuantityTypeIdentifier.swimmingStrokeCount {
458
+ totalSwimmingStrokeCount = quantity
459
+ }
460
+ if typeId == HKQuantityTypeIdentifier.flightsClimbed {
461
+ totalFlightsClimbed = quantity
462
+ }
463
+ if let quantityStart, let quantityEnd {
464
+ let quantityStartDate = self._dateFormatter.date(from: quantityStart) ?? start
465
+ let quantityEndDate = self._dateFormatter.date(from: quantityEnd) ?? end
466
+ let quantitySample = HKQuantitySample.init(
467
+ type: type,
468
+ quantity: quantity,
469
+ start: quantityStartDate,
470
+ end: quantityEndDate,
471
+ metadata: metadata
472
+ )
473
+ initializedSamples.append(quantitySample)
474
+ } else {
475
+ // Handle the case where either startDate or endDate is nil
476
+ let quantitySample = HKQuantitySample.init(
477
+ type: type,
478
+ quantity: quantity,
479
+ start: start,
480
+ end: end,
481
+ metadata: metadata
482
+ )
483
+ initializedSamples.append(quantitySample)
373
484
  }
485
+ }
486
+ }
374
487
 
375
- // if totals are provided override samples
376
- let rawTotalDistance = totals["distance"] as? Double ?? 0.0
377
- let rawTotalEnergy = totals["energyBurned"] as? Double ?? 0.0
378
-
379
- if rawTotalDistance != 0.0 {
380
- totalDistance = HKQuantity(unit: .meter(), doubleValue: rawTotalDistance)
381
- }
382
- if rawTotalEnergy != 0.0 {
383
- totalEnergyBurned = HKQuantity(unit: .kilocalorie(), doubleValue: rawTotalEnergy)
384
- }
488
+ // if totals are provided override samples
489
+ let rawTotalDistance = totals["distance"] as? Double ?? 0.0
490
+ let rawTotalEnergy = totals["energyBurned"] as? Double ?? 0.0
385
491
 
386
- // creating workout
387
- var workout: HKWorkout?
492
+ if rawTotalDistance != 0.0 {
493
+ totalDistance = HKQuantity(unit: .meter(), doubleValue: rawTotalDistance)
494
+ }
495
+ if rawTotalEnergy != 0.0 {
496
+ totalEnergyBurned = HKQuantity(unit: .kilocalorie(), doubleValue: rawTotalEnergy)
497
+ }
388
498
 
389
- if totalSwimmingStrokeCount != nil {
390
- workout = HKWorkout.init(activityType: type, start: start, end: end, workoutEvents: nil, totalEnergyBurned: totalEnergyBurned, totalDistance: totalDistance, totalSwimmingStrokeCount: totalSwimmingStrokeCount, device: nil, metadata: metadata)
391
- } else {
392
- if #available(iOS 11, *) {
393
- if totalFlightsClimbed != nil {
394
- workout = HKWorkout.init(activityType: type, start: start, end: end, workoutEvents: nil, totalEnergyBurned: totalEnergyBurned, totalDistance: totalDistance, totalFlightsClimbed: totalFlightsClimbed, device: nil, metadata: metadata)
395
- }
396
- }
499
+ // creating workout
500
+ var workout: HKWorkout?
501
+
502
+ if totalSwimmingStrokeCount != nil {
503
+ workout = HKWorkout.init(
504
+ activityType: type,
505
+ start: start,
506
+ end: end,
507
+ workoutEvents: nil,
508
+ totalEnergyBurned: totalEnergyBurned,
509
+ totalDistance: totalDistance,
510
+ totalSwimmingStrokeCount: totalSwimmingStrokeCount,
511
+ device: nil,
512
+ metadata: metadata
513
+ )
514
+ } else {
515
+ if #available(iOS 11, *) {
516
+ if totalFlightsClimbed != nil {
517
+ workout = HKWorkout.init(
518
+ activityType: type,
519
+ start: start,
520
+ end: end,
521
+ workoutEvents: nil,
522
+ totalEnergyBurned: totalEnergyBurned,
523
+ totalDistance: totalDistance,
524
+ totalFlightsClimbed: totalFlightsClimbed,
525
+ device: nil,
526
+ metadata: metadata
527
+ )
397
528
  }
529
+ }
530
+ }
398
531
 
399
- if workout == nil {
400
- workout = HKWorkout.init(activityType: type, start: start, end: end, workoutEvents: nil, totalEnergyBurned: totalEnergyBurned, totalDistance: totalDistance, metadata: metadata)
401
- }
532
+ if workout == nil {
533
+ workout = HKWorkout.init(
534
+ activityType: type,
535
+ start: start,
536
+ end: end,
537
+ workoutEvents: nil,
538
+ totalEnergyBurned: totalEnergyBurned,
539
+ totalDistance: totalDistance,
540
+ metadata: metadata
541
+ )
542
+ }
402
543
 
403
- guard let workout = workout else {
404
- reject(GENERIC_ERROR, "Could not create workout", nil)
405
- return
406
- }
544
+ guard let workout = workout else {
545
+ reject(GENERIC_ERROR, "Could not create workout", nil)
546
+ return
547
+ }
407
548
 
408
- // saving workout, samples and route
409
- store.save(workout) { (_: Bool, error: Error?) in
410
- guard error == nil else {
411
- reject(GENERIC_ERROR, error!.localizedDescription, error)
412
- return
413
- }
549
+ // saving workout, samples and route
550
+ store.save(workout) { (_: Bool, error: Error?) in
551
+ guard error == nil else {
552
+ reject(GENERIC_ERROR, error!.localizedDescription, error)
553
+ return
554
+ }
414
555
 
415
- if initializedSamples.isEmpty {
416
- return resolve(workout.uuid.uuidString)
417
- }
556
+ if initializedSamples.isEmpty {
557
+ return resolve(workout.uuid.uuidString)
558
+ }
418
559
 
419
- store.add(initializedSamples, to: workout) { (_, error: Error?) in
420
- guard error == nil else {
421
- reject(GENERIC_ERROR, error!.localizedDescription, error)
422
- return
423
- }
424
- return resolve(workout.uuid.uuidString)
425
- }
560
+ store.add(initializedSamples, to: workout) { (_, error: Error?) in
561
+ guard error == nil else {
562
+ reject(GENERIC_ERROR, error!.localizedDescription, error)
563
+ return
426
564
  }
565
+ return resolve(workout.uuid.uuidString)
566
+ }
427
567
  }
568
+ }
428
569
 
429
- // function which will take an array of location in string format and create an array of CLLocations
430
- func _createCLLocations(from locations: [[String: Any]]) -> [CLLocation] {
431
- var clLocations: [CLLocation] = []
432
- for location in locations {
433
- guard let latitude = location["latitude"] as? CLLocationDegrees,
434
- let longitude = location["longitude"] as? CLLocationDegrees,
435
- let altitude = location["altitude"] as? CLLocationDistance,
436
- let horizontalAccuracy = location["horizontalAccuracy"] as? CLLocationAccuracy,
437
- let verticalAccuracy = location["verticalAccuracy"] as? CLLocationAccuracy,
438
- let course = location["course"] as? CLLocationDirection,
439
- let speed = location["speed"] as? CLLocationSpeed,
440
- let timestamp = location["timestamp"] as? String else {
441
- continue
442
- }
570
+ // function which will take an array of location in string format and create an array of CLLocations
571
+ func _createCLLocations(from locations: [[String: Any]]) -> [CLLocation] {
572
+ var clLocations: [CLLocation] = []
573
+ for location in locations {
574
+ guard let latitude = location["latitude"] as? CLLocationDegrees,
575
+ let longitude = location["longitude"] as? CLLocationDegrees,
576
+ let altitude = location["altitude"] as? CLLocationDistance,
577
+ let horizontalAccuracy = location["horizontalAccuracy"] as? CLLocationAccuracy,
578
+ let verticalAccuracy = location["verticalAccuracy"] as? CLLocationAccuracy,
579
+ let course = location["course"] as? CLLocationDirection,
580
+ let speed = location["speed"] as? CLLocationSpeed,
581
+ let timestamp = location["timestamp"] as? String else {
582
+ continue
583
+ }
443
584
 
444
- let date = self._dateFormatter.date(from: timestamp) ?? Date()
445
- let clLocation = CLLocation(coordinate: CLLocationCoordinate2D(latitude: latitude, longitude: longitude), altitude: altitude, horizontalAccuracy: horizontalAccuracy, verticalAccuracy: verticalAccuracy, course: course, speed: speed, timestamp: date)
446
- clLocations.append(clLocation)
447
- }
448
- return clLocations
585
+ let date = self._dateFormatter.date(from: timestamp) ?? Date()
586
+ let clLocation = CLLocation(
587
+ coordinate: CLLocationCoordinate2D(
588
+ latitude: latitude,
589
+ longitude: longitude
590
+ ), altitude: altitude,
591
+ horizontalAccuracy: horizontalAccuracy,
592
+ verticalAccuracy: verticalAccuracy,
593
+ course: course,
594
+ speed: speed,
595
+ timestamp: date
596
+ )
597
+ clLocations.append(clLocation)
449
598
  }
599
+ return clLocations
600
+ }
450
601
 
451
- @available(iOS 13.0.0, *)
452
- @objc(saveWorkoutRoute:locations:resolve:reject:)
453
- func saveWorkoutRoute(workoutUUID: String, locations: [[String: Any]], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
454
- guard let store = _store else {
455
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
456
- }
602
+ @available(iOS 13.0.0, *)
603
+ @objc(saveWorkoutRoute:locations:resolve:reject:)
604
+ func saveWorkoutRoute(
605
+ workoutUUID: String,
606
+ locations: [[String: Any]],
607
+ resolve: @escaping RCTPromiseResolveBlock,
608
+ reject: @escaping RCTPromiseRejectBlock
609
+ ) {
610
+ guard let store = _store else {
611
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
612
+ }
457
613
 
458
- Task {
459
- if let uuid = UUID(uuidString: workoutUUID) {
460
- do {
461
- let workout = await self.getWorkoutByID(store: store, workoutUUID: uuid)
462
- if let workout {
463
- // create CLLocations and return if locations are empty
464
- let clLocations = self._createCLLocations(from: locations)
465
- if clLocations.isEmpty {
466
- return reject(GENERIC_ERROR, "No locations provided", nil)
467
- }
468
- // create route
469
- let routeBuilder = HKWorkoutRouteBuilder(healthStore: store, device: nil)
470
- try await routeBuilder.insertRouteData(clLocations)
471
- try await routeBuilder.finishRoute(with: workout, metadata: nil)
472
-
473
- return resolve(true)
474
- } else {
475
- return reject(GENERIC_ERROR, "No workout found", nil)
476
- }
477
- } catch {
478
- return reject(GENERIC_ERROR, error.localizedDescription, error)
479
- }
480
- } else {
481
- return reject(GENERIC_ERROR, "Invalid UUID", nil)
614
+ Task {
615
+ if let uuid = UUID(uuidString: workoutUUID) {
616
+ do {
617
+ let workout = await self.getWorkoutByID(store: store, workoutUUID: uuid)
618
+ if let workout {
619
+ // create CLLocations and return if locations are empty
620
+ let clLocations = self._createCLLocations(from: locations)
621
+ if clLocations.isEmpty {
622
+ return reject(GENERIC_ERROR, "No locations provided", nil)
482
623
  }
624
+ // create route
625
+ let routeBuilder = HKWorkoutRouteBuilder(healthStore: store, device: nil)
626
+ try await routeBuilder.insertRouteData(clLocations)
627
+ try await routeBuilder.finishRoute(with: workout, metadata: nil)
628
+
629
+ return resolve(true)
630
+ } else {
631
+ return reject(GENERIC_ERROR, "No workout found", nil)
632
+ }
633
+ } catch {
634
+ return reject(GENERIC_ERROR, error.localizedDescription, error)
483
635
  }
636
+ } else {
637
+ return reject(GENERIC_ERROR, "Invalid UUID", nil)
638
+ }
484
639
  }
640
+ }
485
641
 
486
- @objc(saveCategorySample:value:start:end:metadata:resolve:reject:)
487
- func saveCategorySample(typeIdentifier: String, value: Double, start: Date, end: Date, metadata: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
488
- guard let store = _store else {
489
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
490
- }
642
+ @objc(saveCategorySample:value:start:end:metadata:resolve:reject:)
643
+ func saveCategorySample(
644
+ typeIdentifier: String,
645
+ value: Double,
646
+ start: Date,
647
+ end: Date,
648
+ metadata: NSDictionary,
649
+ resolve: @escaping RCTPromiseResolveBlock,
650
+ reject: @escaping RCTPromiseRejectBlock
651
+ ) {
652
+ guard let store = _store else {
653
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
654
+ }
491
655
 
492
- let identifier = HKCategoryTypeIdentifier.init(rawValue: typeIdentifier)
656
+ let identifier = HKCategoryTypeIdentifier.init(rawValue: typeIdentifier)
493
657
 
494
- guard let type = HKObjectType.categoryType(forIdentifier: identifier) else {
495
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
496
- }
658
+ guard let type = HKObjectType.categoryType(forIdentifier: identifier) else {
659
+ return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
660
+ }
497
661
 
498
- let sample = HKCategorySample.init(type: type, value: Int(value), start: start, end: end, metadata: metadata as? [String: Any])
662
+ let sample = HKCategorySample.init(
663
+ type: type,
664
+ value: Int(value),
665
+ start: start,
666
+ end: end,
667
+ metadata: metadata as? [String: Any]
668
+ )
499
669
 
500
- store.save(sample) { (success: Bool, error: Error?) in
501
- guard let err = error else {
502
- return resolve(success)
503
- }
504
- reject(GENERIC_ERROR, err.localizedDescription, error)
505
- }
670
+ store.save(sample) { (success: Bool, error: Error?) in
671
+ guard let err = error else {
672
+ return resolve(success)
673
+ }
674
+ reject(GENERIC_ERROR, err.localizedDescription, error)
506
675
  }
676
+ }
677
+
678
+ override func supportedEvents() -> [String]! {
679
+ return ["onChange"]
680
+ }
507
681
 
508
- override func supportedEvents() -> [String]! {
509
- return ["onChange"]
682
+ @objc(enableBackgroundDelivery:updateFrequency:resolve:reject:)
683
+ func enableBackgroundDelivery(
684
+ typeIdentifier: String,
685
+ updateFrequency: Int,
686
+ resolve: @escaping RCTPromiseResolveBlock,
687
+ reject: @escaping RCTPromiseRejectBlock
688
+ ) {
689
+ guard let store = _store else {
690
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
510
691
  }
511
692
 
512
- @objc(enableBackgroundDelivery:updateFrequency:resolve:reject:)
513
- func enableBackgroundDelivery(typeIdentifier: String, updateFrequency: Int, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
514
- guard let store = _store else {
515
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
516
- }
693
+ guard let sampleType = objectTypeFromString(typeIdentifier: typeIdentifier) else {
694
+ return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
695
+ }
517
696
 
518
- guard let sampleType = objectTypeFromString(typeIdentifier: typeIdentifier) else {
519
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
520
- }
697
+ guard let frequency = HKUpdateFrequency.init(rawValue: updateFrequency) else {
698
+ return reject("UpdateFrequency not valid", "UpdateFrequency not valid", nil)
699
+ }
521
700
 
522
- guard let frequency = HKUpdateFrequency.init(rawValue: updateFrequency) else {
523
- return reject("UpdateFrequency not valid", "UpdateFrequency not valid", nil)
524
- }
701
+ store.enableBackgroundDelivery(for: sampleType, frequency: frequency ) { (success, error) in
702
+ guard let err = error else {
703
+ return resolve(success)
704
+ }
705
+ reject(GENERIC_ERROR, err.localizedDescription, err)
706
+ }
707
+ }
525
708
 
526
- store.enableBackgroundDelivery(for: sampleType, frequency: frequency ) { (success, error) in
527
- guard let err = error else {
528
- return resolve(success)
529
- }
530
- reject(GENERIC_ERROR, err.localizedDescription, err)
531
- }
709
+ @objc(disableAllBackgroundDelivery:reject:)
710
+ func disableAllBackgroundDelivery(
711
+ resolve: @escaping RCTPromiseResolveBlock,
712
+ reject: @escaping RCTPromiseRejectBlock
713
+ ) {
714
+ guard let store = _store else {
715
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
532
716
  }
533
717
 
534
- @objc(disableAllBackgroundDelivery:reject:)
535
- func disableAllBackgroundDelivery(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
536
- guard let store = _store else {
537
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
538
- }
718
+ store.disableAllBackgroundDelivery(completion: { (success, error) in
719
+ guard let err = error else {
720
+ return resolve(success)
721
+ }
722
+ reject(GENERIC_ERROR, err.localizedDescription, err)
723
+ })
724
+ }
539
725
 
540
- store.disableAllBackgroundDelivery(completion: { (success, error) in
541
- guard let err = error else {
542
- return resolve(success)
543
- }
544
- reject(GENERIC_ERROR, err.localizedDescription, err)
545
- })
726
+ @objc(disableBackgroundDelivery:resolve:reject:)
727
+ func disableBackgroundDelivery(
728
+ typeIdentifier: String,
729
+ resolve: @escaping RCTPromiseResolveBlock,
730
+ reject: @escaping RCTPromiseRejectBlock
731
+ ) {
732
+ guard let store = _store else {
733
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
546
734
  }
547
735
 
548
- @objc(disableBackgroundDelivery:resolve:reject:)
549
- func disableBackgroundDelivery(typeIdentifier: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
550
- guard let store = _store else {
551
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
552
- }
553
-
554
- guard let sampleType = objectTypeFromString(typeIdentifier: typeIdentifier) else {
555
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
556
- }
736
+ guard let sampleType = objectTypeFromString(typeIdentifier: typeIdentifier) else {
737
+ return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
738
+ }
557
739
 
558
- store.disableBackgroundDelivery(for: sampleType) { (success, error) in
559
- guard let err = error else {
560
- return resolve(success)
561
- }
562
- reject(GENERIC_ERROR, err.localizedDescription, err)
563
- }
740
+ store.disableBackgroundDelivery(for: sampleType) { (success, error) in
741
+ guard let err = error else {
742
+ return resolve(success)
743
+ }
744
+ reject(GENERIC_ERROR, err.localizedDescription, err)
564
745
  }
746
+ }
565
747
 
566
- @objc(subscribeToObserverQuery:resolve:reject:)
567
- func subscribeToObserverQuery(typeIdentifier: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
568
- guard let store = _store else {
569
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
570
- }
748
+ @objc(subscribeToObserverQuery:resolve:reject:)
749
+ func subscribeToObserverQuery(
750
+ typeIdentifier: String,
751
+ resolve: @escaping RCTPromiseResolveBlock,
752
+ reject: @escaping RCTPromiseRejectBlock
753
+ ) {
754
+ guard let store = _store else {
755
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
756
+ }
571
757
 
572
- guard let sampleType = sampleTypeFromString(typeIdentifier: typeIdentifier) else {
573
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
574
- }
758
+ guard let sampleType = sampleTypeFromString(typeIdentifier: typeIdentifier) else {
759
+ return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
760
+ }
575
761
 
576
- let predicate = HKQuery.predicateForSamples(withStart: Date.init(), end: nil, options: HKQueryOptions.strictStartDate)
762
+ let predicate = HKQuery.predicateForSamples(
763
+ withStart: Date.init(),
764
+ end: nil,
765
+ options: HKQueryOptions.strictStartDate
766
+ )
577
767
 
578
- let queryId = UUID().uuidString
768
+ let queryId = UUID().uuidString
579
769
 
580
- func responder(query: HKObserverQuery, handler: @escaping HKObserverQueryCompletionHandler, error: Error?) {
581
- if error == nil {
582
- DispatchQueue.main.async {
583
- if self.bridge != nil && self.bridge.isValid {
584
- self.sendEvent(withName: "onChange", body: [
585
- "typeIdentifier": typeIdentifier
586
- ])
587
- }
770
+ func responder(query: HKObserverQuery, handler: @escaping HKObserverQueryCompletionHandler, error: Error?) {
771
+ if error == nil {
772
+ DispatchQueue.main.async {
773
+ if self.bridge != nil && self.bridge.isValid {
774
+ self.sendEvent(withName: "onChange", body: [
775
+ "typeIdentifier": typeIdentifier
776
+ ])
777
+ }
588
778
 
589
- }
590
- handler()
591
- }
592
779
  }
780
+ handler()
781
+ }
782
+ }
783
+
784
+ let query = HKObserverQuery(
785
+ sampleType: sampleType,
786
+ predicate: predicate
787
+ ) { (query: HKObserverQuery, handler: @escaping HKObserverQueryCompletionHandler, error: Error?) in
788
+ guard let err = error else {
789
+ return responder(query: query, handler: handler, error: error)
790
+ }
791
+ reject(GENERIC_ERROR, err.localizedDescription, err)
792
+ }
593
793
 
594
- let query = HKObserverQuery(sampleType: sampleType, predicate: predicate) { (query: HKObserverQuery, handler: @escaping HKObserverQueryCompletionHandler, error: Error?) in
595
- guard let err = error else {
596
- return responder(query: query, handler: handler, error: error)
597
- }
598
- reject(GENERIC_ERROR, err.localizedDescription, err)
599
- }
794
+ store.execute(query)
795
+
796
+ self._runningQueries.updateValue(query, forKey: queryId)
600
797
 
601
- store.execute(query)
798
+ resolve(queryId)
799
+ }
602
800
 
603
- self._runningQueries.updateValue(query, forKey: queryId)
801
+ @objc(unsubscribeQuery:resolve:reject:)
802
+ func unsubscribeQuery(
803
+ queryId: String,
804
+ resolve: @escaping RCTPromiseResolveBlock,
805
+ reject: @escaping RCTPromiseRejectBlock
806
+ ) {
807
+ guard let store = _store else {
808
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
809
+ }
604
810
 
605
- resolve(queryId)
811
+ guard let query = self._runningQueries[queryId] else {
812
+ reject("Error", "Query with id " + queryId + " not found", nil)
813
+ return
606
814
  }
607
815
 
608
- @objc(unsubscribeQuery:resolve:reject:)
609
- func unsubscribeQuery(queryId: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
610
- guard let store = _store else {
611
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
612
- }
816
+ store.stop(query)
613
817
 
614
- guard let query = self._runningQueries[queryId] else {
615
- reject("Error", "Query with id " + queryId + " not found", nil)
616
- return
617
- }
818
+ self._runningQueries.removeValue(forKey: queryId)
618
819
 
619
- store.stop(query)
820
+ resolve(true)
821
+ }
620
822
 
621
- self._runningQueries.removeValue(forKey: queryId)
823
+ static override func requiresMainQueueSetup() -> Bool {
824
+ return true
825
+ }
622
826
 
623
- resolve(true)
827
+ @objc(queryStatisticsForQuantity:unitString:from:to:options:resolve:reject:)
828
+ func queryStatisticsForQuantity(
829
+ typeIdentifier: String,
830
+ unitString: String,
831
+ from: Date,
832
+ to: Date,
833
+ options: NSArray,
834
+ resolve: @escaping RCTPromiseResolveBlock,
835
+ reject: @escaping RCTPromiseRejectBlock
836
+ ) {
837
+ guard let store = _store else {
838
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
624
839
  }
625
840
 
626
- static override func requiresMainQueueSetup() -> Bool {
627
- return true
841
+ let identifier = HKQuantityTypeIdentifier.init(rawValue: typeIdentifier)
842
+ guard let quantityType = HKObjectType.quantityType(forIdentifier: identifier) else {
843
+ return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
628
844
  }
629
845
 
630
- @objc(queryStatisticsForQuantity:unitString:from:to:options:resolve:reject:)
631
- func queryStatisticsForQuantity(typeIdentifier: String, unitString: String, from: Date, to: Date, options: NSArray, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
632
- guard let store = _store else {
633
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
846
+ let predicate = HKQuery.predicateForSamples(
847
+ withStart: from,
848
+ end: to,
849
+ options: HKQueryOptions.strictEndDate
850
+ )
851
+
852
+ var opts = HKStatisticsOptions.init()
853
+
854
+ for o in options {
855
+ let str = o as! String
856
+ if str == "cumulativeSum" {
857
+ opts.insert(HKStatisticsOptions.cumulativeSum)
858
+ } else if str == "discreteAverage" {
859
+ opts.insert(HKStatisticsOptions.discreteAverage)
860
+ } else if str == "discreteMax" {
861
+ opts.insert(HKStatisticsOptions.discreteMax)
862
+ } else if str == "discreteMin" {
863
+ opts.insert(HKStatisticsOptions.discreteMin)
864
+ }
865
+ if #available(iOS 12, *) {
866
+ if str == "discreteMostRecent" {
867
+ opts.insert(HKStatisticsOptions.discreteMostRecent)
634
868
  }
635
-
636
- let identifier = HKQuantityTypeIdentifier.init(rawValue: typeIdentifier)
637
- guard let quantityType = HKObjectType.quantityType(forIdentifier: identifier) else {
638
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
869
+ }
870
+ if #available(iOS 13, *) {
871
+ if str == "duration" {
872
+ opts.insert(HKStatisticsOptions.duration)
639
873
  }
640
-
641
- let predicate = HKQuery.predicateForSamples(withStart: from, end: to, options: HKQueryOptions.strictEndDate)
642
-
643
- var opts = HKStatisticsOptions.init()
644
-
645
- for o in options {
646
- let str = o as! String
647
- if str == "cumulativeSum" {
648
- opts.insert(HKStatisticsOptions.cumulativeSum)
649
- } else if str == "discreteAverage" {
650
- opts.insert(HKStatisticsOptions.discreteAverage)
651
- } else if str == "discreteMax" {
652
- opts.insert(HKStatisticsOptions.discreteMax)
653
- } else if str == "discreteMin" {
654
- opts.insert(HKStatisticsOptions.discreteMin)
655
- }
656
- if #available(iOS 12, *) {
657
- if str == "discreteMostRecent" {
658
- opts.insert(HKStatisticsOptions.discreteMostRecent)
659
- }
660
- }
661
- if #available(iOS 13, *) {
662
- if str == "duration" {
663
- opts.insert(HKStatisticsOptions.duration)
664
- }
665
- if str == "mostRecent" {
666
- opts.insert(HKStatisticsOptions.mostRecent)
667
- }
668
- }
669
-
670
- if str == "separateBySource" {
671
- opts.insert(HKStatisticsOptions.separateBySource)
672
- }
874
+ if str == "mostRecent" {
875
+ opts.insert(HKStatisticsOptions.mostRecent)
673
876
  }
877
+ }
674
878
 
675
- let query = HKStatisticsQuery.init(quantityType: quantityType, quantitySamplePredicate: predicate, options: opts) { (_, stats: HKStatistics?, _: Error?) in
676
- var dic = [String: [String: Any]?]()
879
+ if str == "separateBySource" {
880
+ opts.insert(HKStatisticsOptions.separateBySource)
881
+ }
882
+ }
677
883
 
678
- guard let gottenStats = stats else {
679
- return resolve(dic)
680
- }
884
+ let query = HKStatisticsQuery.init(
885
+ quantityType: quantityType,
886
+ quantitySamplePredicate: predicate,
887
+ options: opts
888
+ ) { (_, stats: HKStatistics?, _: Error?) in
889
+ var dic = [String: [String: Any]?]()
681
890
 
682
- let unit = HKUnit.init(from: unitString)
683
- if let averageQuantity = gottenStats.averageQuantity() {
684
- dic.updateValue(serializeQuantity(unit: unit, quantity: averageQuantity), forKey: "averageQuantity")
685
- }
686
- if let maximumQuantity = gottenStats.maximumQuantity() {
687
- dic.updateValue(serializeQuantity(unit: unit, quantity: maximumQuantity), forKey: "maximumQuantity")
688
- }
689
- if let minimumQuantity = gottenStats.minimumQuantity() {
690
- dic.updateValue(serializeQuantity(unit: unit, quantity: minimumQuantity), forKey: "minimumQuantity")
691
- }
692
- if let sumQuantity = gottenStats.sumQuantity() {
693
- dic.updateValue(serializeQuantity(unit: unit, quantity: sumQuantity), forKey: "sumQuantity")
694
- }
695
- if #available(iOS 12, *) {
696
- if let mostRecent = gottenStats.mostRecentQuantity() {
697
- dic.updateValue(serializeQuantity(unit: unit, quantity: mostRecent), forKey: "mostRecentQuantity")
698
- }
891
+ guard let gottenStats = stats else {
892
+ return resolve(dic)
893
+ }
699
894
 
700
- if let mostRecentDateInterval = gottenStats.mostRecentQuantityDateInterval() {
701
- dic.updateValue([
702
- "start": self._dateFormatter.string(from: mostRecentDateInterval.start),
703
- "end": self._dateFormatter.string(from: mostRecentDateInterval.end)
704
- ], forKey: "mostRecentQuantityDateInterval")
705
- }
706
- }
707
- if #available(iOS 13, *) {
708
- let durationUnit = HKUnit.second()
709
- if let duration = gottenStats.duration() {
710
- dic.updateValue(serializeQuantity(unit: durationUnit, quantity: duration), forKey: "duration")
711
- }
712
- }
895
+ let unit = HKUnit.init(from: unitString)
896
+ if let averageQuantity = gottenStats.averageQuantity() {
897
+ dic.updateValue(serializeQuantity(unit: unit, quantity: averageQuantity), forKey: "averageQuantity")
898
+ }
899
+ if let maximumQuantity = gottenStats.maximumQuantity() {
900
+ dic.updateValue(serializeQuantity(unit: unit, quantity: maximumQuantity), forKey: "maximumQuantity")
901
+ }
902
+ if let minimumQuantity = gottenStats.minimumQuantity() {
903
+ dic.updateValue(serializeQuantity(unit: unit, quantity: minimumQuantity), forKey: "minimumQuantity")
904
+ }
905
+ if let sumQuantity = gottenStats.sumQuantity() {
906
+ dic.updateValue(serializeQuantity(unit: unit, quantity: sumQuantity), forKey: "sumQuantity")
907
+ }
908
+ if #available(iOS 12, *) {
909
+ if let mostRecent = gottenStats.mostRecentQuantity() {
910
+ dic.updateValue(serializeQuantity(unit: unit, quantity: mostRecent), forKey: "mostRecentQuantity")
911
+ }
713
912
 
714
- resolve(dic)
913
+ if let mostRecentDateInterval = gottenStats.mostRecentQuantityDateInterval() {
914
+ dic.updateValue([
915
+ "start": self._dateFormatter.string(from: mostRecentDateInterval.start),
916
+ "end": self._dateFormatter.string(from: mostRecentDateInterval.end)
917
+ ], forKey: "mostRecentQuantityDateInterval")
918
+ }
919
+ }
920
+ if #available(iOS 13, *) {
921
+ let durationUnit = HKUnit.second()
922
+ if let duration = gottenStats.duration() {
923
+ dic.updateValue(serializeQuantity(unit: durationUnit, quantity: duration), forKey: "duration")
715
924
  }
925
+ }
716
926
 
717
- store.execute(query)
927
+ resolve(dic)
718
928
  }
719
929
 
720
- func mapWorkout(workout: HKWorkout, distanceUnit: HKUnit, energyUnit: HKUnit) -> NSMutableDictionary {
930
+ store.execute(query)
931
+ }
932
+
933
+ func mapWorkout(
934
+ workout: HKWorkout,
935
+ distanceUnit: HKUnit,
936
+ energyUnit: HKUnit
937
+ ) -> NSMutableDictionary {
721
938
  let endDate = self._dateFormatter.string(from: workout.endDate)
722
939
  let startDate = self._dateFormatter.string(from: workout.startDate)
723
940
 
724
941
  let dict: NSMutableDictionary = [
725
- "uuid": workout.uuid.uuidString,
726
- "device": serializeDevice(_device: workout.device) as Any,
727
- "duration": workout.duration,
728
- "totalDistance": serializeQuantity(unit: distanceUnit, quantity: workout.totalDistance) as Any,
729
- "totalEnergyBurned": serializeQuantity(unit: energyUnit, quantity: workout.totalEnergyBurned) as Any,
730
- "totalSwimmingStrokeCount": serializeQuantity(unit: HKUnit.count(), quantity: workout.totalSwimmingStrokeCount) as Any,
731
- "workoutActivityType": workout.workoutActivityType.rawValue,
732
- "startDate": startDate,
733
- "endDate": endDate,
734
- "metadata": serializeMetadata(metadata: workout.metadata),
735
- "sourceRevision": serializeSourceRevision(_sourceRevision: workout.sourceRevision) as Any
942
+ "uuid": workout.uuid.uuidString,
943
+ "device": serializeDevice(_device: workout.device) as Any,
944
+ "duration": workout.duration,
945
+ "totalDistance": serializeQuantity(unit: distanceUnit, quantity: workout.totalDistance) as Any,
946
+ "totalEnergyBurned": serializeQuantity(unit: energyUnit, quantity: workout.totalEnergyBurned) as Any,
947
+ "totalSwimmingStrokeCount": serializeQuantity(unit: HKUnit.count(), quantity: workout.totalSwimmingStrokeCount) as Any,
948
+ "workoutActivityType": workout.workoutActivityType.rawValue,
949
+ "startDate": startDate,
950
+ "endDate": endDate,
951
+ "metadata": serializeMetadata(metadata: workout.metadata),
952
+ "sourceRevision": serializeSourceRevision(_sourceRevision: workout.sourceRevision) as Any
736
953
  ]
737
954
 
738
955
  // this is used for our laps functionality to get markers
739
956
  // https://developer.apple.com/documentation/healthkit/hkworkoutevent
740
957
  var eventArray: [[String: Any]] = []
741
958
  if let events = workout.workoutEvents {
742
- for event in events {
743
- let eventStartDate = self._dateFormatter.string(from: event.dateInterval.start)
744
- let eventEndDate = self._dateFormatter.string(from: event.dateInterval.end)
745
- let eventDict: [String: Any] = [
746
- "type": event.type.rawValue, // https://developer.apple.com/documentation/healthkit/hkworkouteventtype
747
- "startDate": eventStartDate,
748
- "endDate": eventEndDate
749
- ]
750
- eventArray.append(eventDict)
751
- }
959
+ for event in events {
960
+ let eventStartDate = self._dateFormatter.string(from: event.dateInterval.start)
961
+ let eventEndDate = self._dateFormatter.string(from: event.dateInterval.end)
962
+ let eventDict: [String: Any] = [
963
+ "type": event.type.rawValue, // https://developer.apple.com/documentation/healthkit/hkworkouteventtype
964
+ "startDate": eventStartDate,
965
+ "endDate": eventEndDate
966
+ ]
967
+ eventArray.append(eventDict)
968
+ }
752
969
  }
753
970
  dict["events"] = eventArray
754
971
 
@@ -757,83 +974,106 @@ class ReactNativeHealthkit: RCTEventEmitter {
757
974
  // it seems this might be depricated in the latest beta so this might need updating!
758
975
  var activitiesArray: [[String: Any]] = []
759
976
  if #available(iOS 16.0, *) {
760
- let activities: [HKWorkoutActivity] = workout.workoutActivities
761
-
762
- if !activities.isEmpty {
763
- for activity in activities {
764
- var activityStartDate = ""
765
- var activityEndDate = ""
766
- if let start = activity.startDate as Date? {
767
- activityStartDate = self._dateFormatter.string(from: start)
768
- }
769
- if let end = activity.endDate as Date? {
770
- activityEndDate = self._dateFormatter.string(from: end)
771
- }
772
- let activityDict: [String: Any] = [
773
- "startDate": activityStartDate,
774
- "endDate": activityEndDate,
775
- "uuid": activity.uuid.uuidString,
776
- "duration": activity.duration
777
- ]
778
- activitiesArray.append(activityDict)
779
- }
977
+ let activities: [HKWorkoutActivity] = workout.workoutActivities
978
+
979
+ if !activities.isEmpty {
980
+ for activity in activities {
981
+ var activityStartDate = ""
982
+ var activityEndDate = ""
983
+ if let start = activity.startDate as Date? {
984
+ activityStartDate = self._dateFormatter.string(from: start)
985
+ }
986
+ if let end = activity.endDate as Date? {
987
+ activityEndDate = self._dateFormatter.string(from: end)
988
+ }
989
+ let activityDict: [String: Any] = [
990
+ "startDate": activityStartDate,
991
+ "endDate": activityEndDate,
992
+ "uuid": activity.uuid.uuidString,
993
+ "duration": activity.duration
994
+ ]
995
+ activitiesArray.append(activityDict)
780
996
  }
997
+ }
781
998
  }
782
999
  dict["activities"] = activitiesArray
783
1000
 
784
1001
  if #available(iOS 11, *) {
785
- dict.setValue(serializeQuantity(unit: HKUnit.count(), quantity: workout.totalFlightsClimbed), forKey: "totalFlightsClimbed")
1002
+ dict.setValue(serializeQuantity(unit: HKUnit.count(), quantity: workout.totalFlightsClimbed), forKey: "totalFlightsClimbed")
786
1003
  }
787
1004
  return dict
788
1005
  }
789
1006
 
790
- @objc(queryWorkoutSamples:distanceUnitString:from:to:limit:ascending:resolve:reject:)
791
- func queryWorkoutSamples(energyUnitString: String, distanceUnitString: String, from: Date, to: Date, limit: Int, ascending: Bool, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
792
- guard let store = _store else {
793
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
794
- }
1007
+ @objc(queryWorkoutSamples:distanceUnitString:from:to:limit:ascending:resolve:reject:)
1008
+ func queryWorkoutSamples(
1009
+ energyUnitString: String,
1010
+ distanceUnitString: String,
1011
+ from: Date,
1012
+ to: Date,
1013
+ limit: Int,
1014
+ ascending: Bool,
1015
+ resolve: @escaping RCTPromiseResolveBlock,
1016
+ reject: @escaping RCTPromiseRejectBlock
1017
+ ) {
1018
+ guard let store = _store else {
1019
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1020
+ }
795
1021
 
796
- let from = dateOrNilIfZero(date: from)
797
- let to = dateOrNilIfZero(date: to)
1022
+ let from = dateOrNilIfZero(date: from)
1023
+ let to = dateOrNilIfZero(date: to)
798
1024
 
799
- let predicate = createPredicate(from: from, to: to)
1025
+ let predicate = createPredicate(from: from, to: to)
800
1026
 
801
- let limit = limitOrNilIfZero(limit: limit)
1027
+ let limit = limitOrNilIfZero(limit: limit)
802
1028
 
803
- let energyUnit = HKUnit.init(from: energyUnitString)
804
- let distanceUnit = HKUnit.init(from: distanceUnitString)
1029
+ let energyUnit = HKUnit.init(from: energyUnitString)
1030
+ let distanceUnit = HKUnit.init(from: distanceUnitString)
805
1031
 
806
- let q = HKSampleQuery(sampleType: .workoutType(), predicate: predicate, limit: limit, sortDescriptors: getSortDescriptors(ascending: ascending)) { (_: HKSampleQuery, sample: [HKSample]?, error: Error?) in
807
- guard let err = error else {
808
- guard let samples = sample else {
809
- return resolve([])
810
- }
811
- let arr: NSMutableArray = []
812
-
813
- for s in samples {
814
- if let workout = s as? HKWorkout {
815
- let dict = self.mapWorkout(
816
- workout: workout,
817
- distanceUnit: distanceUnit,
818
- energyUnit: energyUnit
819
- )
1032
+ let q = HKSampleQuery(
1033
+ sampleType: .workoutType(),
1034
+ predicate: predicate,
1035
+ limit: limit,
1036
+ sortDescriptors: getSortDescriptors(ascending: ascending)
1037
+ ) { (_: HKSampleQuery, sample: [HKSample]?, error: Error?) in
1038
+ guard let err = error else {
1039
+ guard let samples = sample else {
1040
+ return resolve([])
1041
+ }
1042
+ let arr: NSMutableArray = []
820
1043
 
821
- arr.add(dict)
822
- }
823
- }
1044
+ for s in samples {
1045
+ if let workout = s as? HKWorkout {
1046
+ let dict = self.mapWorkout(
1047
+ workout: workout,
1048
+ distanceUnit: distanceUnit,
1049
+ energyUnit: energyUnit
1050
+ )
824
1051
 
825
- return resolve(arr)
826
- }
827
- reject(GENERIC_ERROR, err.localizedDescription, err)
1052
+ arr.add(dict)
1053
+ }
828
1054
  }
829
1055
 
830
- store.execute(q)
1056
+ return resolve(arr)
1057
+ }
1058
+ reject(GENERIC_ERROR, err.localizedDescription, err)
831
1059
  }
832
1060
 
1061
+ store.execute(q)
1062
+ }
1063
+
833
1064
  @objc(queryWorkoutSamplesWithAnchor:distanceUnitString:from:to:limit:anchor:resolve:reject:)
834
- func queryWorkoutSamplesWithAnchor(energyUnitString: String, distanceUnitString: String, from: Date, to: Date, limit: Int, anchor: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
1065
+ func queryWorkoutSamplesWithAnchor(
1066
+ energyUnitString: String,
1067
+ distanceUnitString: String,
1068
+ from: Date,
1069
+ to: Date,
1070
+ limit: Int,
1071
+ anchor: String,
1072
+ resolve: @escaping RCTPromiseResolveBlock,
1073
+ reject: @escaping RCTPromiseRejectBlock
1074
+ ) {
835
1075
  guard let store = _store else {
836
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1076
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
837
1077
  }
838
1078
 
839
1079
  let from = dateOrNilIfZero(date: from)
@@ -857,7 +1097,7 @@ class ReactNativeHealthkit: RCTEventEmitter {
857
1097
  ) in
858
1098
  guard let err = error else {
859
1099
  guard let samples = s else {
860
- return resolve([])
1100
+ return resolve([])
861
1101
  }
862
1102
 
863
1103
  let arr: NSMutableArray = []
@@ -899,148 +1139,176 @@ class ReactNativeHealthkit: RCTEventEmitter {
899
1139
  resolve: @escaping RCTPromiseResolveBlock,
900
1140
  reject: @escaping RCTPromiseRejectBlock
901
1141
  ) {
902
- guard let store = _store else {
903
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
904
- }
905
-
906
- let identifier = HKQuantityTypeIdentifier.init(rawValue: typeIdentifier)
907
- guard let sampleType = HKSampleType.quantityType(forIdentifier: identifier) else {
908
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
909
- }
1142
+ guard let store = _store else {
1143
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1144
+ }
910
1145
 
911
- let from = dateOrNilIfZero(date: from)
912
- let to = dateOrNilIfZero(date: to)
913
- let predicate = createPredicate(from: from, to: to)
914
- let limit = limitOrNilIfZero(limit: limit)
1146
+ let identifier = HKQuantityTypeIdentifier.init(rawValue: typeIdentifier)
1147
+ guard let sampleType = HKSampleType.quantityType(forIdentifier: identifier) else {
1148
+ return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
1149
+ }
915
1150
 
916
- let q = HKSampleQuery(sampleType: sampleType, predicate: predicate, limit: limit, sortDescriptors: getSortDescriptors(ascending: ascending)) { (_: HKSampleQuery, sample: [HKSample]?, error: Error?) in
917
- guard let err = error else {
918
- guard let samples = sample else {
919
- return resolve([])
920
- }
921
- let arr: NSMutableArray = []
1151
+ let from = dateOrNilIfZero(date: from)
1152
+ let to = dateOrNilIfZero(date: to)
1153
+ let predicate = createPredicate(from: from, to: to)
1154
+ let limit = limitOrNilIfZero(limit: limit)
922
1155
 
923
- for s in samples {
924
- if let sample = s as? HKQuantitySample {
925
- let serialized = serializeQuantitySample(sample: sample, unit: HKUnit.init(from: unitString))
1156
+ let q = HKSampleQuery(
1157
+ sampleType: sampleType,
1158
+ predicate: predicate,
1159
+ limit: limit,
1160
+ sortDescriptors: getSortDescriptors(ascending: ascending)
1161
+ ) { (_: HKSampleQuery, sample: [HKSample]?, error: Error?) in
1162
+ guard let err = error else {
1163
+ guard let samples = sample else {
1164
+ return resolve([])
1165
+ }
1166
+ let arr: NSMutableArray = []
926
1167
 
927
- arr.add(serialized)
928
- }
929
- }
1168
+ for s in samples {
1169
+ if let sample = s as? HKQuantitySample {
1170
+ let serialized = serializeQuantitySample(sample: sample, unit: HKUnit.init(from: unitString))
930
1171
 
931
- return resolve(arr)
932
- }
933
- reject(GENERIC_ERROR, err.localizedDescription, err)
1172
+ arr.add(serialized)
1173
+ }
934
1174
  }
935
1175
 
936
- store.execute(q)
1176
+ return resolve(arr)
1177
+ }
1178
+ reject(GENERIC_ERROR, err.localizedDescription, err)
937
1179
  }
938
1180
 
939
- @objc(queryCorrelationSamples:from:to:resolve:reject:)
940
- func queryCorrelationSamples(typeIdentifier: String, from: Date, to: Date, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
941
- guard let store = _store else {
942
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
943
- }
1181
+ store.execute(q)
1182
+ }
944
1183
 
945
- let identifier = HKCorrelationTypeIdentifier.init(rawValue: typeIdentifier)
946
- guard let sampleType = HKSampleType.correlationType(forIdentifier: identifier) else {
947
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
948
- }
1184
+ @objc(queryCorrelationSamples:from:to:resolve:reject:)
1185
+ func queryCorrelationSamples(
1186
+ typeIdentifier: String,
1187
+ from: Date,
1188
+ to: Date,
1189
+ resolve: @escaping RCTPromiseResolveBlock,
1190
+ reject: @escaping RCTPromiseRejectBlock
1191
+ ) {
1192
+ guard let store = _store else {
1193
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1194
+ }
949
1195
 
950
- let from = from.timeIntervalSince1970 >= 0 ? from : nil
951
- let to = to.timeIntervalSince1970 >= 0 ? to : nil
1196
+ let identifier = HKCorrelationTypeIdentifier.init(rawValue: typeIdentifier)
1197
+ guard let sampleType = HKSampleType.correlationType(forIdentifier: identifier) else {
1198
+ return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
1199
+ }
952
1200
 
953
- let predicate = createPredicate(from: from, to: to)
1201
+ let from = from.timeIntervalSince1970 >= 0 ? from : nil
1202
+ let to = to.timeIntervalSince1970 >= 0 ? to : nil
954
1203
 
955
- let q = HKCorrelationQuery(type: sampleType, predicate: predicate, samplePredicates: nil) { (_: HKCorrelationQuery, _correlations: [HKCorrelation]?, error: Error?) in
956
- guard let err = error else {
957
- guard let correlations = _correlations else {
958
- return resolve([])
959
- }
1204
+ let predicate = createPredicate(from: from, to: to)
1205
+
1206
+ let q = HKCorrelationQuery(
1207
+ type: sampleType,
1208
+ predicate: predicate,
1209
+ samplePredicates: nil
1210
+ ) { (_: HKCorrelationQuery, _correlations: [HKCorrelation]?, error: Error?) in
1211
+ guard let err = error else {
1212
+ guard let correlations = _correlations else {
1213
+ return resolve([])
1214
+ }
960
1215
 
961
- var qts = Set<HKQuantityType>()
962
- for c in correlations {
963
- for object in c.objects {
964
- if let quantitySample = object as? HKQuantitySample {
965
- qts.insert(quantitySample.quantityType)
966
- }
967
- }
1216
+ var qts = Set<HKQuantityType>()
1217
+ for c in correlations {
1218
+ for object in c.objects {
1219
+ if let quantitySample = object as? HKQuantitySample {
1220
+ qts.insert(quantitySample.quantityType)
1221
+ }
1222
+ }
1223
+ }
1224
+ return store.preferredUnits(for: qts) { (map: [HKQuantityType: HKUnit], error: Error?) in
1225
+ guard let e = error else {
1226
+ let collerationsToReturn: NSMutableArray = []
1227
+ for c in correlations {
1228
+ let objects = NSMutableArray()
1229
+ for o in c.objects {
1230
+ if let quantitySample = o as? HKQuantitySample {
1231
+ objects.add(
1232
+ serializeQuantitySample(
1233
+ sample: quantitySample,
1234
+ unit: map[quantitySample.quantityType]!
1235
+ )
1236
+ )
968
1237
  }
969
- return store.preferredUnits(for: qts) { (map: [HKQuantityType: HKUnit], error: Error?) in
970
- guard let e = error else {
971
- let collerationsToReturn: NSMutableArray = []
972
- for c in correlations {
973
- let objects = NSMutableArray()
974
- for o in c.objects {
975
- if let quantitySample = o as? HKQuantitySample {
976
- objects.add(serializeQuantitySample(sample: quantitySample, unit: map[quantitySample.quantityType]!))
977
- }
978
- if let categorySample = o as? HKCategorySample {
979
- objects.add(serializeCategorySample(sample: categorySample))
980
- }
981
- }
982
-
983
- collerationsToReturn.add([
984
- "uuid": c.uuid.uuidString,
985
- "device": serializeDevice(_device: c.device) as Any,
986
- "correlationType": c.correlationType.identifier,
987
- "objects": objects,
988
- "metadata": serializeMetadata(metadata: c.metadata),
989
- "startDate": self._dateFormatter.string(from: c.startDate),
990
- "endDate": self._dateFormatter.string(from: c.endDate)
991
- ])
992
- }
993
-
994
- return resolve(collerationsToReturn)
995
- }
996
- reject(GENERIC_ERROR, e.localizedDescription, e)
1238
+ if let categorySample = o as? HKCategorySample {
1239
+ objects.add(serializeCategorySample(sample: categorySample))
997
1240
  }
1241
+ }
1242
+
1243
+ collerationsToReturn.add([
1244
+ "uuid": c.uuid.uuidString,
1245
+ "device": serializeDevice(_device: c.device) as Any,
1246
+ "correlationType": c.correlationType.identifier,
1247
+ "objects": objects,
1248
+ "metadata": serializeMetadata(metadata: c.metadata),
1249
+ "startDate": self._dateFormatter.string(from: c.startDate),
1250
+ "endDate": self._dateFormatter.string(from: c.endDate)
1251
+ ])
998
1252
  }
999
- reject(GENERIC_ERROR, err.localizedDescription, err)
1000
- }
1001
1253
 
1002
- store.execute(q)
1254
+ return resolve(collerationsToReturn)
1255
+ }
1256
+ reject(GENERIC_ERROR, e.localizedDescription, e)
1257
+ }
1258
+ }
1259
+ reject(GENERIC_ERROR, err.localizedDescription, err)
1003
1260
  }
1004
1261
 
1005
- @objc(queryCategorySamples:from:to:limit:ascending:resolve:reject:)
1006
- func queryCategorySamples(typeIdentifier: String, from: Date, to: Date, limit: Int, ascending: Bool, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
1007
- guard let store = _store else {
1008
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1009
- }
1262
+ store.execute(q)
1263
+ }
1010
1264
 
1011
- let identifier = HKCategoryTypeIdentifier.init(rawValue: typeIdentifier)
1012
- guard let sampleType = HKSampleType.categoryType(forIdentifier: identifier) else {
1013
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
1014
- }
1265
+ @objc(queryCategorySamples:from:to:limit:ascending:resolve:reject:)
1266
+ func queryCategorySamples(
1267
+ typeIdentifier: String,
1268
+ from: Date,
1269
+ to: Date,
1270
+ limit: Int,
1271
+ ascending: Bool,
1272
+ resolve: @escaping RCTPromiseResolveBlock,
1273
+ reject: @escaping RCTPromiseRejectBlock
1274
+ ) {
1275
+ guard let store = _store else {
1276
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1277
+ }
1015
1278
 
1016
- let from = dateOrNilIfZero(date: from)
1017
- let to = dateOrNilIfZero(date: to)
1018
- let predicate = createPredicate(from: from, to: to)
1019
- let limit = limitOrNilIfZero(limit: limit)
1279
+ let identifier = HKCategoryTypeIdentifier.init(rawValue: typeIdentifier)
1280
+ guard let sampleType = HKSampleType.categoryType(forIdentifier: identifier) else {
1281
+ return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
1282
+ }
1020
1283
 
1021
- let q = HKSampleQuery(sampleType: sampleType, predicate: predicate, limit: limit, sortDescriptors: getSortDescriptors(ascending: ascending)) { (_: HKSampleQuery, sample: [HKSample]?, error: Error?) in
1022
- guard let err = error else {
1023
- guard let samples = sample else {
1024
- return resolve([])
1025
- }
1026
- let arr: NSMutableArray = []
1284
+ let from = dateOrNilIfZero(date: from)
1285
+ let to = dateOrNilIfZero(date: to)
1286
+ let predicate = createPredicate(from: from, to: to)
1287
+ let limit = limitOrNilIfZero(limit: limit)
1027
1288
 
1028
- for s in samples {
1029
- if let sample = s as? HKCategorySample {
1030
- let serialized = serializeCategorySample(sample: sample)
1289
+ let q = HKSampleQuery(sampleType: sampleType, predicate: predicate, limit: limit, sortDescriptors: getSortDescriptors(ascending: ascending)) { (_: HKSampleQuery, sample: [HKSample]?, error: Error?) in
1290
+ guard let err = error else {
1291
+ guard let samples = sample else {
1292
+ return resolve([])
1293
+ }
1294
+ let arr: NSMutableArray = []
1031
1295
 
1032
- arr.add(serialized)
1033
- }
1034
- }
1035
- return resolve(arr)
1036
- }
1296
+ for s in samples {
1297
+ if let sample = s as? HKCategorySample {
1298
+ let serialized = serializeCategorySample(sample: sample)
1037
1299
 
1038
- reject(GENERIC_ERROR, err.localizedDescription, err)
1300
+ arr.add(serialized)
1301
+ }
1039
1302
  }
1303
+ return resolve(arr)
1304
+ }
1040
1305
 
1041
- store.execute(q)
1306
+ reject(GENERIC_ERROR, err.localizedDescription, err)
1042
1307
  }
1043
1308
 
1309
+ store.execute(q)
1310
+ }
1311
+
1044
1312
  @objc(queryQuantitySamplesWithAnchor:unitString:from:to:limit:anchor:resolve:reject:)
1045
1313
  func queryQuantitySamplesWithAnchor(
1046
1314
  typeIdentifier: String,
@@ -1052,586 +1320,687 @@ class ReactNativeHealthkit: RCTEventEmitter {
1052
1320
  resolve: @escaping RCTPromiseResolveBlock,
1053
1321
  reject: @escaping RCTPromiseRejectBlock
1054
1322
  ) {
1055
- guard let store = _store else {
1056
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1057
- }
1058
-
1059
- let identifier = HKQuantityTypeIdentifier.init(rawValue: typeIdentifier)
1060
- guard let sampleType = HKSampleType.quantityType(forIdentifier: identifier) else {
1061
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
1062
- }
1063
-
1064
- let from = dateOrNilIfZero(date: from)
1065
- let to = dateOrNilIfZero(date: to)
1066
- let predicate = createPredicate(from: from, to: to)
1067
- let limit = limitOrNilIfZero(limit: limit)
1068
-
1069
- let actualAnchor = deserializeHKQueryAnchor(anchor: anchor)
1070
-
1071
- let q = HKAnchoredObjectQuery(
1072
- type: sampleType,
1073
- predicate: predicate,
1074
- anchor: actualAnchor,
1075
- limit: limit
1076
- ) { (
1077
- _: HKAnchoredObjectQuery,
1078
- s: [HKSample]?,
1079
- deletedSamples: [HKDeletedObject]?,
1080
- newAnchor: HKQueryAnchor?,
1081
- error: Error?
1082
- ) in
1083
- guard let err = error else {
1084
- guard let samples = s else {
1085
- return resolve([])
1086
- }
1087
-
1088
- return resolve([
1089
- "samples": samples.map({ sample in
1090
- let serialized = serializeQuantitySample(sample: sample as! HKQuantitySample, unit: HKUnit.init(from: unitString))
1091
-
1092
- return serialized
1093
- }) as Any,
1094
- "deletedSamples": deletedSamples?.map({ sample in
1095
- return serializeDeletedSample(sample: sample)
1096
- }) as Any,
1097
- "newAnchor": serializeAnchor(anchor: newAnchor) as Any
1098
- ])
1099
- }
1100
- reject(GENERIC_ERROR, err.localizedDescription, err)
1101
- }
1323
+ guard let store = _store else {
1324
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1325
+ }
1326
+
1327
+ let identifier = HKQuantityTypeIdentifier.init(rawValue: typeIdentifier)
1328
+ guard let sampleType = HKSampleType.quantityType(forIdentifier: identifier) else {
1329
+ return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
1330
+ }
1331
+
1332
+ let from = dateOrNilIfZero(date: from)
1333
+ let to = dateOrNilIfZero(date: to)
1334
+ let predicate = createPredicate(from: from, to: to)
1335
+ let limit = limitOrNilIfZero(limit: limit)
1336
+
1337
+ let actualAnchor = deserializeHKQueryAnchor(anchor: anchor)
1338
+
1339
+ let q = HKAnchoredObjectQuery(
1340
+ type: sampleType,
1341
+ predicate: predicate,
1342
+ anchor: actualAnchor,
1343
+ limit: limit
1344
+ ) { (
1345
+ _: HKAnchoredObjectQuery,
1346
+ s: [HKSample]?,
1347
+ deletedSamples: [HKDeletedObject]?,
1348
+ newAnchor: HKQueryAnchor?,
1349
+ error: Error?
1350
+ ) in
1351
+ guard let err = error else {
1352
+ guard let samples = s else {
1353
+ return resolve([])
1354
+ }
1355
+
1356
+ return resolve([
1357
+ "samples": samples.map({ sample in
1358
+ let serialized = serializeQuantitySample(
1359
+ sample: sample as! HKQuantitySample,
1360
+ unit: HKUnit.init(from: unitString)
1361
+ )
1362
+
1363
+ return serialized
1364
+ }) as Any,
1365
+ "deletedSamples": deletedSamples?.map({ sample in
1366
+ return serializeDeletedSample(sample: sample)
1367
+ }) as Any,
1368
+ "newAnchor": serializeAnchor(anchor: newAnchor) as Any
1369
+ ])
1370
+ }
1371
+ reject(GENERIC_ERROR, err.localizedDescription, err)
1372
+ }
1373
+
1374
+ store.execute(q)
1375
+ }
1376
+
1377
+ @objc(queryCategorySamplesWithAnchor:from:to:limit:anchor:resolve:reject:)
1378
+ func queryCategorySamplesWithAnchor(
1379
+ typeIdentifier: String,
1380
+ from: Date,
1381
+ to: Date,
1382
+ limit: Int,
1383
+ anchor: String,
1384
+ resolve: @escaping RCTPromiseResolveBlock,
1385
+ reject: @escaping RCTPromiseRejectBlock
1386
+ ) {
1387
+ guard let store = _store else {
1388
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1389
+ }
1102
1390
 
1103
- store.execute(q)
1391
+ let identifier = HKCategoryTypeIdentifier.init(rawValue: typeIdentifier)
1392
+ guard let sampleType = HKSampleType.categoryType(forIdentifier: identifier) else {
1393
+ return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
1104
1394
  }
1105
1395
 
1106
- @objc(queryCategorySamplesWithAnchor:from:to:limit:anchor:resolve:reject:)
1107
- func queryCategorySamplesWithAnchor(typeIdentifier: String, from: Date, to: Date, limit: Int, anchor: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
1108
- guard let store = _store else {
1109
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1110
- }
1396
+ let from = dateOrNilIfZero(date: from)
1397
+ let to = dateOrNilIfZero(date: to)
1398
+ let predicate = createPredicate(from: from, to: to)
1399
+ let limit = limitOrNilIfZero(limit: limit)
1111
1400
 
1112
- let identifier = HKCategoryTypeIdentifier.init(rawValue: typeIdentifier)
1113
- guard let sampleType = HKSampleType.categoryType(forIdentifier: identifier) else {
1114
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
1401
+ let q = HKAnchoredObjectQuery(
1402
+ type: sampleType,
1403
+ predicate: predicate,
1404
+ anchor: anchor != "" ? base64StringToHKQueryAnchor(base64String: anchor) : nil,
1405
+ limit: limit
1406
+ ) { (
1407
+ _: HKAnchoredObjectQuery,
1408
+ s: [HKSample]?,
1409
+ deletedSamples: [HKDeletedObject]?,
1410
+ newAnchor: HKQueryAnchor?,
1411
+ error: Error?
1412
+ ) in
1413
+ guard let err = error else {
1414
+ guard let samples = s else {
1415
+ return resolve([])
1115
1416
  }
1116
1417
 
1117
- let from = dateOrNilIfZero(date: from)
1118
- let to = dateOrNilIfZero(date: to)
1119
- let predicate = createPredicate(from: from, to: to)
1120
- let limit = limitOrNilIfZero(limit: limit)
1121
-
1122
- let q = HKAnchoredObjectQuery(
1123
- type: sampleType,
1124
- predicate: predicate,
1125
- anchor: anchor != "" ? base64StringToHKQueryAnchor(base64String: anchor) : nil,
1126
- limit: limit
1127
- ) { (
1128
- _: HKAnchoredObjectQuery,
1129
- s: [HKSample]?,
1130
- deletedSamples: [HKDeletedObject]?,
1131
- newAnchor: HKQueryAnchor?,
1132
- error: Error?
1133
- ) in
1134
- guard let err = error else {
1135
- guard let samples = s else {
1136
- return resolve([])
1137
- }
1138
-
1139
- let arr: NSMutableArray = []
1140
-
1141
- for s in samples {
1142
- if let sample = s as? HKCategorySample {
1143
- let serialized = serializeCategorySample(sample: sample)
1418
+ let arr: NSMutableArray = []
1144
1419
 
1145
- arr.add(serialized)
1146
- }
1147
- }
1420
+ for s in samples {
1421
+ if let sample = s as? HKCategorySample {
1422
+ let serialized = serializeCategorySample(sample: sample)
1148
1423
 
1149
- return resolve([
1150
- "samples": arr,
1151
- "deletedSamples": deletedSamples?.map({ sample in
1152
- return serializeDeletedSample(sample: sample)
1153
- }) as Any,
1154
- "newAnchor": serializeAnchor(anchor: newAnchor) as Any
1155
- ])
1424
+ arr.add(serialized)
1156
1425
  }
1157
- reject(GENERIC_ERROR, err.localizedDescription, err)
1158
1426
  }
1159
1427
 
1160
- store.execute(q)
1428
+ return resolve([
1429
+ "samples": arr,
1430
+ "deletedSamples": deletedSamples?.map({ sample in
1431
+ return serializeDeletedSample(sample: sample)
1432
+ }) as Any,
1433
+ "newAnchor": serializeAnchor(anchor: newAnchor) as Any
1434
+ ])
1435
+ }
1436
+ reject(GENERIC_ERROR, err.localizedDescription, err)
1161
1437
  }
1162
1438
 
1163
- @objc(querySources:resolve:reject:)
1164
- func querySources(typeIdentifier: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
1165
-
1166
- guard let store = _store else {
1167
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1168
- }
1439
+ store.execute(q)
1440
+ }
1169
1441
 
1170
- guard let type = objectTypeFromString(typeIdentifier: typeIdentifier) else {
1171
- return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
1172
- }
1442
+ @objc(querySources:resolve:reject:)
1443
+ func querySources(
1444
+ typeIdentifier: String,
1445
+ resolve: @escaping RCTPromiseResolveBlock,
1446
+ reject: @escaping RCTPromiseRejectBlock
1447
+ ) {
1173
1448
 
1174
- let query = HKSourceQuery(sampleType: type as! HKSampleType, samplePredicate: nil) { (_: HKSourceQuery, source: Set<HKSource>?, error: Error?) in
1175
- guard let err = error else {
1176
- guard let sources = source else {
1177
- return resolve([])
1178
- }
1179
- let arr: NSMutableArray = []
1449
+ guard let store = _store else {
1450
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1451
+ }
1180
1452
 
1181
- for s in sources {
1182
- if let source = s as? HKSource {
1183
- let serialized = serializeSource(source: source)
1453
+ guard let type = objectTypeFromString(typeIdentifier: typeIdentifier) else {
1454
+ return reject(TYPE_IDENTIFIER_ERROR, "Failed to initialize " + typeIdentifier, nil)
1455
+ }
1184
1456
 
1185
- arr.add(serialized)
1186
- }
1187
- }
1457
+ let query = HKSourceQuery(
1458
+ sampleType: type as! HKSampleType,
1459
+ samplePredicate: nil
1460
+ ) { (
1461
+ _: HKSourceQuery,
1462
+ source: Set<HKSource>?,
1463
+ error: Error?
1464
+ ) in
1465
+ guard let err = error else {
1466
+ guard let sources = source else {
1467
+ return resolve([])
1468
+ }
1469
+ let arr: NSMutableArray = []
1188
1470
 
1189
- return resolve(arr)
1190
- }
1191
- reject(GENERIC_ERROR, err.localizedDescription, err)
1471
+ for s in sources {
1472
+ let serialized = serializeSource(source: s)
1192
1473
 
1474
+ arr.add(serialized)
1193
1475
  }
1194
1476
 
1195
- store.execute(query)
1196
- }
1477
+ return resolve(arr)
1478
+ }
1479
+ reject(GENERIC_ERROR, err.localizedDescription, err)
1197
1480
 
1198
- @objc(requestAuthorization:read:resolve:withRejecter:)
1199
- func requestAuthorization(toShare: NSDictionary, read: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
1200
- guard let store = _store else {
1201
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1202
- }
1481
+ }
1203
1482
 
1204
- let share = sampleTypesFromDictionary(typeIdentifiers: toShare)
1205
- let toRead = objectTypesFromDictionary(typeIdentifiers: read)
1483
+ store.execute(query)
1484
+ }
1206
1485
 
1207
- store.requestAuthorization(toShare: share, read: toRead) { (success: Bool, error: Error?) in
1208
- guard let err = error else {
1209
- return resolve(success)
1210
- }
1211
- reject(GENERIC_ERROR, err.localizedDescription, err)
1212
- }
1486
+ @objc(requestAuthorization:read:resolve:withRejecter:)
1487
+ func requestAuthorization(
1488
+ toShare: NSDictionary,
1489
+ read: NSDictionary,
1490
+ resolve: @escaping RCTPromiseResolveBlock,
1491
+ reject: @escaping RCTPromiseRejectBlock
1492
+ ) {
1493
+ guard let store = _store else {
1494
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1213
1495
  }
1214
1496
 
1215
- @available(iOS 13.0.0, *)
1216
- func getWorkoutByID(store: HKHealthStore,
1217
- workoutUUID: UUID) async -> HKWorkout? {
1218
- let workoutPredicate = HKQuery.predicateForObject(with: workoutUUID)
1497
+ let share = sampleTypesFromDictionary(typeIdentifiers: toShare)
1498
+ let toRead = objectTypesFromDictionary(typeIdentifiers: read)
1219
1499
 
1220
- let samples = try! await withCheckedThrowingContinuation {
1221
- (continuation: CheckedContinuation<[HKSample], Error>) in
1222
- let query = HKSampleQuery(sampleType: HKObjectType.workoutType(), predicate: workoutPredicate, limit: 1, sortDescriptors: nil) { (_, results, error) in
1500
+ store.requestAuthorization(toShare: share, read: toRead) { (success: Bool, error: Error?) in
1501
+ guard let err = error else {
1502
+ return resolve(success)
1503
+ }
1504
+ reject(GENERIC_ERROR, err.localizedDescription, err)
1505
+ }
1506
+ }
1223
1507
 
1224
- if let hasError = error {
1225
- continuation.resume(throwing: hasError)
1226
- return
1227
- }
1508
+ @available(iOS 13.0.0, *)
1509
+ func getWorkoutByID(store: HKHealthStore,
1510
+ workoutUUID: UUID) async -> HKWorkout? {
1511
+ let workoutPredicate = HKQuery.predicateForObject(with: workoutUUID)
1228
1512
 
1229
- guard let samples = results else {
1230
- fatalError("Should not fail")
1231
- }
1513
+ let samples = try! await withCheckedThrowingContinuation {
1514
+ (continuation: CheckedContinuation<[HKSample], Error>) in
1515
+ let query = HKSampleQuery(
1516
+ sampleType: HKObjectType.workoutType(),
1517
+ predicate: workoutPredicate,
1518
+ limit: 1,
1519
+ sortDescriptors: nil
1520
+ ) { (_, results, error) in
1232
1521
 
1233
- continuation.resume(returning: samples)
1234
- }
1235
- store.execute(query)
1522
+ if let hasError = error {
1523
+ continuation.resume(throwing: hasError)
1524
+ return
1236
1525
  }
1237
1526
 
1238
- guard let workouts = samples as? [HKWorkout] else {
1239
- return nil
1527
+ guard let samples = results else {
1528
+ fatalError("workout samples unexpectedly nil")
1240
1529
  }
1241
1530
 
1242
- return workouts.first ?? nil
1531
+ continuation.resume(returning: samples)
1532
+ }
1533
+ store.execute(query)
1243
1534
  }
1244
1535
 
1245
- @available(iOS 13.0.0, *)
1246
- func _getWorkoutRoutes(store: HKHealthStore, workoutUUID: UUID) async -> [HKWorkoutRoute]? {
1247
- guard let workout = await getWorkoutByID(store: store, workoutUUID: workoutUUID) else {
1248
- return nil
1249
- }
1536
+ guard let workouts = samples as? [HKWorkout] else {
1537
+ return nil
1538
+ }
1250
1539
 
1251
- let workoutPredicate = HKQuery.predicateForObjects(from: workout)
1252
- let samples = try! await withCheckedThrowingContinuation {
1253
- (continuation: CheckedContinuation<[HKSample], Error>) in
1254
- let query = HKAnchoredObjectQuery(type: HKSeriesType.workoutRoute(),
1255
- predicate: workoutPredicate,
1256
- anchor: nil,
1257
- limit: HKObjectQueryNoLimit) {
1258
- (_, samples, _, _, error) in
1259
-
1260
- if let hasError = error {
1261
- continuation.resume(throwing: hasError)
1262
- return
1263
- }
1540
+ return workouts.first ?? nil
1541
+ }
1264
1542
 
1265
- guard let samples = samples else {
1266
- fatalError("Should not fail")
1267
- }
1543
+ @available(iOS 13.0.0, *)
1544
+ func _getWorkoutRoutes(
1545
+ store: HKHealthStore,
1546
+ workoutUUID: UUID
1547
+ ) async -> [HKWorkoutRoute]? {
1548
+ guard let workout = await getWorkoutByID(store: store, workoutUUID: workoutUUID) else {
1549
+ return nil
1550
+ }
1268
1551
 
1269
- continuation.resume(returning: samples)
1270
- }
1271
- store.execute(query)
1552
+ let workoutPredicate = HKQuery.predicateForObjects(from: workout)
1553
+ let samples = try! await withCheckedThrowingContinuation {
1554
+ (continuation: CheckedContinuation<[HKSample], Error>) in
1555
+ let query = HKAnchoredObjectQuery(
1556
+ type: HKSeriesType.workoutRoute(),
1557
+ predicate: workoutPredicate,
1558
+ anchor: nil,
1559
+ limit: HKObjectQueryNoLimit
1560
+ ) {
1561
+ (_, samples, _, _, error) in
1562
+
1563
+ if let hasError = error {
1564
+ continuation.resume(throwing: hasError)
1565
+ return
1272
1566
  }
1273
1567
 
1274
- guard let routes = samples as? [HKWorkoutRoute] else {
1275
- return nil
1568
+ guard let samples = samples else {
1569
+ fatalError("workoutRoute samples unexpectedly nil")
1276
1570
  }
1277
1571
 
1278
- return routes
1572
+ continuation.resume(returning: samples)
1573
+ }
1574
+ store.execute(query)
1575
+ }
1576
+
1577
+ guard let routes = samples as? [HKWorkoutRoute] else {
1578
+ return nil
1279
1579
  }
1280
1580
 
1281
- @available(iOS 13.0.0, *)
1282
- func getRouteLocations(store: HKHealthStore, route: HKWorkoutRoute) async -> [CLLocation] {
1283
- let locations = try! await withCheckedThrowingContinuation {
1284
- (continuation: CheckedContinuation<[CLLocation], Error>) in
1285
- var allLocations: [CLLocation] = []
1581
+ return routes
1582
+ }
1286
1583
 
1287
- let query = HKWorkoutRouteQuery(route: route) {
1288
- (_, locationsOrNil, done, errorOrNil) in
1584
+ @available(iOS 13.0.0, *)
1585
+ func getRouteLocations(
1586
+ store: HKHealthStore,
1587
+ route: HKWorkoutRoute
1588
+ ) async -> [CLLocation] {
1589
+ let locations = try! await withCheckedThrowingContinuation {
1590
+ (continuation: CheckedContinuation<[CLLocation], Error>) in
1591
+ var allLocations: [CLLocation] = []
1289
1592
 
1290
- if let error = errorOrNil {
1291
- continuation.resume(throwing: error)
1292
- return
1293
- }
1593
+ let query = HKWorkoutRouteQuery(route: route) {
1594
+ (_, locationsOrNil, done, errorOrNil) in
1294
1595
 
1295
- guard let currentLocationBatch = locationsOrNil else {
1296
- fatalError("Should not fail")
1297
- }
1596
+ if let error = errorOrNil {
1597
+ continuation.resume(throwing: error)
1598
+ return
1599
+ }
1298
1600
 
1299
- allLocations.append(contentsOf: currentLocationBatch)
1601
+ guard let currentLocationBatch = locationsOrNil else {
1602
+ fatalError("routeLocations unexpectedly nil")
1603
+ }
1300
1604
 
1301
- if done {
1302
- continuation.resume(returning: allLocations)
1303
- }
1304
- }
1605
+ allLocations.append(contentsOf: currentLocationBatch)
1305
1606
 
1306
- store.execute(query)
1607
+ if done {
1608
+ continuation.resume(returning: allLocations)
1307
1609
  }
1610
+ }
1308
1611
 
1309
- return locations
1612
+ store.execute(query)
1310
1613
  }
1311
1614
 
1312
- @available(iOS 13.0.0, *)
1313
- func getSerializedWorkoutLocations(store: HKHealthStore, workoutUUID: UUID) async -> [Dictionary<String, Any>]? {
1314
- let routes = await _getWorkoutRoutes(store: store, workoutUUID: workoutUUID)
1615
+ return locations
1616
+ }
1315
1617
 
1316
- var allRoutes: [Dictionary<String, Any>] = []
1317
- guard let _routes = routes else {
1318
- return nil
1319
- }
1320
- for route in _routes {
1321
- let routeMetadata = serializeMetadata(metadata: route.metadata) as! [String: Any]
1322
- let routeCLLocations = (await getRouteLocations(store: store, route: route))
1323
- let routeLocations = routeCLLocations.enumerated().map {(i, loc) in serializeLocation(location: loc, previousLocation: i == 0 ? nil: routeCLLocations[i - 1])}
1324
- let routeInfos: [String: Any] = ["locations": routeLocations]
1325
- allRoutes.append(routeInfos.merging(routeMetadata) { (current, _) in current })
1326
- }
1327
- return allRoutes
1618
+ @available(iOS 13.0.0, *)
1619
+ func getSerializedWorkoutLocations(
1620
+ store: HKHealthStore,
1621
+ workoutUUID: UUID
1622
+ ) async -> [Dictionary<String, Any>]? {
1623
+ let routes = await _getWorkoutRoutes(
1624
+ store: store,
1625
+ workoutUUID: workoutUUID
1626
+ )
1627
+
1628
+ var allRoutes: [Dictionary<String, Any>] = []
1629
+ guard let _routes = routes else {
1630
+ return nil
1328
1631
  }
1632
+ for route in _routes {
1633
+ let routeMetadata = serializeMetadata(
1634
+ metadata: route.metadata
1635
+ ) as! [String: Any]
1636
+ let routeCLLocations = await getRouteLocations(
1637
+ store: store,
1638
+ route: route
1639
+ )
1640
+ let routeLocations = routeCLLocations.enumerated().map {
1641
+ (i, loc) in serializeLocation(
1642
+ location: loc,
1643
+ previousLocation: i == 0 ? nil: routeCLLocations[i - 1]
1644
+ )
1645
+ }
1646
+ let routeInfos: [String: Any] = ["locations": routeLocations]
1329
1647
 
1330
- @available(iOS 17.0.0, *)
1331
- func getWorkoutPlan(workout: HKWorkout) async -> [String: Any]? {
1332
- #if canImport(WorkoutKit)
1333
- do {
1334
- let workoutPlan = try await workout.workoutPlan
1648
+ allRoutes.append(routeInfos.merging(routeMetadata) { (current, _) in current })
1649
+ }
1650
+ return allRoutes
1651
+ }
1335
1652
 
1336
- var dict = [String: Any]()
1337
- if (workoutPlan?.id) != nil {
1338
- dict["id"] = workoutPlan?.id.uuidString
1653
+ @available(iOS 17.0.0, *)
1654
+ func getWorkoutPlan(workout: HKWorkout) async -> [String: Any]? {
1655
+ #if canImport(WorkoutKit)
1656
+ do {
1657
+ let workoutPlan = try await workout.workoutPlan
1339
1658
 
1340
- }
1341
- if (workoutPlan?.workout.activity) != nil {
1342
- dict["activityType"] = workoutPlan?.workout.activity.rawValue
1343
- }
1659
+ var dict = [String: Any]()
1660
+ if (workoutPlan?.id) != nil {
1661
+ dict["id"] = workoutPlan?.id.uuidString
1344
1662
 
1345
- if dict.isEmpty {
1346
- return nil
1347
- }
1348
- return dict
1349
- } catch {
1350
- return nil
1351
- }
1352
- #else
1663
+ }
1664
+ if (workoutPlan?.workout.activity) != nil {
1665
+ dict["activityType"] = workoutPlan?.workout.activity.rawValue
1666
+ }
1667
+
1668
+ if dict.isEmpty {
1353
1669
  return nil
1354
- #endif
1670
+ }
1671
+ return dict
1672
+ } catch {
1673
+ return nil
1355
1674
  }
1675
+ #else
1676
+ return nil
1677
+ #endif
1678
+ }
1356
1679
 
1357
- @objc(getWorkoutPlanById:resolve:reject:)
1358
- func getWorkoutPlanById(workoutUUID: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
1359
- if #available(iOS 17.0, *) {
1360
- #if canImport(WorkoutKit)
1361
- guard let store = _store else {
1362
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1363
- }
1680
+ @objc(getWorkoutPlanById:resolve:reject:)
1681
+ func getWorkoutPlanById(
1682
+ workoutUUID: String,
1683
+ resolve: @escaping RCTPromiseResolveBlock,
1684
+ reject: @escaping RCTPromiseRejectBlock) {
1685
+ if #available(iOS 17.0, *) {
1686
+ #if canImport(WorkoutKit)
1687
+ guard let store = _store else {
1688
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1689
+ }
1364
1690
 
1365
- Task {
1366
- if let uuid = UUID(uuidString: workoutUUID) {
1367
- let workout = await self.getWorkoutByID(store: store, workoutUUID: uuid)
1368
- if let workout {
1369
- let workoutPlan = await self.getWorkoutPlan(workout: workout)
1370
-
1371
- return resolve(workoutPlan)
1372
- } else {
1373
- return reject(GENERIC_ERROR, "No workout found", nil)
1374
- }
1375
- } else {
1376
- return reject(GENERIC_ERROR, "Invalid UUID", nil)
1377
- }
1378
- }
1379
- #else
1380
- return resolve(nil)
1381
- #endif
1691
+ Task {
1692
+ if let uuid = UUID(uuidString: workoutUUID) {
1693
+ let workout = await self.getWorkoutByID(store: store, workoutUUID: uuid)
1694
+ if let workout {
1695
+ let workoutPlan = await self.getWorkoutPlan(workout: workout)
1696
+
1697
+ return resolve(workoutPlan)
1698
+ } else {
1699
+ return reject(GENERIC_ERROR, "No workout found", nil)
1700
+ }
1382
1701
  } else {
1383
- return resolve(nil)
1702
+ return reject(GENERIC_ERROR, "Invalid UUID", nil)
1384
1703
  }
1704
+ }
1705
+ #else
1706
+ return resolve(nil)
1707
+ #endif
1708
+ } else {
1709
+ return resolve(nil)
1385
1710
  }
1711
+ }
1386
1712
 
1387
- func serializeLocation(location: CLLocation, previousLocation: CLLocation?) -> [String: Any] {
1388
- var distance: CLLocationDistance?
1389
- if let previousLocation = previousLocation {
1390
- distance = location.distance(from: previousLocation)
1391
- } else {
1392
- distance = nil
1393
- }
1394
- return [
1395
- "longitude": location.coordinate.longitude,
1396
- "latitude": location.coordinate.latitude,
1397
- "altitude": location.altitude,
1398
- "speed": location.speed,
1399
- "timestamp": location.timestamp.timeIntervalSince1970,
1400
- "horizontalAccuracy": location.horizontalAccuracy,
1401
- "speedAccuracy": location.speedAccuracy,
1402
- "verticalAccuracy": location.verticalAccuracy,
1403
- "distance": distance as Any
1404
- ]
1713
+ func serializeLocation(location: CLLocation, previousLocation: CLLocation?) -> [String: Any] {
1714
+ var distance: CLLocationDistance?
1715
+ if let previousLocation = previousLocation {
1716
+ distance = location.distance(from: previousLocation)
1717
+ } else {
1718
+ distance = nil
1405
1719
  }
1720
+ return [
1721
+ "longitude": location.coordinate.longitude,
1722
+ "latitude": location.coordinate.latitude,
1723
+ "altitude": location.altitude,
1724
+ "speed": location.speed,
1725
+ "timestamp": location.timestamp.timeIntervalSince1970,
1726
+ "horizontalAccuracy": location.horizontalAccuracy,
1727
+ "speedAccuracy": location.speedAccuracy,
1728
+ "verticalAccuracy": location.verticalAccuracy,
1729
+ "distance": distance as Any
1730
+ ]
1731
+ }
1406
1732
 
1407
- @available(iOS 13.0.0, *)
1408
- @objc(getWorkoutRoutes:resolve:reject:)
1409
- func getWorkoutRoutes(
1410
- workoutUUID: String,
1411
- resolve: @escaping RCTPromiseResolveBlock,
1412
- reject: @escaping RCTPromiseRejectBlock) {
1733
+ @available(iOS 13.0.0, *)
1734
+ @objc(getWorkoutRoutes:resolve:reject:)
1735
+ func getWorkoutRoutes(
1736
+ workoutUUID: String,
1737
+ resolve: @escaping RCTPromiseResolveBlock,
1738
+ reject: @escaping RCTPromiseRejectBlock) {
1413
1739
 
1414
- guard let store = _store else {
1415
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1416
- }
1740
+ guard let store = _store else {
1741
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1742
+ }
1417
1743
 
1418
- guard let _workoutUUID = UUID(uuidString: workoutUUID) else {
1419
- return reject("INVALID_UUID_ERROR", "Invalid UUID received", nil)
1420
- }
1744
+ guard let _workoutUUID = UUID(uuidString: workoutUUID) else {
1745
+ return reject("INVALID_UUID_ERROR", "Invalid UUID received", nil)
1746
+ }
1421
1747
 
1422
- Task {
1423
- do {
1424
- let locations = await getSerializedWorkoutLocations(store: store, workoutUUID: _workoutUUID)
1425
- resolve(locations)
1426
- } catch {
1427
- reject("WORKOUT_LOCATION_ERROR", "Failed to retrieve workout locations", nil)
1428
- }
1748
+ Task {
1749
+ do {
1750
+ let locations = await getSerializedWorkoutLocations(store: store, workoutUUID: _workoutUUID)
1751
+ resolve(locations)
1752
+ } catch {
1753
+ reject("WORKOUT_LOCATION_ERROR", "Failed to retrieve workout locations", nil)
1429
1754
  }
1755
+ }
1430
1756
  }
1431
1757
 
1432
- typealias HKAnchoredObjectQueryResult = (samples: [HKSample], deletedSamples: [HKDeletedObject]?, newAnchor: HKQueryAnchor?)
1433
-
1434
- @available(iOS 13.0.0, *)
1435
- func _queryHeartbeatSeriesSamplesWithAnchor(
1436
- store: HKHealthStore,
1437
- predicate: NSPredicate?,
1438
- limit: Int,
1439
- anchor: HKQueryAnchor?
1440
- ) async throws -> HKAnchoredObjectQueryResult {
1441
- let queryResult = try await withCheckedThrowingContinuation {
1442
- (continuation: CheckedContinuation<HKAnchoredObjectQueryResult, Error>) in
1443
- let query = HKAnchoredObjectQuery(
1444
- type: HKSeriesType.heartbeat(),
1445
- predicate: predicate,
1446
- anchor: anchor,
1447
- limit: limit
1448
- ) { (
1449
- _: HKAnchoredObjectQuery,
1450
- s: [HKSample]?,
1451
- deletedSamples: [HKDeletedObject]?,
1452
- newAnchor: HKQueryAnchor?,
1453
- error: Error?
1454
- ) in
1455
- if let err = error {
1456
- continuation.resume(throwing: err)
1457
- }
1458
-
1459
- guard let samples = s else {
1460
- fatalError("Should not fail")
1461
- }
1758
+ typealias HKAnchoredObjectQueryResult = (
1759
+ samples: [HKSample],
1760
+ deletedSamples: [HKDeletedObject]?,
1761
+ newAnchor: HKQueryAnchor?
1762
+ )
1462
1763
 
1463
- continuation.resume(returning: HKAnchoredObjectQueryResult(samples: samples, deletedSamples: deletedSamples, newAnchor: newAnchor))
1464
- }
1764
+ @available(iOS 13.0.0, *)
1765
+ func _queryHeartbeatSeriesSamplesWithAnchor(
1766
+ store: HKHealthStore,
1767
+ predicate: NSPredicate?,
1768
+ limit: Int,
1769
+ anchor: HKQueryAnchor?
1770
+ ) async throws -> HKAnchoredObjectQueryResult {
1771
+ let queryResult = try await withCheckedThrowingContinuation {
1772
+ (continuation: CheckedContinuation<HKAnchoredObjectQueryResult, Error>) in
1773
+ let query = HKAnchoredObjectQuery(
1774
+ type: HKSeriesType.heartbeat(),
1775
+ predicate: predicate,
1776
+ anchor: anchor,
1777
+ limit: limit
1778
+ ) { (
1779
+ _: HKAnchoredObjectQuery,
1780
+ s: [HKSample]?,
1781
+ deletedSamples: [HKDeletedObject]?,
1782
+ newAnchor: HKQueryAnchor?,
1783
+ error: Error?
1784
+ ) in
1785
+ if let err = error {
1786
+ return continuation.resume(throwing: err)
1787
+ } else {
1788
+ guard let samples = s else {
1789
+ fatalError("heartbeatSeries unexpectedly nil")
1790
+ }
1465
1791
 
1466
- store.execute(query)
1792
+ continuation.resume(
1793
+ returning: HKAnchoredObjectQueryResult(
1794
+ samples: samples,
1795
+ deletedSamples: deletedSamples,
1796
+ newAnchor: newAnchor)
1797
+ )
1467
1798
  }
1799
+ }
1468
1800
 
1469
- return queryResult
1801
+ store.execute(query)
1470
1802
  }
1471
1803
 
1472
- @available(iOS 13.0.0, *)
1473
- func getHeartbeatSeriesHeartbeats(store: HKHealthStore, sample: HKHeartbeatSeriesSample) async throws -> [Dictionary<String, Any>] {
1474
- let beatTimes = try await withCheckedThrowingContinuation {
1475
- (continuation: CheckedContinuation<[Dictionary<String, Any>], Error>) in
1476
- var allBeats: [Dictionary<String, Any>] = []
1477
-
1478
- let query = HKHeartbeatSeriesQuery(heartbeatSeries: sample) { (
1479
- _: HKHeartbeatSeriesQuery,
1480
- timeSinceSeriesStart: TimeInterval,
1481
- precededByGap: Bool,
1482
- done: Bool,
1483
- error: Error?
1484
- ) in
1485
- if let err = error {
1486
- continuation.resume(throwing: err)
1487
- }
1488
-
1489
- let timeDict: [String: Any] = [
1490
- "timeSinceSeriesStart": timeSinceSeriesStart,
1491
- "precededByGap": precededByGap
1492
- ]
1804
+ return queryResult
1805
+ }
1493
1806
 
1494
- allBeats.append(timeDict)
1807
+ @available(iOS 13.0.0, *)
1808
+ func getHeartbeatSeriesHeartbeats(
1809
+ store: HKHealthStore,
1810
+ sample: HKHeartbeatSeriesSample
1811
+ ) async throws -> [Dictionary<String, Any>] {
1812
+ let beatTimes = try await withCheckedThrowingContinuation {
1813
+ (continuation: CheckedContinuation<[Dictionary<String, Any>], Error>) in
1814
+ var allBeats: [Dictionary<String, Any>] = []
1815
+
1816
+ let query = HKHeartbeatSeriesQuery(heartbeatSeries: sample) { (
1817
+ _: HKHeartbeatSeriesQuery,
1818
+ timeSinceSeriesStart: TimeInterval,
1819
+ precededByGap: Bool,
1820
+ done: Bool,
1821
+ error: Error?
1822
+ ) in
1823
+ if let err = error {
1824
+ return continuation.resume(throwing: err)
1825
+ } else {
1826
+ let timeDict: [String: Any] = [
1827
+ "timeSinceSeriesStart": timeSinceSeriesStart,
1828
+ "precededByGap": precededByGap
1829
+ ]
1495
1830
 
1496
- if done {
1497
- continuation.resume(returning: allBeats)
1498
- }
1499
- }
1831
+ allBeats.append(timeDict)
1500
1832
 
1501
- store.execute(query)
1833
+ if done {
1834
+ continuation.resume(returning: allBeats)
1835
+ }
1502
1836
  }
1837
+ }
1503
1838
 
1504
- return beatTimes
1839
+ store.execute(query)
1505
1840
  }
1506
1841
 
1507
- @available(iOS 13.0.0, *)
1508
- func getSerializedHeartbeatSeriesSample(store: HKHealthStore, sample: HKHeartbeatSeriesSample) async throws -> [String: Any] {
1509
- let sampleMetadata = serializeMetadata(metadata: sample.metadata) as! [String: Any]
1510
- let sampleHeartbeats = try await getHeartbeatSeriesHeartbeats(store: store, sample: sample)
1511
-
1512
- return [
1513
- "uuid": sample.uuid.uuidString,
1514
- "device": serializeDevice(_device: sample.device) as Any,
1515
- "startDate": self._dateFormatter.string(from: sample.startDate),
1516
- "endDate": self._dateFormatter.string(from: sample.endDate),
1517
- "heartbeats": sampleHeartbeats as Any,
1518
- "metadata": serializeMetadata(metadata: sample.metadata),
1519
- "sourceRevision": serializeSourceRevision(_sourceRevision: sample.sourceRevision) as Any
1520
- ]
1521
- }
1842
+ return beatTimes
1843
+ }
1522
1844
 
1523
- @available(iOS 13.0.0, *)
1524
- @objc(queryHeartbeatSeriesSamplesWithAnchor:to:limit:anchor:resolve:reject:)
1525
- func queryHeartbeatSeriesSamplesWithAnchor(
1526
- from: Date,
1527
- to: Date,
1528
- limit: Int,
1529
- anchor: String,
1530
- resolve: @escaping RCTPromiseResolveBlock,
1531
- reject: @escaping RCTPromiseRejectBlock
1532
- ) {
1533
- guard let store = _store else {
1534
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1535
- }
1845
+ @available(iOS 13.0.0, *)
1846
+ func getSerializedHeartbeatSeriesSample(
1847
+ store: HKHealthStore,
1848
+ sample: HKHeartbeatSeriesSample
1849
+ ) async throws -> [String: Any] {
1850
+ let sampleMetadata = serializeMetadata(metadata: sample.metadata) as! [String: Any]
1851
+ let sampleHeartbeats = try await getHeartbeatSeriesHeartbeats(
1852
+ store: store,
1853
+ sample: sample
1854
+ )
1855
+
1856
+ return [
1857
+ "uuid": sample.uuid.uuidString,
1858
+ "device": serializeDevice(_device: sample.device) as Any,
1859
+ "startDate": self._dateFormatter.string(from: sample.startDate),
1860
+ "endDate": self._dateFormatter.string(from: sample.endDate),
1861
+ "heartbeats": sampleHeartbeats as Any,
1862
+ "metadata": serializeMetadata(metadata: sample.metadata),
1863
+ "sourceRevision": serializeSourceRevision(_sourceRevision: sample.sourceRevision) as Any
1864
+ ]
1865
+ }
1536
1866
 
1537
- Task {
1538
- do {
1539
- let from = dateOrNilIfZero(date: from)
1540
- let to = dateOrNilIfZero(date: to)
1867
+ @available(iOS 13.0.0, *)
1868
+ @objc(queryHeartbeatSeriesSamplesWithAnchor:to:limit:anchor:resolve:reject:)
1869
+ func queryHeartbeatSeriesSamplesWithAnchor(
1870
+ from: Date,
1871
+ to: Date,
1872
+ limit: Int,
1873
+ anchor: String,
1874
+ resolve: @escaping RCTPromiseResolveBlock,
1875
+ reject: @escaping RCTPromiseRejectBlock
1876
+ ) {
1877
+ guard let store = _store else {
1878
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1879
+ }
1541
1880
 
1542
- let predicate = createPredicate(from: from, to: to)
1881
+ Task {
1882
+ do {
1883
+ let from = dateOrNilIfZero(date: from)
1884
+ let to = dateOrNilIfZero(date: to)
1543
1885
 
1544
- let limit = limitOrNilIfZero(limit: limit)
1886
+ let predicate = createPredicate(from: from, to: to)
1545
1887
 
1546
- let actualAnchor = deserializeHKQueryAnchor(anchor: anchor)
1888
+ let limit = limitOrNilIfZero(limit: limit)
1547
1889
 
1548
- let queryResult = try await _queryHeartbeatSeriesSamplesWithAnchor(store: store, predicate: predicate, limit: limit, anchor: actualAnchor)
1890
+ let actualAnchor = deserializeHKQueryAnchor(anchor: anchor)
1549
1891
 
1550
- var allHeartbeatSamples: [Dictionary<String, Any>] = []
1551
- for sample in queryResult.samples as! [HKHeartbeatSeriesSample] {
1552
- allHeartbeatSamples.append(try await getSerializedHeartbeatSeriesSample(store: store, sample: sample))
1553
- }
1892
+ let queryResult = try await _queryHeartbeatSeriesSamplesWithAnchor(
1893
+ store: store,
1894
+ predicate: predicate,
1895
+ limit: limit,
1896
+ anchor: actualAnchor
1897
+ )
1554
1898
 
1555
- resolve([
1556
- "samples": allHeartbeatSamples as Any,
1557
- "deletedSamples": queryResult.deletedSamples?.map({ sample in
1558
- return serializeDeletedSample(sample: sample)
1559
- }) as Any,
1560
- "newAnchor": serializeAnchor(anchor: queryResult.newAnchor) as Any
1561
- ])
1562
- } catch {
1563
- reject(GENERIC_ERROR, error.localizedDescription, error)
1564
- }
1899
+ var allHeartbeatSamples: [Dictionary<String, Any>] = []
1900
+ for sample in queryResult.samples as! [HKHeartbeatSeriesSample] {
1901
+ allHeartbeatSamples.append(
1902
+ try await getSerializedHeartbeatSeriesSample(
1903
+ store: store,
1904
+ sample: sample
1905
+ )
1906
+ )
1565
1907
  }
1908
+
1909
+ resolve([
1910
+ "samples": allHeartbeatSamples as Any,
1911
+ "deletedSamples": queryResult.deletedSamples?.map({ sample in
1912
+ return serializeDeletedSample(sample: sample)
1913
+ }) as Any,
1914
+ "newAnchor": serializeAnchor(anchor: queryResult.newAnchor) as Any
1915
+ ])
1916
+ } catch {
1917
+ reject(GENERIC_ERROR, error.localizedDescription, error)
1918
+ }
1566
1919
  }
1920
+ }
1567
1921
 
1568
1922
  @available(iOS 13.0.0, *)
1569
1923
  func _queryHeartbeatSeriesSamples(
1570
- store: HKHealthStore,
1571
- predicate: NSPredicate?,
1572
- limit: Int,
1573
- ascending: Bool
1924
+ store: HKHealthStore,
1925
+ predicate: NSPredicate?,
1926
+ limit: Int,
1927
+ ascending: Bool
1574
1928
  ) async throws -> [HKSample] {
1575
- let samples = try await withCheckedThrowingContinuation {
1576
- (continuation: CheckedContinuation<[HKSample], Error>) in
1577
-
1578
- let query = HKSampleQuery(
1579
- sampleType: HKSeriesType.heartbeat(),
1580
- predicate: predicate,
1581
- limit: limit,
1582
- sortDescriptors: [NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: ascending)]
1583
- ) { (_: HKSampleQuery, sample: [HKSample]?, error: Error?) in
1584
- if let err = error {
1585
- continuation.resume(throwing: err)
1586
- } else {
1587
- guard let actualSamples = sample else {
1588
- fatalError("Should not fail")
1589
- }
1590
- continuation.resume(returning: actualSamples)
1591
- }
1929
+ let samples = try await withCheckedThrowingContinuation {
1930
+ (continuation: CheckedContinuation<[HKSample], Error>) in
1931
+
1932
+ let query = HKSampleQuery(
1933
+ sampleType: HKSeriesType.heartbeat(),
1934
+ predicate: predicate,
1935
+ limit: limit,
1936
+ sortDescriptors: [NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: ascending)]
1937
+ ) { (_: HKSampleQuery, sample: [HKSample]?, error: Error?) in
1938
+ if let err = error {
1939
+ continuation.resume(throwing: err)
1940
+ } else {
1941
+ guard let actualSamples = sample else {
1942
+ fatalError("heartbeatSeries samples unexpectedly nil")
1592
1943
  }
1593
-
1594
- store.execute(query)
1944
+ continuation.resume(returning: actualSamples)
1945
+ }
1595
1946
  }
1596
1947
 
1597
- return samples
1948
+ store.execute(query)
1949
+ }
1950
+
1951
+ return samples
1598
1952
  }
1599
1953
 
1600
1954
  @available(iOS 13.0.0, *)
1601
1955
  @objc(queryHeartbeatSeriesSamples:to:limit:ascending:resolve:reject:)
1602
1956
  func queryHeartbeatSeriesSamples(
1603
- from: Date,
1604
- to: Date,
1605
- limit: Int,
1606
- ascending: Bool,
1607
- resolve: @escaping RCTPromiseResolveBlock,
1608
- reject: @escaping RCTPromiseRejectBlock
1957
+ from: Date,
1958
+ to: Date,
1959
+ limit: Int,
1960
+ ascending: Bool,
1961
+ resolve: @escaping RCTPromiseResolveBlock,
1962
+ reject: @escaping RCTPromiseRejectBlock
1609
1963
  ) {
1610
- guard let store = _store else {
1611
- return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1612
- }
1964
+ guard let store = _store else {
1965
+ return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
1966
+ }
1613
1967
 
1614
- Task {
1615
- do {
1616
- let from = dateOrNilIfZero(date: from)
1617
- let to = dateOrNilIfZero(date: to)
1968
+ Task {
1969
+ do {
1970
+ let from = dateOrNilIfZero(date: from)
1971
+ let to = dateOrNilIfZero(date: to)
1618
1972
 
1619
- let predicate = createPredicate(from: from, to: to)
1973
+ let predicate = createPredicate(
1974
+ from: from,
1975
+ to: to
1976
+ )
1620
1977
 
1621
- let limit = limitOrNilIfZero(limit: limit)
1978
+ let limit = limitOrNilIfZero(
1979
+ limit: limit
1980
+ )
1622
1981
 
1623
- let samples = try await _queryHeartbeatSeriesSamples(store: store, predicate: predicate, limit: limit, ascending: ascending)
1982
+ let samples = try await _queryHeartbeatSeriesSamples(
1983
+ store: store,
1984
+ predicate: predicate,
1985
+ limit: limit,
1986
+ ascending: ascending
1987
+ )
1624
1988
 
1625
- var allHeartbeatSamples: [Dictionary<String, Any>] = []
1626
- for sample in samples as! [HKHeartbeatSeriesSample] {
1627
- allHeartbeatSamples.append(try await getSerializedHeartbeatSeriesSample(store: store, sample: sample))
1628
- }
1989
+ var allHeartbeatSamples: [Dictionary<String, Any>] = []
1990
+ for sample in samples as! [HKHeartbeatSeriesSample] {
1991
+ allHeartbeatSamples.append(
1992
+ try await getSerializedHeartbeatSeriesSample(
1993
+ store: store,
1994
+ sample: sample
1995
+ )
1996
+ )
1997
+ }
1629
1998
 
1630
- resolve(allHeartbeatSamples as Any)
1631
- } catch {
1632
- reject(GENERIC_ERROR, error.localizedDescription, error)
1633
- }
1999
+ resolve(allHeartbeatSamples as Any)
2000
+ } catch {
2001
+ reject(GENERIC_ERROR, error.localizedDescription, error)
1634
2002
  }
2003
+ }
1635
2004
  }
1636
2005
 
1637
2006
  }