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