@kingstinct/react-native-healthkit 4.1.1 → 4.4.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.
- package/README.md +1 -1
- package/ios/ReactNativeHealthkit.m +15 -10
- package/ios/ReactNativeHealthkit.swift +365 -177
- package/lib/commonjs/index.ios.js +104 -106
- package/lib/commonjs/index.ios.js.map +1 -1
- package/lib/commonjs/index.js +11 -10
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/native-types.js +25 -1
- package/lib/commonjs/native-types.js.map +1 -1
- package/lib/commonjs/types.js +4 -0
- package/lib/commonjs/types.js.map +1 -1
- package/lib/example/App.js +197 -0
- package/lib/index.ios.js +310 -0
- package/lib/index.js +44 -0
- package/lib/module/index.ios.js +104 -106
- package/lib/module/index.ios.js.map +1 -1
- package/lib/module/index.js +4 -3
- package/lib/module/index.js.map +1 -1
- package/lib/module/native-types.js +20 -1
- package/lib/module/native-types.js.map +1 -1
- package/lib/module/types.js +1 -1
- package/lib/module/types.js.map +1 -1
- package/lib/native-types.js +447 -0
- package/lib/src/index.ios.js +314 -0
- package/lib/src/index.js +45 -0
- package/lib/src/native-types.js +453 -0
- package/lib/src/types.js +1 -0
- package/lib/types.js +1 -0
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.ios.d.ts +1 -1
- package/lib/typescript/src/native-types.d.ts +142 -108
- package/lib/typescript/src/types.d.ts +80 -78
- package/package.json +25 -39
- package/src/index.ios.tsx +260 -264
- package/src/index.tsx +14 -13
- package/src/native-types.ts +213 -179
- package/src/types.ts +106 -100
- package/ios/ReactNativeHealthkit.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -4
- package/ios/ReactNativeHealthkit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- package/ios/ReactNativeHealthkit.xcodeproj/project.xcworkspace/xcuserdata/robertherber.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/ReactNativeHealthkit.xcodeproj/xcuserdata/robertherber.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import HealthKit;
|
|
2
|
-
import
|
|
2
|
+
import CoreLocation;
|
|
3
3
|
|
|
4
4
|
let INIT_ERROR = "HEALTHKIT_INIT_ERROR"
|
|
5
5
|
let INIT_ERROR_MESSAGE = "HealthKit not initialized"
|
|
6
6
|
let TYPE_IDENTIFIER_ERROR = "HEALTHKIT_TYPE_IDENTIFIER_NOT_RECOGNIZED_ERROR"
|
|
7
|
-
let GENERIC_ERROR = "HEALTHKIT_ERROR"
|
|
7
|
+
let GENERIC_ERROR = "HEALTHKIT_ERROR"
|
|
8
8
|
|
|
9
9
|
let HKCharacteristicTypeIdentifier_PREFIX = "HKCharacteristicTypeIdentifier"
|
|
10
10
|
let HKQuantityTypeIdentifier_PREFIX = "HKQuantityTypeIdentifier"
|
|
11
11
|
let HKCategoryTypeIdentifier_PREFIX = "HKCategoryTypeIdentifier"
|
|
12
12
|
let HKCorrelationTypeIdentifier_PREFIX = "HKCorrelationTypeIdentifier"
|
|
13
13
|
let HKActivitySummaryTypeIdentifier = "HKActivitySummaryTypeIdentifier"
|
|
14
|
-
let HKAudiogramTypeIdentifier = "HKAudiogramTypeIdentifier"
|
|
14
|
+
let HKAudiogramTypeIdentifier = "HKAudiogramTypeIdentifier"
|
|
15
15
|
let HKWorkoutTypeIdentifier = "HKWorkoutTypeIdentifier"
|
|
16
|
+
let HKWorkoutRouteTypeIdentifier = "HKWorkoutRouteTypeIdentifier"
|
|
17
|
+
let HKDataTypeIdentifierHeartbeatSeries = "HKDataTypeIdentifierHeartbeatSeries"
|
|
18
|
+
|
|
19
|
+
let SpeedUnit = HKUnit(from: "m/s") // HKUnit.meter().unitDivided(by: HKUnit.second())
|
|
20
|
+
// Support for MET data: HKAverageMETs 8.24046 kcal/hr·kg
|
|
21
|
+
let METUnit = HKUnit(from: "kcal/hr·kg")
|
|
16
22
|
|
|
17
23
|
@objc(ReactNativeHealthkit)
|
|
18
24
|
@available(iOS 10.0, *)
|
|
@@ -21,17 +27,17 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
21
27
|
var _runningQueries : Dictionary<String, HKQuery>;
|
|
22
28
|
var _dateFormatter : ISO8601DateFormatter;
|
|
23
29
|
var _hasListeners = false
|
|
24
|
-
|
|
30
|
+
|
|
25
31
|
override init() {
|
|
26
32
|
self._runningQueries = Dictionary<String, HKQuery>();
|
|
27
33
|
self._dateFormatter = ISO8601DateFormatter();
|
|
28
|
-
|
|
34
|
+
|
|
29
35
|
if(HKHealthStore.isHealthDataAvailable()){
|
|
30
36
|
self._store = HKHealthStore.init();
|
|
31
37
|
}
|
|
32
38
|
super.init();
|
|
33
39
|
}
|
|
34
|
-
|
|
40
|
+
|
|
35
41
|
deinit {
|
|
36
42
|
if let store = _store {
|
|
37
43
|
for query in self._runningQueries {
|
|
@@ -39,7 +45,7 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
39
45
|
}
|
|
40
46
|
}
|
|
41
47
|
}
|
|
42
|
-
|
|
48
|
+
|
|
43
49
|
override func stopObserving() -> Void {
|
|
44
50
|
self._hasListeners = false
|
|
45
51
|
if let store = _store {
|
|
@@ -48,78 +54,88 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
48
54
|
}
|
|
49
55
|
}
|
|
50
56
|
}
|
|
51
|
-
|
|
57
|
+
|
|
52
58
|
override func startObserving() -> Void {
|
|
53
59
|
self._hasListeners = true
|
|
54
60
|
}
|
|
55
|
-
|
|
61
|
+
|
|
56
62
|
func objectTypeFromString(typeIdentifier: String) -> HKObjectType? {
|
|
57
63
|
if(typeIdentifier.starts(with: HKCharacteristicTypeIdentifier_PREFIX)){
|
|
58
64
|
let identifier = HKCharacteristicTypeIdentifier.init(rawValue: typeIdentifier);
|
|
59
65
|
return HKObjectType.characteristicType(forIdentifier: identifier) as HKObjectType?
|
|
60
66
|
}
|
|
61
|
-
|
|
67
|
+
|
|
62
68
|
if(typeIdentifier.starts(with: HKQuantityTypeIdentifier_PREFIX)){
|
|
63
69
|
let identifier = HKQuantityTypeIdentifier.init(rawValue: typeIdentifier);
|
|
64
70
|
return HKObjectType.quantityType(forIdentifier: identifier) as HKObjectType?
|
|
65
71
|
}
|
|
66
|
-
|
|
72
|
+
|
|
67
73
|
if(typeIdentifier.starts(with: HKCategoryTypeIdentifier_PREFIX)){
|
|
68
74
|
let identifier = HKCategoryTypeIdentifier.init(rawValue: typeIdentifier);
|
|
69
75
|
return HKObjectType.categoryType(forIdentifier: identifier) as HKObjectType?
|
|
70
76
|
}
|
|
71
|
-
|
|
77
|
+
|
|
72
78
|
if(typeIdentifier.starts(with: HKCorrelationTypeIdentifier_PREFIX)){
|
|
73
79
|
let identifier = HKCorrelationTypeIdentifier.init(rawValue: typeIdentifier);
|
|
74
80
|
return HKObjectType.correlationType(forIdentifier: identifier) as HKObjectType?
|
|
75
81
|
}
|
|
76
|
-
|
|
82
|
+
|
|
77
83
|
if(typeIdentifier == HKActivitySummaryTypeIdentifier){
|
|
78
84
|
return HKObjectType.activitySummaryType();
|
|
79
85
|
}
|
|
80
|
-
|
|
86
|
+
|
|
81
87
|
if #available(iOS 13, *) {
|
|
82
88
|
if(typeIdentifier == HKAudiogramTypeIdentifier){
|
|
83
89
|
return HKObjectType.audiogramSampleType();
|
|
84
90
|
}
|
|
91
|
+
|
|
92
|
+
if(typeIdentifier == HKDataTypeIdentifierHeartbeatSeries){
|
|
93
|
+
return HKObjectType.seriesType(forIdentifier: typeIdentifier);
|
|
94
|
+
}
|
|
85
95
|
}
|
|
86
|
-
|
|
96
|
+
|
|
87
97
|
if(typeIdentifier == HKWorkoutTypeIdentifier){
|
|
88
98
|
return HKObjectType.workoutType()
|
|
89
99
|
}
|
|
90
|
-
|
|
100
|
+
|
|
101
|
+
if #available(iOS 11.0, *){
|
|
102
|
+
if typeIdentifier == HKWorkoutRouteTypeIdentifier{
|
|
103
|
+
return HKObjectType.seriesType(forIdentifier: typeIdentifier);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
91
107
|
return nil;
|
|
92
108
|
}
|
|
93
|
-
|
|
109
|
+
|
|
94
110
|
func sampleTypeFromString(typeIdentifier: String) -> HKSampleType? {
|
|
95
111
|
if(typeIdentifier.starts(with: HKQuantityTypeIdentifier_PREFIX)){
|
|
96
112
|
let identifier = HKQuantityTypeIdentifier.init(rawValue: typeIdentifier);
|
|
97
113
|
return HKSampleType.quantityType(forIdentifier: identifier) as HKSampleType?
|
|
98
114
|
}
|
|
99
|
-
|
|
115
|
+
|
|
100
116
|
if(typeIdentifier.starts(with: HKCategoryTypeIdentifier_PREFIX)){
|
|
101
117
|
let identifier = HKCategoryTypeIdentifier.init(rawValue: typeIdentifier);
|
|
102
118
|
return HKSampleType.categoryType(forIdentifier: identifier) as HKSampleType?
|
|
103
119
|
}
|
|
104
|
-
|
|
120
|
+
|
|
105
121
|
if(typeIdentifier.starts(with: HKCorrelationTypeIdentifier_PREFIX)){
|
|
106
122
|
let identifier = HKCorrelationTypeIdentifier.init(rawValue: typeIdentifier);
|
|
107
123
|
return HKSampleType.correlationType(forIdentifier: identifier) as HKSampleType?
|
|
108
124
|
}
|
|
109
|
-
|
|
125
|
+
|
|
110
126
|
if #available(iOS 13, *) {
|
|
111
127
|
if(typeIdentifier == HKAudiogramTypeIdentifier){
|
|
112
128
|
return HKSampleType.audiogramSampleType();
|
|
113
129
|
}
|
|
114
130
|
}
|
|
115
|
-
|
|
131
|
+
|
|
116
132
|
if(typeIdentifier == HKWorkoutTypeIdentifier){
|
|
117
133
|
return HKSampleType.workoutType();
|
|
118
134
|
}
|
|
119
|
-
|
|
135
|
+
|
|
120
136
|
return nil;
|
|
121
137
|
}
|
|
122
|
-
|
|
138
|
+
|
|
123
139
|
func objectTypesFromDictionary(typeIdentifiers: NSDictionary) -> Set<HKObjectType> {
|
|
124
140
|
var share = Set<HKObjectType>();
|
|
125
141
|
for item in typeIdentifiers {
|
|
@@ -132,7 +148,7 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
132
148
|
}
|
|
133
149
|
return share;
|
|
134
150
|
}
|
|
135
|
-
|
|
151
|
+
|
|
136
152
|
func sampleTypesFromDictionary(typeIdentifiers: NSDictionary) -> Set<HKSampleType> {
|
|
137
153
|
var share = Set<HKSampleType>();
|
|
138
154
|
for item in typeIdentifiers {
|
|
@@ -145,12 +161,12 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
145
161
|
}
|
|
146
162
|
return share;
|
|
147
163
|
}
|
|
148
|
-
|
|
164
|
+
|
|
149
165
|
@objc(isHealthDataAvailable:withRejecter:)
|
|
150
166
|
func isHealthDataAvailable(resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock) -> Void {
|
|
151
167
|
resolve(HKHealthStore.isHealthDataAvailable())
|
|
152
168
|
}
|
|
153
|
-
|
|
169
|
+
|
|
154
170
|
@available(iOS 12.0, *)
|
|
155
171
|
@objc(supportsHealthRecords:withRejecter:)
|
|
156
172
|
func supportsHealthRecords(resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock) -> Void {
|
|
@@ -159,7 +175,7 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
159
175
|
}
|
|
160
176
|
resolve(store.supportsHealthRecords());
|
|
161
177
|
}
|
|
162
|
-
|
|
178
|
+
|
|
163
179
|
@available(iOS 12.0, *)
|
|
164
180
|
@objc(getRequestStatusForAuthorization:read:resolve:withRejecter:)
|
|
165
181
|
func getRequestStatusForAuthorization(toShare: NSDictionary, read: NSDictionary, resolve: @escaping RCTPromiseResolveBlock,reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
@@ -175,7 +191,7 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
175
191
|
reject(GENERIC_ERROR, err.localizedDescription, err);
|
|
176
192
|
}
|
|
177
193
|
}
|
|
178
|
-
|
|
194
|
+
|
|
179
195
|
@objc(getPreferredUnits:resolve:reject:)
|
|
180
196
|
func getPreferredUnits(forIdentifiers: NSArray, resolve: @escaping RCTPromiseResolveBlock,reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
181
197
|
guard let store = _store else {
|
|
@@ -189,35 +205,35 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
189
205
|
quantityTypes.insert(type!);
|
|
190
206
|
}
|
|
191
207
|
}
|
|
192
|
-
|
|
208
|
+
|
|
193
209
|
store.preferredUnits(for: quantityTypes) { (typePerUnits: [HKQuantityType : HKUnit], error: Error?) in
|
|
194
210
|
let dic: NSMutableDictionary = NSMutableDictionary();
|
|
195
|
-
|
|
211
|
+
|
|
196
212
|
for typePerUnit in typePerUnits {
|
|
197
213
|
dic.setObject(typePerUnit.value.unitString, forKey: typePerUnit.key.identifier as NSCopying);
|
|
198
214
|
}
|
|
199
|
-
|
|
215
|
+
|
|
200
216
|
resolve(dic);
|
|
201
217
|
}
|
|
202
218
|
}
|
|
203
|
-
|
|
219
|
+
|
|
204
220
|
func serializeQuantity(unit: HKUnit, quantity: HKQuantity?) -> Dictionary<String, Any>? {
|
|
205
221
|
guard let q = quantity else {
|
|
206
222
|
return nil;
|
|
207
223
|
}
|
|
208
|
-
|
|
224
|
+
|
|
209
225
|
return [
|
|
210
226
|
"quantity": q.doubleValue(for: unit),
|
|
211
227
|
"unit": unit.unitString
|
|
212
228
|
]
|
|
213
229
|
}
|
|
214
|
-
|
|
230
|
+
|
|
215
231
|
func serializeQuantitySample(sample: HKQuantitySample, unit: HKUnit) -> NSDictionary {
|
|
216
232
|
let endDate = _dateFormatter.string(from: sample.endDate)
|
|
217
233
|
let startDate = _dateFormatter.string(from: sample.startDate);
|
|
218
|
-
|
|
234
|
+
|
|
219
235
|
let quantity = sample.quantity.doubleValue(for: unit);
|
|
220
|
-
|
|
236
|
+
|
|
221
237
|
return [
|
|
222
238
|
"uuid": sample.uuid.uuidString,
|
|
223
239
|
"device": self.serializeDevice(_device: sample.device),
|
|
@@ -230,11 +246,11 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
230
246
|
"sourceRevision": self.serializeSourceRevision(_sourceRevision: sample.sourceRevision) as Any,
|
|
231
247
|
]
|
|
232
248
|
}
|
|
233
|
-
|
|
249
|
+
|
|
234
250
|
func serializeCategorySample(sample: HKCategorySample) -> NSDictionary {
|
|
235
251
|
let endDate = _dateFormatter.string(from: sample.endDate)
|
|
236
252
|
let startDate = _dateFormatter.string(from: sample.startDate);
|
|
237
|
-
|
|
253
|
+
|
|
238
254
|
return [
|
|
239
255
|
"uuid": sample.uuid.uuidString,
|
|
240
256
|
"device": self.serializeDevice(_device: sample.device),
|
|
@@ -246,13 +262,13 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
246
262
|
"sourceRevision": self.serializeSourceRevision(_sourceRevision: sample.sourceRevision) as Any,
|
|
247
263
|
]
|
|
248
264
|
}
|
|
249
|
-
|
|
265
|
+
|
|
250
266
|
@objc(getBiologicalSex:withRejecter:)
|
|
251
267
|
func getBiologicalSex(resolve: @escaping RCTPromiseResolveBlock,reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
252
268
|
guard let store = _store else {
|
|
253
269
|
return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil);
|
|
254
270
|
}
|
|
255
|
-
|
|
271
|
+
|
|
256
272
|
do {
|
|
257
273
|
let bioSex = try store.biologicalSex();
|
|
258
274
|
resolve(bioSex.biologicalSex.rawValue);
|
|
@@ -260,13 +276,13 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
260
276
|
reject(GENERIC_ERROR, error.localizedDescription, error);
|
|
261
277
|
}
|
|
262
278
|
}
|
|
263
|
-
|
|
279
|
+
|
|
264
280
|
@objc(getDateOfBirth:withRejecter:)
|
|
265
281
|
func getDateOfBirth(resolve: @escaping RCTPromiseResolveBlock,reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
266
282
|
guard let store = _store else {
|
|
267
283
|
return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil);
|
|
268
284
|
}
|
|
269
|
-
|
|
285
|
+
|
|
270
286
|
do {
|
|
271
287
|
let dateOfBirth = try store.dateOfBirth();
|
|
272
288
|
resolve(_dateFormatter.string(from: dateOfBirth));
|
|
@@ -274,13 +290,13 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
274
290
|
reject(GENERIC_ERROR, error.localizedDescription, error);
|
|
275
291
|
}
|
|
276
292
|
}
|
|
277
|
-
|
|
293
|
+
|
|
278
294
|
@objc(getBloodType:withRejecter:)
|
|
279
295
|
func getBloodType(resolve: @escaping RCTPromiseResolveBlock,reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
280
296
|
guard let store = _store else {
|
|
281
297
|
return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil);
|
|
282
298
|
}
|
|
283
|
-
|
|
299
|
+
|
|
284
300
|
do {
|
|
285
301
|
let bloodType = try store.bloodType();
|
|
286
302
|
resolve(bloodType.bloodType.rawValue);
|
|
@@ -288,13 +304,13 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
288
304
|
reject(GENERIC_ERROR, error.localizedDescription, error);
|
|
289
305
|
}
|
|
290
306
|
}
|
|
291
|
-
|
|
307
|
+
|
|
292
308
|
@objc(getFitzpatrickSkinType:withRejecter:)
|
|
293
309
|
func getFitzpatrickSkinType(resolve: @escaping RCTPromiseResolveBlock,reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
294
310
|
guard let store = _store else {
|
|
295
311
|
return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil);
|
|
296
312
|
}
|
|
297
|
-
|
|
313
|
+
|
|
298
314
|
do {
|
|
299
315
|
let fitzpatrickSkinType = try store.fitzpatrickSkinType();
|
|
300
316
|
resolve(fitzpatrickSkinType.skinType.rawValue);
|
|
@@ -302,15 +318,15 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
302
318
|
reject(GENERIC_ERROR, error.localizedDescription, error);
|
|
303
319
|
}
|
|
304
320
|
}
|
|
305
|
-
|
|
306
|
-
|
|
321
|
+
|
|
322
|
+
|
|
307
323
|
@available(iOS 10.0, *)
|
|
308
324
|
@objc(getWheelchairUse:withRejecter:)
|
|
309
325
|
func getWheelchairUse(resolve: @escaping RCTPromiseResolveBlock,reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
310
326
|
guard let store = _store else {
|
|
311
327
|
return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil);
|
|
312
328
|
}
|
|
313
|
-
|
|
329
|
+
|
|
314
330
|
do {
|
|
315
331
|
let wheelchairUse = try store.wheelchairUse();
|
|
316
332
|
resolve(wheelchairUse.wheelchairUse.rawValue);
|
|
@@ -318,29 +334,29 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
318
334
|
reject(GENERIC_ERROR, error.localizedDescription, error);
|
|
319
335
|
}
|
|
320
336
|
}
|
|
321
|
-
|
|
337
|
+
|
|
322
338
|
@objc(authorizationStatusFor:withResolver:withRejecter:)
|
|
323
339
|
func authorizationStatusFor(typeIdentifier: String, resolve: @escaping RCTPromiseResolveBlock,reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
324
340
|
guard let store = _store else {
|
|
325
341
|
return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil);
|
|
326
342
|
}
|
|
327
|
-
|
|
343
|
+
|
|
328
344
|
guard let objectType = objectTypeFromString(typeIdentifier: typeIdentifier) else {
|
|
329
345
|
return reject(TYPE_IDENTIFIER_ERROR, typeIdentifier, nil);
|
|
330
346
|
}
|
|
331
|
-
|
|
347
|
+
|
|
332
348
|
let authStatus = store.authorizationStatus(for: objectType);
|
|
333
349
|
resolve(authStatus.rawValue);
|
|
334
350
|
}
|
|
335
|
-
|
|
351
|
+
|
|
336
352
|
@objc(saveQuantitySample:unitString:value:start:end:metadata:resolve:reject:)
|
|
337
353
|
func saveQuantitySample(typeIdentifier: String, unitString: String, value: Double, start: Date, end: Date, metadata: Dictionary<String, Any>, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock){
|
|
338
354
|
guard let store = _store else {
|
|
339
355
|
return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil);
|
|
340
356
|
}
|
|
341
|
-
|
|
357
|
+
|
|
342
358
|
let identifier = HKQuantityTypeIdentifier.init(rawValue: typeIdentifier);
|
|
343
|
-
|
|
359
|
+
|
|
344
360
|
guard let type = HKObjectType.quantityType(forIdentifier: identifier) else {
|
|
345
361
|
return reject(TYPE_IDENTIFIER_ERROR, typeIdentifier, nil);
|
|
346
362
|
}
|
|
@@ -362,19 +378,19 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
362
378
|
reject(GENERIC_ERROR, err.localizedDescription, error);
|
|
363
379
|
}
|
|
364
380
|
}
|
|
365
|
-
|
|
381
|
+
|
|
366
382
|
@objc(saveCorrelationSample:samples:start:end:metadata:resolve:reject:)
|
|
367
383
|
func saveCorrelationSample(typeIdentifier: String, samples: Array<Dictionary<String, Any>>, start: Date, end: Date, metadata: Dictionary<String, Any>, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock){
|
|
368
384
|
guard let store = _store else {
|
|
369
385
|
return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil);
|
|
370
386
|
}
|
|
371
|
-
|
|
387
|
+
|
|
372
388
|
let identifier = HKCorrelationTypeIdentifier.init(rawValue: typeIdentifier);
|
|
373
|
-
|
|
389
|
+
|
|
374
390
|
guard let type = HKObjectType.correlationType(forIdentifier: identifier) else {
|
|
375
391
|
return reject(TYPE_IDENTIFIER_ERROR, typeIdentifier, nil);
|
|
376
392
|
}
|
|
377
|
-
|
|
393
|
+
|
|
378
394
|
var initializedSamples = Set<HKSample>();
|
|
379
395
|
for sample in samples {
|
|
380
396
|
if(sample.keys.contains("quantityType")){
|
|
@@ -383,7 +399,7 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
383
399
|
let unitStr = sample["unit"] as! String
|
|
384
400
|
let quantityVal = sample["quantity"] as! Double
|
|
385
401
|
let metadata = sample["metadata"] as? [String: Any]
|
|
386
|
-
|
|
402
|
+
|
|
387
403
|
let unit = HKUnit.init(from: unitStr)
|
|
388
404
|
let quantity = HKQuantity.init(unit: unit, doubleValue: quantityVal)
|
|
389
405
|
let quantitySample = HKQuantitySample.init(type: type, quantity: quantity, start: start, end: end, metadata: metadata)
|
|
@@ -394,16 +410,16 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
394
410
|
if let type = HKSampleType.categoryType(forIdentifier: typeId) {
|
|
395
411
|
let value = sample["value"] as! Int
|
|
396
412
|
let metadata = sample["metadata"] as? [String: Any]
|
|
397
|
-
|
|
413
|
+
|
|
398
414
|
let categorySample = HKCategorySample.init(type: type, value: value, start: start, end: end, metadata: metadata)
|
|
399
415
|
initializedSamples.insert(categorySample);
|
|
400
416
|
}
|
|
401
417
|
}
|
|
402
|
-
|
|
418
|
+
|
|
403
419
|
}
|
|
404
|
-
|
|
420
|
+
|
|
405
421
|
let correlation = HKCorrelation.init(type: type, start: start, end: end, objects: initializedSamples, metadata: metadata)
|
|
406
|
-
|
|
422
|
+
|
|
407
423
|
store.save(correlation) { (success: Bool, error: Error?) in
|
|
408
424
|
guard let err = error else {
|
|
409
425
|
return resolve(success);
|
|
@@ -411,30 +427,30 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
411
427
|
reject(GENERIC_ERROR, err.localizedDescription, error);
|
|
412
428
|
}
|
|
413
429
|
}
|
|
414
|
-
|
|
430
|
+
|
|
415
431
|
@objc(saveWorkoutSample:quantities:start:end:metadata:resolve:reject:)
|
|
416
432
|
func saveWorkoutSample(typeIdentifier: UInt, quantities: Array<Dictionary<String, Any>>, start: Date, end: Date, metadata: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock){
|
|
417
433
|
guard let store = _store else {
|
|
418
434
|
return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil);
|
|
419
435
|
}
|
|
420
|
-
|
|
436
|
+
|
|
421
437
|
guard let type = HKWorkoutActivityType.init(rawValue: typeIdentifier) else {
|
|
422
438
|
return reject(TYPE_IDENTIFIER_ERROR, typeIdentifier.description, nil);
|
|
423
439
|
}
|
|
424
|
-
|
|
440
|
+
|
|
425
441
|
var initializedSamples = [HKSample]();
|
|
426
442
|
var totalEnergyBurned: HKQuantity?
|
|
427
443
|
var totalDistance: HKQuantity?
|
|
428
444
|
var totalSwimmingStrokeCount: HKQuantity?
|
|
429
445
|
var totalFlightsClimbed: HKQuantity?
|
|
430
|
-
|
|
446
|
+
|
|
431
447
|
for quantity in quantities {
|
|
432
448
|
let typeId = HKQuantityTypeIdentifier.init(rawValue: quantity["quantityType"] as! String)
|
|
433
449
|
if let type = HKSampleType.quantityType(forIdentifier: typeId) {
|
|
434
450
|
let unitStr = quantity["unit"] as! String
|
|
435
451
|
let quantityVal = quantity["quantity"] as! Double
|
|
436
452
|
let metadata = quantity["metadata"] as? [String: Any]
|
|
437
|
-
|
|
453
|
+
|
|
438
454
|
let unit = HKUnit.init(from: unitStr)
|
|
439
455
|
let quantity = HKQuantity.init(unit: unit, doubleValue: quantityVal)
|
|
440
456
|
if(quantity.is(compatibleWith: HKUnit.kilocalorie())){
|
|
@@ -453,10 +469,10 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
453
469
|
initializedSamples.append(quantitySample)
|
|
454
470
|
}
|
|
455
471
|
}
|
|
456
|
-
|
|
472
|
+
|
|
457
473
|
var workout: HKWorkout?;
|
|
458
|
-
|
|
459
|
-
|
|
474
|
+
|
|
475
|
+
|
|
460
476
|
if(totalSwimmingStrokeCount != nil){
|
|
461
477
|
workout = HKWorkout.init(activityType: type, start: start, end: end, workoutEvents: nil, totalEnergyBurned: totalEnergyBurned, totalDistance: totalDistance, totalSwimmingStrokeCount: totalSwimmingStrokeCount, device: nil, metadata: metadata)
|
|
462
478
|
} else {
|
|
@@ -466,11 +482,11 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
466
482
|
}
|
|
467
483
|
}
|
|
468
484
|
}
|
|
469
|
-
|
|
485
|
+
|
|
470
486
|
if(workout == nil){
|
|
471
487
|
workout = HKWorkout.init(activityType: type, start: start, end: end, workoutEvents: nil, totalEnergyBurned: totalEnergyBurned, totalDistance: totalDistance, metadata: metadata)
|
|
472
488
|
}
|
|
473
|
-
|
|
489
|
+
|
|
474
490
|
store.save(workout!) { (success: Bool, error: Error?) in
|
|
475
491
|
guard let err = error else {
|
|
476
492
|
if(success){
|
|
@@ -486,24 +502,24 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
486
502
|
}
|
|
487
503
|
reject(GENERIC_ERROR, err.localizedDescription, error);
|
|
488
504
|
}
|
|
489
|
-
|
|
490
|
-
|
|
505
|
+
|
|
506
|
+
|
|
491
507
|
}
|
|
492
|
-
|
|
508
|
+
|
|
493
509
|
@objc(saveCategorySample:value:start:end:metadata:resolve:reject:)
|
|
494
|
-
func saveCategorySample(typeIdentifier: String, value:
|
|
510
|
+
func saveCategorySample(typeIdentifier: String, value: Double, start: Date, end: Date, metadata: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock){
|
|
495
511
|
guard let store = _store else {
|
|
496
512
|
return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil);
|
|
497
513
|
}
|
|
498
|
-
|
|
514
|
+
|
|
499
515
|
let identifier = HKCategoryTypeIdentifier.init(rawValue: typeIdentifier);
|
|
500
|
-
|
|
516
|
+
|
|
501
517
|
guard let type = HKObjectType.categoryType(forIdentifier: identifier) else {
|
|
502
518
|
return reject(TYPE_IDENTIFIER_ERROR, typeIdentifier, nil);
|
|
503
519
|
}
|
|
504
520
|
|
|
505
|
-
let sample = HKCategorySample.init(type: type, value: value, start: start, end: end, metadata: metadata as? Dictionary<String, Any>)
|
|
506
|
-
|
|
521
|
+
let sample = HKCategorySample.init(type: type, value: Int(value), start: start, end: end, metadata: metadata as? Dictionary<String, Any>)
|
|
522
|
+
|
|
507
523
|
store.save(sample) { (success: Bool, error: Error?) in
|
|
508
524
|
guard let err = error else {
|
|
509
525
|
return resolve(success);
|
|
@@ -511,25 +527,25 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
511
527
|
reject(GENERIC_ERROR, err.localizedDescription, error);
|
|
512
528
|
}
|
|
513
529
|
}
|
|
514
|
-
|
|
530
|
+
|
|
515
531
|
override func supportedEvents() -> [String]! {
|
|
516
532
|
return ["onChange"]
|
|
517
533
|
}
|
|
518
|
-
|
|
534
|
+
|
|
519
535
|
@objc(enableBackgroundDelivery:updateFrequency:resolve:reject:)
|
|
520
536
|
func enableBackgroundDelivery(typeIdentifier: String, updateFrequency: Int, resolve: @escaping RCTPromiseResolveBlock,reject: @escaping RCTPromiseRejectBlock){
|
|
521
537
|
guard let store = _store else {
|
|
522
538
|
return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil);
|
|
523
539
|
}
|
|
524
|
-
|
|
540
|
+
|
|
525
541
|
guard let sampleType = objectTypeFromString(typeIdentifier: typeIdentifier) else {
|
|
526
542
|
return reject(TYPE_IDENTIFIER_ERROR, typeIdentifier, nil);
|
|
527
543
|
}
|
|
528
|
-
|
|
544
|
+
|
|
529
545
|
guard let frequency = HKUpdateFrequency.init(rawValue: updateFrequency) else {
|
|
530
546
|
return reject("UpdateFrequency not valid", "UpdateFrequency not valid", nil);
|
|
531
547
|
}
|
|
532
|
-
|
|
548
|
+
|
|
533
549
|
store.enableBackgroundDelivery(for: sampleType, frequency:frequency ) { (success, error) in
|
|
534
550
|
guard let err = error else {
|
|
535
551
|
return resolve(success);
|
|
@@ -537,13 +553,13 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
537
553
|
reject(GENERIC_ERROR, err.localizedDescription, err);
|
|
538
554
|
}
|
|
539
555
|
}
|
|
540
|
-
|
|
556
|
+
|
|
541
557
|
@objc(disableAllBackgroundDelivery:reject:)
|
|
542
558
|
func disableAllBackgroundDelivery(resolve: @escaping RCTPromiseResolveBlock,reject: @escaping RCTPromiseRejectBlock){
|
|
543
559
|
guard let store = _store else {
|
|
544
560
|
return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil);
|
|
545
561
|
}
|
|
546
|
-
|
|
562
|
+
|
|
547
563
|
store.disableAllBackgroundDelivery(completion: { (success, error) in
|
|
548
564
|
guard let err = error else {
|
|
549
565
|
return resolve(success);
|
|
@@ -551,18 +567,18 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
551
567
|
reject(GENERIC_ERROR, err.localizedDescription, err);
|
|
552
568
|
})
|
|
553
569
|
}
|
|
554
|
-
|
|
570
|
+
|
|
555
571
|
@objc(disableBackgroundDelivery:resolve:reject:)
|
|
556
572
|
func disableBackgroundDelivery(typeIdentifier: String, resolve: @escaping RCTPromiseResolveBlock,reject: @escaping RCTPromiseRejectBlock){
|
|
557
573
|
guard let store = _store else {
|
|
558
574
|
return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil);
|
|
559
575
|
}
|
|
560
|
-
|
|
576
|
+
|
|
561
577
|
guard let sampleType = objectTypeFromString(typeIdentifier: typeIdentifier) else {
|
|
562
578
|
return reject(TYPE_IDENTIFIER_ERROR, typeIdentifier, nil);
|
|
563
579
|
}
|
|
564
|
-
|
|
565
|
-
|
|
580
|
+
|
|
581
|
+
|
|
566
582
|
store.disableBackgroundDelivery(for: sampleType) { (success, error) in
|
|
567
583
|
guard let err = error else {
|
|
568
584
|
return resolve(success);
|
|
@@ -570,21 +586,21 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
570
586
|
reject(GENERIC_ERROR, err.localizedDescription, err);
|
|
571
587
|
}
|
|
572
588
|
}
|
|
573
|
-
|
|
589
|
+
|
|
574
590
|
@objc(subscribeToObserverQuery:resolve:reject:)
|
|
575
591
|
func subscribeToObserverQuery(typeIdentifier: String, resolve: @escaping RCTPromiseResolveBlock,reject: @escaping RCTPromiseRejectBlock){
|
|
576
592
|
guard let store = _store else {
|
|
577
593
|
return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil);
|
|
578
594
|
}
|
|
579
|
-
|
|
595
|
+
|
|
580
596
|
guard let sampleType = sampleTypeFromString(typeIdentifier: typeIdentifier) else {
|
|
581
597
|
return reject(TYPE_IDENTIFIER_ERROR, typeIdentifier, nil);
|
|
582
598
|
}
|
|
583
|
-
|
|
599
|
+
|
|
584
600
|
let predicate = HKQuery.predicateForSamples(withStart: Date.init(), end: nil, options: HKQueryOptions.strictStartDate)
|
|
585
|
-
|
|
601
|
+
|
|
586
602
|
let queryId = UUID().uuidString
|
|
587
|
-
|
|
603
|
+
|
|
588
604
|
func responder(query: HKObserverQuery, handler: @escaping HKObserverQueryCompletionHandler, error: Error?) -> Void {
|
|
589
605
|
if(error == nil){
|
|
590
606
|
DispatchQueue.main.async {
|
|
@@ -593,62 +609,62 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
593
609
|
"typeIdentifier": typeIdentifier,
|
|
594
610
|
]);
|
|
595
611
|
}
|
|
596
|
-
|
|
612
|
+
|
|
597
613
|
}
|
|
598
614
|
handler();
|
|
599
615
|
}
|
|
600
616
|
}
|
|
601
|
-
|
|
617
|
+
|
|
602
618
|
let query = HKObserverQuery(sampleType: sampleType, predicate: predicate) { (query: HKObserverQuery, handler: @escaping HKObserverQueryCompletionHandler, error: Error?) in
|
|
603
619
|
guard let err = error else {
|
|
604
620
|
return responder(query: query, handler: handler, error: error);
|
|
605
621
|
}
|
|
606
622
|
reject(GENERIC_ERROR, err.localizedDescription, err);
|
|
607
623
|
}
|
|
608
|
-
|
|
624
|
+
|
|
609
625
|
store.execute(query);
|
|
610
|
-
|
|
626
|
+
|
|
611
627
|
self._runningQueries.updateValue(query, forKey: queryId);
|
|
612
628
|
}
|
|
613
|
-
|
|
629
|
+
|
|
614
630
|
@objc(unsubscribeQuery:resolve:reject:)
|
|
615
631
|
func unsubscribeQuery(queryId: String, resolve: @escaping RCTPromiseResolveBlock,reject: @escaping RCTPromiseRejectBlock) {
|
|
616
632
|
guard let store = _store else {
|
|
617
633
|
return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil);
|
|
618
634
|
}
|
|
619
|
-
|
|
635
|
+
|
|
620
636
|
guard let query = self._runningQueries[queryId] else {
|
|
621
637
|
reject("Error", "Query with id " + queryId + " not found", nil);
|
|
622
638
|
return;
|
|
623
639
|
}
|
|
624
|
-
|
|
640
|
+
|
|
625
641
|
store.stop(query);
|
|
626
|
-
|
|
642
|
+
|
|
627
643
|
self._runningQueries.removeValue(forKey: queryId);
|
|
628
|
-
|
|
644
|
+
|
|
629
645
|
resolve(true);
|
|
630
646
|
}
|
|
631
|
-
|
|
647
|
+
|
|
632
648
|
|
|
633
649
|
static override func requiresMainQueueSetup() -> Bool {
|
|
634
650
|
return true
|
|
635
651
|
}
|
|
636
|
-
|
|
652
|
+
|
|
637
653
|
@objc(queryStatisticsForQuantity:unitString:from:to:options:resolve:reject:)
|
|
638
654
|
func queryStatisticsForQuantity(typeIdentifier: String, unitString: String, from: Date, to: Date, options: NSArray, resolve: @escaping RCTPromiseResolveBlock,reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
639
655
|
guard let store = _store else {
|
|
640
656
|
return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil);
|
|
641
657
|
}
|
|
642
|
-
|
|
658
|
+
|
|
643
659
|
let identifier = HKQuantityTypeIdentifier.init(rawValue: typeIdentifier);
|
|
644
660
|
guard let quantityType = HKObjectType.quantityType(forIdentifier: identifier) else {
|
|
645
661
|
return reject(TYPE_IDENTIFIER_ERROR, typeIdentifier, nil);
|
|
646
662
|
}
|
|
647
|
-
|
|
663
|
+
|
|
648
664
|
let predicate = HKQuery.predicateForSamples(withStart: from, end: to, options: HKQueryOptions.strictEndDate)
|
|
649
|
-
|
|
665
|
+
|
|
650
666
|
var opts = HKStatisticsOptions.init();
|
|
651
|
-
|
|
667
|
+
|
|
652
668
|
for o in options {
|
|
653
669
|
let str = o as! String;
|
|
654
670
|
if(str == "cumulativeSum"){
|
|
@@ -675,12 +691,12 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
675
691
|
opts.insert(HKStatisticsOptions.mostRecent)
|
|
676
692
|
}
|
|
677
693
|
}
|
|
678
|
-
|
|
694
|
+
|
|
679
695
|
if(str == "separateBySource"){
|
|
680
696
|
opts.insert(HKStatisticsOptions.separateBySource)
|
|
681
697
|
}
|
|
682
698
|
}
|
|
683
|
-
|
|
699
|
+
|
|
684
700
|
let query = HKStatisticsQuery.init(quantityType: quantityType, quantitySamplePredicate: predicate, options: opts) { (query, stats: HKStatistics?, error: Error?) in
|
|
685
701
|
if let gottenStats = stats {
|
|
686
702
|
var dic = Dictionary<String, Dictionary<String, Any>?>()
|
|
@@ -701,7 +717,7 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
701
717
|
if let mostRecent = gottenStats.mostRecentQuantity() {
|
|
702
718
|
dic.updateValue(self.serializeQuantity(unit: unit, quantity: mostRecent), forKey: "mostRecentQuantity")
|
|
703
719
|
}
|
|
704
|
-
|
|
720
|
+
|
|
705
721
|
if let mostRecentDateInterval = gottenStats.mostRecentQuantityDateInterval() {
|
|
706
722
|
dic.updateValue([
|
|
707
723
|
"start": self._dateFormatter.string(from: mostRecentDateInterval.start),
|
|
@@ -715,38 +731,41 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
715
731
|
dic.updateValue(self.serializeQuantity(unit: durationUnit, quantity: duration), forKey: "duration")
|
|
716
732
|
}
|
|
717
733
|
}
|
|
718
|
-
|
|
734
|
+
|
|
719
735
|
resolve(dic);
|
|
720
736
|
}
|
|
721
737
|
}
|
|
722
|
-
|
|
738
|
+
|
|
723
739
|
store.execute(query);
|
|
724
740
|
}
|
|
725
|
-
|
|
741
|
+
|
|
726
742
|
func serializeUnknownQuantity(quantity: HKQuantity) -> Dictionary<String, Any>? {
|
|
727
743
|
if(quantity.is(compatibleWith: HKUnit.percent())){
|
|
728
744
|
return self.serializeQuantity(unit: HKUnit.percent(), quantity: quantity);
|
|
729
745
|
}
|
|
730
|
-
|
|
731
|
-
|
|
746
|
+
|
|
732
747
|
if(quantity.is(compatibleWith: HKUnit.second())){
|
|
733
748
|
return self.serializeQuantity(unit: HKUnit.second(), quantity: quantity);
|
|
734
749
|
}
|
|
735
|
-
|
|
750
|
+
|
|
736
751
|
if(quantity.is(compatibleWith: HKUnit.kilocalorie())){
|
|
737
752
|
return self.serializeQuantity(unit: HKUnit.kilocalorie(), quantity: quantity);
|
|
738
753
|
}
|
|
739
|
-
|
|
754
|
+
|
|
740
755
|
if(quantity.is(compatibleWith: HKUnit.count())){
|
|
741
756
|
return self.serializeQuantity(unit: HKUnit.count(), quantity: quantity)
|
|
742
757
|
}
|
|
743
|
-
|
|
758
|
+
|
|
759
|
+
if(quantity.is(compatibleWith: HKUnit.meter())){
|
|
760
|
+
return self.serializeQuantity(unit: HKUnit.meter(), quantity: quantity)
|
|
761
|
+
}
|
|
762
|
+
|
|
744
763
|
if #available(iOS 11, *) {
|
|
745
764
|
if(quantity.is(compatibleWith: HKUnit.internationalUnit())){
|
|
746
765
|
return self.serializeQuantity(unit: HKUnit.internationalUnit(), quantity: quantity);
|
|
747
766
|
}
|
|
748
767
|
}
|
|
749
|
-
|
|
768
|
+
|
|
750
769
|
if #available(iOS 13, *) {
|
|
751
770
|
if(quantity.is(compatibleWith: HKUnit.hertz())){
|
|
752
771
|
return self.serializeQuantity(unit: HKUnit.hertz(), quantity: quantity);
|
|
@@ -755,9 +774,18 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
755
774
|
return self.serializeQuantity(unit: HKUnit.decibelHearingLevel(), quantity: quantity);
|
|
756
775
|
}
|
|
757
776
|
}
|
|
777
|
+
|
|
778
|
+
if(quantity.is(compatibleWith: SpeedUnit)){
|
|
779
|
+
return self.serializeQuantity(unit: SpeedUnit, quantity: quantity);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
if(quantity.is(compatibleWith: METUnit)){
|
|
783
|
+
return self.serializeQuantity(unit: METUnit, quantity: quantity);
|
|
784
|
+
}
|
|
785
|
+
|
|
758
786
|
return nil;
|
|
759
787
|
}
|
|
760
|
-
|
|
788
|
+
|
|
761
789
|
func serializeMetadata(metadata: [String: Any]?) -> NSDictionary {
|
|
762
790
|
let serialized: NSMutableDictionary = [:];
|
|
763
791
|
if let m = metadata {
|
|
@@ -768,6 +796,7 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
768
796
|
if let str = item.value as? String {
|
|
769
797
|
serialized.setValue(str, forKey: item.key)
|
|
770
798
|
}
|
|
799
|
+
|
|
771
800
|
if let double = item.value as? Double {
|
|
772
801
|
serialized.setValue(double, forKey: item.key)
|
|
773
802
|
}
|
|
@@ -780,7 +809,7 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
780
809
|
}
|
|
781
810
|
return serialized;
|
|
782
811
|
}
|
|
783
|
-
|
|
812
|
+
|
|
784
813
|
func serializeDevice(_device: HKDevice?) -> Dictionary<String, String?>? {
|
|
785
814
|
guard let device = _device else {
|
|
786
815
|
return nil;
|
|
@@ -795,22 +824,22 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
795
824
|
"softwareVersion": device.softwareVersion,
|
|
796
825
|
]
|
|
797
826
|
}
|
|
798
|
-
|
|
827
|
+
|
|
799
828
|
func serializeOperatingSystemVersion(_version: OperatingSystemVersion?) -> String? {
|
|
800
829
|
guard let version = _version else {
|
|
801
830
|
return nil;
|
|
802
831
|
}
|
|
803
|
-
|
|
832
|
+
|
|
804
833
|
let versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)";
|
|
805
|
-
|
|
834
|
+
|
|
806
835
|
return versionString;
|
|
807
836
|
}
|
|
808
|
-
|
|
837
|
+
|
|
809
838
|
func serializeSourceRevision(_sourceRevision: HKSourceRevision?) -> Dictionary<String, Any?>? {
|
|
810
839
|
guard let sourceRevision = _sourceRevision else {
|
|
811
840
|
return nil;
|
|
812
841
|
}
|
|
813
|
-
|
|
842
|
+
|
|
814
843
|
var dict = [
|
|
815
844
|
"source": [
|
|
816
845
|
"name": sourceRevision.source.name,
|
|
@@ -818,12 +847,12 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
818
847
|
],
|
|
819
848
|
"version": sourceRevision.version
|
|
820
849
|
] as [String : Any];
|
|
821
|
-
|
|
850
|
+
|
|
822
851
|
if #available(iOS 11, *) {
|
|
823
852
|
dict["operatingSystemVersion"] = self.serializeOperatingSystemVersion(_version: sourceRevision.operatingSystemVersion);
|
|
824
853
|
dict["productType"] = sourceRevision.productType;
|
|
825
854
|
}
|
|
826
|
-
|
|
855
|
+
|
|
827
856
|
return dict;
|
|
828
857
|
}
|
|
829
858
|
|
|
@@ -832,28 +861,29 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
832
861
|
guard let store = _store else {
|
|
833
862
|
return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil);
|
|
834
863
|
}
|
|
835
|
-
|
|
864
|
+
|
|
836
865
|
let from = from.timeIntervalSince1970 > 0 ? from : nil;
|
|
837
866
|
let to = to.timeIntervalSince1970 > 0 ? to : nil;
|
|
838
|
-
|
|
867
|
+
|
|
839
868
|
let predicate = from != nil || to != nil ? HKQuery.predicateForSamples(withStart: from, end: to, options: [HKQueryOptions.strictEndDate, HKQueryOptions.strictStartDate]) : nil;
|
|
840
|
-
|
|
869
|
+
|
|
841
870
|
let limit = limit == 0 ? HKObjectQueryNoLimit : limit;
|
|
842
|
-
|
|
871
|
+
|
|
843
872
|
let energyUnit = HKUnit.init(from: energyUnitString)
|
|
844
873
|
let distanceUnit = HKUnit.init(from: distanceUnitString)
|
|
845
|
-
|
|
874
|
+
|
|
846
875
|
let q = HKSampleQuery(sampleType: .workoutType(), predicate: predicate, limit: limit, sortDescriptors: [NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: ascending)]) { (query: HKSampleQuery, sample: [HKSample]?, error: Error?) in
|
|
847
876
|
guard let err = error else {
|
|
848
877
|
guard let samples = sample else {
|
|
849
878
|
return resolve([]);
|
|
850
879
|
}
|
|
851
880
|
let arr: NSMutableArray = [];
|
|
852
|
-
|
|
881
|
+
|
|
853
882
|
for s in samples {
|
|
854
883
|
if let workout = s as? HKWorkout {
|
|
855
884
|
let endDate = self._dateFormatter.string(from: workout.endDate)
|
|
856
885
|
let startDate = self._dateFormatter.string(from: workout.startDate);
|
|
886
|
+
|
|
857
887
|
let dict: NSMutableDictionary = [
|
|
858
888
|
"uuid": workout.uuid.uuidString,
|
|
859
889
|
"device": self.serializeDevice(_device: workout.device) as Any,
|
|
@@ -867,87 +897,87 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
867
897
|
"metadata": self.serializeMetadata(metadata: workout.metadata),
|
|
868
898
|
"sourceRevision": self.serializeSourceRevision(_sourceRevision: workout.sourceRevision) as Any
|
|
869
899
|
]
|
|
870
|
-
|
|
900
|
+
|
|
871
901
|
if #available(iOS 11, *) {
|
|
872
902
|
dict.setValue(self.serializeQuantity(unit: HKUnit.count(), quantity: workout.totalFlightsClimbed), forKey: "totalFlightsClimbed")
|
|
873
903
|
}
|
|
874
|
-
|
|
904
|
+
|
|
875
905
|
arr.add(dict)
|
|
876
906
|
}
|
|
877
907
|
}
|
|
878
|
-
|
|
908
|
+
|
|
879
909
|
return resolve(arr);
|
|
880
910
|
}
|
|
881
911
|
reject(GENERIC_ERROR, err.localizedDescription, err);
|
|
882
912
|
}
|
|
883
|
-
|
|
913
|
+
|
|
884
914
|
store.execute(q);
|
|
885
915
|
}
|
|
886
|
-
|
|
887
|
-
|
|
916
|
+
|
|
917
|
+
|
|
888
918
|
@objc(queryQuantitySamples:unitString:from:to:limit:ascending:resolve:reject:)
|
|
889
919
|
func queryQuantitySamples(typeIdentifier: String, unitString: String, from: Date, to: Date, limit: Int, ascending: Bool, resolve: @escaping RCTPromiseResolveBlock,reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
890
920
|
guard let store = _store else {
|
|
891
921
|
return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil);
|
|
892
922
|
}
|
|
893
|
-
|
|
923
|
+
|
|
894
924
|
let identifier = HKQuantityTypeIdentifier.init(rawValue: typeIdentifier);
|
|
895
925
|
guard let sampleType = HKSampleType.quantityType(forIdentifier: identifier) else {
|
|
896
926
|
return reject(TYPE_IDENTIFIER_ERROR, typeIdentifier, nil);
|
|
897
927
|
}
|
|
898
|
-
|
|
928
|
+
|
|
899
929
|
let from = from.timeIntervalSince1970 > 0 ? from : nil;
|
|
900
930
|
let to = to.timeIntervalSince1970 > 0 ? to : nil;
|
|
901
|
-
|
|
931
|
+
|
|
902
932
|
let predicate = from != nil || to != nil ? HKQuery.predicateForSamples(withStart: from, end: to, options: [HKQueryOptions.strictEndDate, HKQueryOptions.strictStartDate]) : nil;
|
|
903
|
-
|
|
933
|
+
|
|
904
934
|
let limit = limit == 0 ? HKObjectQueryNoLimit : limit;
|
|
905
|
-
|
|
935
|
+
|
|
906
936
|
let q = HKSampleQuery(sampleType: sampleType, predicate: predicate, limit: limit, sortDescriptors: [NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: ascending)]) { (query: HKSampleQuery, sample: [HKSample]?, error: Error?) in
|
|
907
937
|
guard let err = error else {
|
|
908
938
|
guard let samples = sample else {
|
|
909
939
|
return resolve([]);
|
|
910
940
|
}
|
|
911
941
|
let arr: NSMutableArray = [];
|
|
912
|
-
|
|
942
|
+
|
|
913
943
|
for s in samples {
|
|
914
944
|
if let sample = s as? HKQuantitySample {
|
|
915
945
|
let serialized = self.serializeQuantitySample(sample: sample, unit: HKUnit.init(from: unitString))
|
|
916
|
-
|
|
946
|
+
|
|
917
947
|
arr.add(serialized)
|
|
918
948
|
}
|
|
919
949
|
}
|
|
920
|
-
|
|
950
|
+
|
|
921
951
|
return resolve(arr);
|
|
922
952
|
}
|
|
923
953
|
reject(GENERIC_ERROR, err.localizedDescription, err);
|
|
924
954
|
}
|
|
925
|
-
|
|
955
|
+
|
|
926
956
|
store.execute(q);
|
|
927
957
|
}
|
|
928
|
-
|
|
958
|
+
|
|
929
959
|
@objc(queryCorrelationSamples:from:to:resolve:reject:)
|
|
930
960
|
func queryCorrelationSamples(typeIdentifier: String, from: Date, to: Date, resolve: @escaping RCTPromiseResolveBlock,reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
931
961
|
guard let store = _store else {
|
|
932
962
|
return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil);
|
|
933
963
|
}
|
|
934
|
-
|
|
964
|
+
|
|
935
965
|
let identifier = HKCorrelationTypeIdentifier.init(rawValue: typeIdentifier);
|
|
936
966
|
guard let sampleType = HKSampleType.correlationType(forIdentifier: identifier) else {
|
|
937
967
|
return reject(TYPE_IDENTIFIER_ERROR, typeIdentifier, nil);
|
|
938
968
|
}
|
|
939
|
-
|
|
969
|
+
|
|
940
970
|
let from = from.timeIntervalSince1970 > 0 ? from : nil;
|
|
941
971
|
let to = to.timeIntervalSince1970 > 0 ? to : nil;
|
|
942
|
-
|
|
972
|
+
|
|
943
973
|
let predicate = from != nil || to != nil ? HKQuery.predicateForSamples(withStart: from, end: to, options: [HKQueryOptions.strictEndDate, HKQueryOptions.strictStartDate]) : nil;
|
|
944
|
-
|
|
974
|
+
|
|
945
975
|
let q = HKCorrelationQuery(type: sampleType, predicate: predicate, samplePredicates: nil) { (query: HKCorrelationQuery, _correlations: [HKCorrelation]?, error: Error?) in
|
|
946
976
|
guard let err = error else {
|
|
947
977
|
guard let correlations = _correlations else {
|
|
948
978
|
return resolve([]);
|
|
949
979
|
}
|
|
950
|
-
|
|
980
|
+
|
|
951
981
|
var qts = Set<HKQuantityType>();
|
|
952
982
|
for c in correlations {
|
|
953
983
|
for object in c.objects {
|
|
@@ -969,7 +999,7 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
969
999
|
objects.add(self.serializeCategorySample(sample: categorySample))
|
|
970
1000
|
}
|
|
971
1001
|
}
|
|
972
|
-
|
|
1002
|
+
|
|
973
1003
|
collerationsToReturn.add([
|
|
974
1004
|
"uuid": c.uuid.uuidString,
|
|
975
1005
|
"device": self.serializeDevice(_device: c.device) as Any,
|
|
@@ -980,7 +1010,7 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
980
1010
|
"endDate": self._dateFormatter.string(from: c.endDate),
|
|
981
1011
|
])
|
|
982
1012
|
}
|
|
983
|
-
|
|
1013
|
+
|
|
984
1014
|
return resolve(collerationsToReturn);
|
|
985
1015
|
}
|
|
986
1016
|
reject(GENERIC_ERROR, e.localizedDescription, e);
|
|
@@ -988,60 +1018,60 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
988
1018
|
}
|
|
989
1019
|
reject(GENERIC_ERROR, err.localizedDescription, err);
|
|
990
1020
|
}
|
|
991
|
-
|
|
1021
|
+
|
|
992
1022
|
store.execute(q);
|
|
993
1023
|
}
|
|
994
|
-
|
|
1024
|
+
|
|
995
1025
|
@objc(queryCategorySamples:from:to:limit:ascending:resolve:reject:)
|
|
996
1026
|
func queryCategorySamples(typeIdentifier: String, from: Date, to: Date, limit: Int, ascending: Bool, resolve: @escaping RCTPromiseResolveBlock,reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
997
1027
|
guard let store = _store else {
|
|
998
1028
|
return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil);
|
|
999
1029
|
}
|
|
1000
|
-
|
|
1030
|
+
|
|
1001
1031
|
let identifier = HKCategoryTypeIdentifier.init(rawValue: typeIdentifier);
|
|
1002
1032
|
guard let sampleType = HKSampleType.categoryType(forIdentifier: identifier) else {
|
|
1003
1033
|
return reject(TYPE_IDENTIFIER_ERROR, typeIdentifier, nil);
|
|
1004
1034
|
}
|
|
1005
|
-
|
|
1035
|
+
|
|
1006
1036
|
let from = from.timeIntervalSince1970 > 0 ? from : nil;
|
|
1007
1037
|
let to = to.timeIntervalSince1970 > 0 ? to : nil;
|
|
1008
|
-
|
|
1038
|
+
|
|
1009
1039
|
let predicate = from != nil || to != nil ? HKQuery.predicateForSamples(withStart: from, end: to, options: [HKQueryOptions.strictEndDate, HKQueryOptions.strictStartDate]) : nil;
|
|
1010
|
-
|
|
1040
|
+
|
|
1011
1041
|
let limit = limit == 0 ? HKObjectQueryNoLimit : limit;
|
|
1012
|
-
|
|
1042
|
+
|
|
1013
1043
|
let q = HKSampleQuery(sampleType: sampleType, predicate: predicate, limit: limit, sortDescriptors: [NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: ascending)]) { (query: HKSampleQuery, sample: [HKSample]?, error: Error?) in
|
|
1014
1044
|
guard let err = error else {
|
|
1015
1045
|
guard let samples = sample else {
|
|
1016
1046
|
return resolve([]);
|
|
1017
1047
|
}
|
|
1018
1048
|
let arr: NSMutableArray = [];
|
|
1019
|
-
|
|
1049
|
+
|
|
1020
1050
|
for s in samples {
|
|
1021
1051
|
if let sample = s as? HKCategorySample {
|
|
1022
1052
|
let serialized = self.serializeCategorySample(sample: sample);
|
|
1023
|
-
|
|
1053
|
+
|
|
1024
1054
|
arr.add(serialized)
|
|
1025
1055
|
}
|
|
1026
1056
|
}
|
|
1027
|
-
|
|
1057
|
+
|
|
1028
1058
|
return resolve(arr);
|
|
1029
1059
|
}
|
|
1030
1060
|
reject(GENERIC_ERROR, err.localizedDescription, err);
|
|
1031
1061
|
}
|
|
1032
|
-
|
|
1062
|
+
|
|
1033
1063
|
store.execute(q);
|
|
1034
1064
|
}
|
|
1035
|
-
|
|
1065
|
+
|
|
1036
1066
|
@objc(requestAuthorization:read:resolve:withRejecter:)
|
|
1037
1067
|
func requestAuthorization(toShare: NSDictionary, read: NSDictionary, resolve: @escaping RCTPromiseResolveBlock,reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
1038
1068
|
guard let store = _store else {
|
|
1039
1069
|
return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil);
|
|
1040
1070
|
}
|
|
1041
|
-
|
|
1071
|
+
|
|
1042
1072
|
let share = sampleTypesFromDictionary(typeIdentifiers: toShare);
|
|
1043
1073
|
let toRead = objectTypesFromDictionary(typeIdentifiers: read);
|
|
1044
|
-
|
|
1074
|
+
|
|
1045
1075
|
store.requestAuthorization(toShare: share, read: toRead) { (success: Bool, error: Error?) in
|
|
1046
1076
|
guard let err = error else {
|
|
1047
1077
|
return resolve(success);
|
|
@@ -1049,4 +1079,162 @@ class ReactNativeHealthkit: RCTEventEmitter {
|
|
|
1049
1079
|
reject(GENERIC_ERROR, err.localizedDescription, err);
|
|
1050
1080
|
}
|
|
1051
1081
|
}
|
|
1082
|
+
|
|
1083
|
+
@available(iOS 13.0.0, *)
|
|
1084
|
+
func getWorkoutByID(store: HKHealthStore,
|
|
1085
|
+
workoutUUID: UUID) async -> HKWorkout? {
|
|
1086
|
+
let workoutPredicate = HKQuery.predicateForObject(with: workoutUUID);
|
|
1087
|
+
|
|
1088
|
+
let samples = try! await withCheckedThrowingContinuation {
|
|
1089
|
+
(continuation: CheckedContinuation<[HKSample], Error>) in
|
|
1090
|
+
let query = HKAnchoredObjectQuery(type: HKSeriesType.workoutType(),
|
|
1091
|
+
predicate: workoutPredicate,
|
|
1092
|
+
anchor: nil,
|
|
1093
|
+
limit: 1) {
|
|
1094
|
+
(query, samples, deletedObjects, anchor, error) in
|
|
1095
|
+
|
|
1096
|
+
if let hasError = error {
|
|
1097
|
+
continuation.resume(throwing: hasError)
|
|
1098
|
+
return
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
guard let samples = samples else {
|
|
1102
|
+
fatalError("Should not fail")
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
continuation.resume(returning: samples)
|
|
1106
|
+
}
|
|
1107
|
+
store.execute(query)
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
guard let workouts = samples as? [HKWorkout] else {
|
|
1111
|
+
return nil
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
return workouts.first ?? nil
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
|
|
1118
|
+
@available(iOS 13.0.0, *)
|
|
1119
|
+
func _getWorkoutRoutes(store: HKHealthStore, workoutUUID: UUID) async -> [HKWorkoutRoute]?{
|
|
1120
|
+
guard let workout = await getWorkoutByID(store: store, workoutUUID: workoutUUID) else {
|
|
1121
|
+
return nil
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
let workoutPredicate = HKQuery.predicateForObjects(from: workout)
|
|
1125
|
+
let samples = try! await withCheckedThrowingContinuation {
|
|
1126
|
+
(continuation: CheckedContinuation<[HKSample], Error>) in
|
|
1127
|
+
let query = HKAnchoredObjectQuery(type: HKSeriesType.workoutRoute(),
|
|
1128
|
+
predicate: workoutPredicate,
|
|
1129
|
+
anchor: nil,
|
|
1130
|
+
limit: HKObjectQueryNoLimit) {
|
|
1131
|
+
(query, samples, deletedObjects, anchor, error) in
|
|
1132
|
+
|
|
1133
|
+
if let hasError = error {
|
|
1134
|
+
continuation.resume(throwing: hasError)
|
|
1135
|
+
return
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
guard let samples = samples else {
|
|
1139
|
+
fatalError("Should not fail")
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
continuation.resume(returning: samples)
|
|
1143
|
+
}
|
|
1144
|
+
store.execute(query)
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
guard let routes = samples as? [HKWorkoutRoute] else {
|
|
1148
|
+
return nil
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
return routes
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
@available(iOS 13.0.0, *)
|
|
1155
|
+
func getRouteLocations(store: HKHealthStore, route: HKWorkoutRoute) async -> [CLLocation] {
|
|
1156
|
+
let locations = try! await withCheckedThrowingContinuation {
|
|
1157
|
+
(continuation: CheckedContinuation<[CLLocation], Error>) in
|
|
1158
|
+
var allLocations: [CLLocation] = []
|
|
1159
|
+
|
|
1160
|
+
let query = HKWorkoutRouteQuery(route: route) {
|
|
1161
|
+
(query, locationsOrNil, done, errorOrNil) in
|
|
1162
|
+
|
|
1163
|
+
if let error = errorOrNil {
|
|
1164
|
+
continuation.resume(throwing: error)
|
|
1165
|
+
return
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
guard let currentLocationBatch = locationsOrNil else {
|
|
1169
|
+
fatalError("Should not fail")
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
allLocations.append(contentsOf: currentLocationBatch)
|
|
1173
|
+
|
|
1174
|
+
if done {
|
|
1175
|
+
continuation.resume(returning: allLocations)
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
store.execute(query)
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
return locations
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
@available(iOS 13.0.0, *)
|
|
1186
|
+
func getSerializedWorkoutLocations(store: HKHealthStore, workoutUUID: UUID) async -> [Dictionary<String, Any>]? {
|
|
1187
|
+
let routes = await _getWorkoutRoutes(store: store, workoutUUID: workoutUUID)
|
|
1188
|
+
|
|
1189
|
+
var allRoutes: [Dictionary<String, Any>] = []
|
|
1190
|
+
guard let _routes = routes else {
|
|
1191
|
+
return nil
|
|
1192
|
+
}
|
|
1193
|
+
for route in _routes {
|
|
1194
|
+
let routeMetadata = self.serializeMetadata(metadata: route.metadata) as! Dictionary<String, Any>
|
|
1195
|
+
let routeLocations = (await getRouteLocations(store: store, route: route)).map{serializeLocation(location: $0)}
|
|
1196
|
+
let routeInfos: Dictionary<String, Any> = ["locations": routeLocations]
|
|
1197
|
+
allRoutes.append(routeInfos.merging(routeMetadata) { (current, _) in current })
|
|
1198
|
+
}
|
|
1199
|
+
return allRoutes
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
func serializeLocation(location: CLLocation) -> Dictionary<String, Any> {
|
|
1203
|
+
return [
|
|
1204
|
+
"longitude": location.coordinate.longitude,
|
|
1205
|
+
"latitude": location.coordinate.latitude,
|
|
1206
|
+
"altitude": location.altitude,
|
|
1207
|
+
"speed": location.speed,
|
|
1208
|
+
"timestamp": location.timestamp.timeIntervalSince1970,
|
|
1209
|
+
"horizontalAccuracy": location.horizontalAccuracy,
|
|
1210
|
+
"speedAccuracy": location.speedAccuracy,
|
|
1211
|
+
"verticalAccuracy": location.verticalAccuracy,
|
|
1212
|
+
]
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
@available(iOS 13.0.0, *)
|
|
1216
|
+
@objc(getWorkoutRoutes:resolve:reject:)
|
|
1217
|
+
func getWorkoutRoutes(
|
|
1218
|
+
workoutUUID: String,
|
|
1219
|
+
resolve: @escaping RCTPromiseResolveBlock,
|
|
1220
|
+
reject: @escaping RCTPromiseRejectBlock){
|
|
1221
|
+
|
|
1222
|
+
guard let store = _store else {
|
|
1223
|
+
return reject(INIT_ERROR, INIT_ERROR_MESSAGE, nil)
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
guard let _workoutUUID = UUID(uuidString: workoutUUID) else {
|
|
1227
|
+
return reject("INVALID_UUID_ERROR", "Invalid UUID received", nil)
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
async {
|
|
1231
|
+
do {
|
|
1232
|
+
let locations = await getSerializedWorkoutLocations(store: store, workoutUUID: _workoutUUID)
|
|
1233
|
+
resolve(locations)
|
|
1234
|
+
}
|
|
1235
|
+
catch {
|
|
1236
|
+
reject("WORKOUT_LOCATION_ERROR", "Failed to retrieve workout locations", nil)
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1052
1240
|
}
|