@flomentumsolutions/capacitor-health-extended 0.0.8 → 0.0.10
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.
|
@@ -6,7 +6,6 @@ import HealthKit
|
|
|
6
6
|
* Please read the Capacitor iOS Plugin Development Guide
|
|
7
7
|
* here: https://capacitorjs.com/docs/plugins/ios
|
|
8
8
|
*/
|
|
9
|
-
@MainActor
|
|
10
9
|
@objc(HealthPlugin)
|
|
11
10
|
public class HealthPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
12
11
|
public let identifier = "HealthPlugin"
|
|
@@ -300,58 +299,45 @@ public class HealthPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
300
299
|
let bucket = call.getString("bucket"),
|
|
301
300
|
let startDate = self.isoDateFormatter.date(from: startDateString),
|
|
302
301
|
let endDate = self.isoDateFormatter.date(from: endDateString) else {
|
|
303
|
-
|
|
302
|
+
DispatchQueue.main.async {
|
|
303
|
+
call.reject("Invalid parameters")
|
|
304
|
+
}
|
|
304
305
|
return
|
|
305
306
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
307
|
+
if dataTypeString == "mindfulness" {
|
|
308
|
+
self.queryMindfulnessAggregated(startDate: startDate, endDate: endDate) { result, error in
|
|
309
|
+
DispatchQueue.main.async {
|
|
310
|
+
if let error = error {
|
|
311
|
+
call.reject(error.localizedDescription)
|
|
312
|
+
} else if let result = result {
|
|
313
|
+
call.resolve(["aggregatedData": result])
|
|
314
|
+
}
|
|
313
315
|
}
|
|
314
316
|
}
|
|
315
317
|
} else {
|
|
316
318
|
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: .strictStartDate)
|
|
317
|
-
|
|
318
319
|
guard let interval = calculateInterval(bucket: bucket) else {
|
|
319
|
-
|
|
320
|
+
DispatchQueue.main.async {
|
|
321
|
+
call.reject("Invalid bucket")
|
|
322
|
+
}
|
|
320
323
|
return
|
|
321
324
|
}
|
|
322
|
-
|
|
323
325
|
guard let dataType = aggregateTypeToHKQuantityType(dataTypeString) else {
|
|
324
|
-
|
|
326
|
+
DispatchQueue.main.async {
|
|
327
|
+
call.reject("Invalid data type")
|
|
328
|
+
}
|
|
325
329
|
return
|
|
326
330
|
}
|
|
327
|
-
|
|
328
331
|
let options: HKStatisticsOptions = {
|
|
329
332
|
switch dataType.aggregationStyle {
|
|
330
333
|
case .cumulative:
|
|
331
334
|
return .cumulativeSum
|
|
332
|
-
|
|
333
|
-
// Newer discrete aggregation styles (iOS 15 +)
|
|
334
|
-
case .discreteAverage:
|
|
335
|
-
return .discreteAverage
|
|
336
|
-
@available(iOS 17.0, *)
|
|
337
|
-
case .discreteTemporallyWeighted:
|
|
338
|
-
return .discreteAverage
|
|
339
|
-
@available(iOS 17.0, *)
|
|
340
|
-
case .discreteEquivalentContinuousLevel:
|
|
341
|
-
return .discreteAverage
|
|
342
|
-
@available(iOS 17.0, *)
|
|
343
|
-
case .discreteArithmetic:
|
|
344
|
-
return .discreteAverage
|
|
345
|
-
|
|
346
|
-
// Legacy discrete fallback
|
|
347
335
|
case .discrete:
|
|
348
336
|
return .discreteAverage
|
|
349
|
-
|
|
350
337
|
@unknown default:
|
|
351
338
|
return .discreteAverage
|
|
352
339
|
}
|
|
353
340
|
}()
|
|
354
|
-
|
|
355
341
|
let query = HKStatisticsCollectionQuery(
|
|
356
342
|
quantityType: dataType,
|
|
357
343
|
quantitySamplePredicate: predicate,
|
|
@@ -359,139 +345,107 @@ public class HealthPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
359
345
|
anchorDate: startDate,
|
|
360
346
|
intervalComponents: interval
|
|
361
347
|
)
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
return HKUnit.count().unitDivided(by: HKUnit.minute())
|
|
398
|
-
case "hrv":
|
|
399
|
-
return HKUnit.secondUnit(with: .milli)
|
|
400
|
-
case "mindfulness":
|
|
401
|
-
return HKUnit.second()
|
|
402
|
-
default:
|
|
403
|
-
return .count()
|
|
404
|
-
}
|
|
405
|
-
}()
|
|
406
|
-
|
|
407
|
-
let value = quantity.doubleValue(for: unit)
|
|
408
|
-
|
|
409
|
-
aggregatedSamples.append([
|
|
410
|
-
"startDate": bucketStart,
|
|
411
|
-
"endDate": bucketEnd,
|
|
412
|
-
"value": value
|
|
413
|
-
])
|
|
348
|
+
query.initialResultsHandler = { _, result, error in
|
|
349
|
+
DispatchQueue.main.async {
|
|
350
|
+
if let error = error {
|
|
351
|
+
call.reject("Error fetching aggregated data: \(error.localizedDescription)")
|
|
352
|
+
return
|
|
353
|
+
}
|
|
354
|
+
var aggregatedSamples: [[String: Any]] = []
|
|
355
|
+
result?.enumerateStatistics(from: startDate, to: endDate) { statistics, _ in
|
|
356
|
+
let quantity: HKQuantity? = options.contains(.cumulativeSum)
|
|
357
|
+
? statistics.sumQuantity()
|
|
358
|
+
: statistics.averageQuantity()
|
|
359
|
+
guard let quantity = quantity else { return }
|
|
360
|
+
let bucketStart = statistics.startDate.timeIntervalSince1970 * 1000
|
|
361
|
+
let bucketEnd = statistics.endDate.timeIntervalSince1970 * 1000
|
|
362
|
+
let unit: HKUnit = {
|
|
363
|
+
switch dataTypeString {
|
|
364
|
+
case "steps": return .count()
|
|
365
|
+
case "active-calories", "total-calories": return .kilocalorie()
|
|
366
|
+
case "distance": return .meter()
|
|
367
|
+
case "weight": return .gramUnit(with: .kilo)
|
|
368
|
+
case "height": return .meter()
|
|
369
|
+
case "heart-rate": return HKUnit.count().unitDivided(by: HKUnit.minute())
|
|
370
|
+
case "hrv": return HKUnit.secondUnit(with: .milli)
|
|
371
|
+
case "mindfulness": return HKUnit.second()
|
|
372
|
+
default: return .count()
|
|
373
|
+
}
|
|
374
|
+
}()
|
|
375
|
+
let value = quantity.doubleValue(for: unit)
|
|
376
|
+
aggregatedSamples.append([
|
|
377
|
+
"startDate": bucketStart,
|
|
378
|
+
"endDate": bucketEnd,
|
|
379
|
+
"value": value
|
|
380
|
+
])
|
|
381
|
+
}
|
|
382
|
+
call.resolve(["aggregatedData": aggregatedSamples])
|
|
414
383
|
}
|
|
415
|
-
|
|
416
|
-
call.resolve(["aggregatedData": aggregatedSamples])
|
|
417
384
|
}
|
|
418
|
-
|
|
419
385
|
healthStore.execute(query)
|
|
420
386
|
}
|
|
421
387
|
}
|
|
422
|
-
|
|
423
|
-
func queryMindfulnessAggregated(startDate: Date, endDate: Date, completion: @escaping
|
|
388
|
+
|
|
389
|
+
func queryMindfulnessAggregated(startDate: Date, endDate: Date, completion: @escaping ([[String: Any]]?, Error?) -> Void) {
|
|
424
390
|
guard let mindfulType = HKObjectType.categoryType(forIdentifier: .mindfulSession) else {
|
|
425
|
-
|
|
391
|
+
DispatchQueue.main.async {
|
|
392
|
+
completion(nil, NSError(domain: "HealthKit", code: -1, userInfo: [NSLocalizedDescriptionKey: "MindfulSession type unavailable"]))
|
|
393
|
+
}
|
|
426
394
|
return
|
|
427
395
|
}
|
|
428
|
-
|
|
429
396
|
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: .strictStartDate)
|
|
430
397
|
let query = HKSampleQuery(sampleType: mindfulType, predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: nil) { _, samples, error in
|
|
431
|
-
guard let categorySamples = samples as? [HKCategorySample], error == nil else {
|
|
432
|
-
completion(nil, error)
|
|
433
|
-
return
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
// Aggregate total time per day
|
|
437
|
-
|
|
438
398
|
var dailyDurations: [Date: TimeInterval] = [:]
|
|
439
399
|
let calendar = Calendar.current
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
400
|
+
if let categorySamples = samples as? [HKCategorySample], error == nil {
|
|
401
|
+
for sample in categorySamples {
|
|
402
|
+
let startOfDay = calendar.startOfDay(for: sample.startDate)
|
|
403
|
+
let duration = sample.endDate.timeIntervalSince(sample.startDate)
|
|
404
|
+
dailyDurations[startOfDay, default: 0] += duration
|
|
405
|
+
}
|
|
406
|
+
var aggregatedSamples: [[String: Any]] = []
|
|
407
|
+
let dayComponent = DateComponents(day: 1)
|
|
408
|
+
for (date, duration) in dailyDurations {
|
|
409
|
+
aggregatedSamples.append([
|
|
410
|
+
"startDate": date,
|
|
411
|
+
"endDate": calendar.date(byAdding: dayComponent, to: date) as Any,
|
|
412
|
+
"value": duration
|
|
413
|
+
])
|
|
414
|
+
}
|
|
415
|
+
DispatchQueue.main.async {
|
|
416
|
+
completion(aggregatedSamples, nil)
|
|
417
|
+
}
|
|
418
|
+
} else {
|
|
419
|
+
DispatchQueue.main.async {
|
|
420
|
+
completion(nil, error)
|
|
449
421
|
}
|
|
450
422
|
}
|
|
451
|
-
|
|
452
|
-
var aggregatedSamples: [[String: Any]] = []
|
|
453
|
-
var dayComponent = DateComponents()
|
|
454
|
-
dayComponent.day = 1
|
|
455
|
-
dailyDurations.forEach { (dateAndDuration) in
|
|
456
|
-
aggregatedSamples.append([
|
|
457
|
-
"startDate": dateAndDuration.key as Any,
|
|
458
|
-
"endDate": calendar.date(byAdding: dayComponent, to: dateAndDuration.key) as Any,
|
|
459
|
-
"value": dateAndDuration.value
|
|
460
|
-
])
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
completion(aggregatedSamples, nil)
|
|
464
423
|
}
|
|
465
|
-
|
|
466
424
|
healthStore.execute(query)
|
|
467
425
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
private func queryAggregated(for startDate: Date, for endDate: Date, for dataType: HKQuantityType?, completion: @escaping @Sendable(Double?) -> Void) {
|
|
472
|
-
|
|
473
|
-
|
|
426
|
+
|
|
427
|
+
private func queryAggregated(for startDate: Date, for endDate: Date, for dataType: HKQuantityType?, completion: @escaping (Double?) -> Void) {
|
|
474
428
|
guard let quantityType = dataType else {
|
|
475
|
-
|
|
429
|
+
DispatchQueue.main.async {
|
|
430
|
+
completion(nil)
|
|
431
|
+
}
|
|
476
432
|
return
|
|
477
433
|
}
|
|
478
|
-
|
|
479
434
|
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: .strictStartDate)
|
|
480
|
-
|
|
481
435
|
let query = HKStatisticsQuery(
|
|
482
436
|
quantityType: quantityType,
|
|
483
437
|
quantitySamplePredicate: predicate,
|
|
484
438
|
options: .cumulativeSum
|
|
485
439
|
) { _, result, _ in
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
return
|
|
440
|
+
let value: Double? = {
|
|
441
|
+
guard let result = result, let sum = result.sumQuantity() else { return 0.0 }
|
|
442
|
+
return sum.doubleValue(for: HKUnit.count())
|
|
443
|
+
}()
|
|
444
|
+
DispatchQueue.main.async {
|
|
445
|
+
completion(value)
|
|
489
446
|
}
|
|
490
|
-
completion(sum.doubleValue(for: HKUnit.count()))
|
|
491
447
|
}
|
|
492
|
-
|
|
493
448
|
healthStore.execute(query)
|
|
494
|
-
|
|
495
449
|
}
|
|
496
450
|
|
|
497
451
|
|
|
@@ -529,30 +483,30 @@ public class HealthPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
529
483
|
call.reject("Invalid parameters")
|
|
530
484
|
return
|
|
531
485
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
// Create a predicate to filter workouts by date
|
|
486
|
+
|
|
536
487
|
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: .strictStartDate)
|
|
537
|
-
|
|
538
|
-
|
|
488
|
+
let workoutQuery = HKSampleQuery(sampleType: HKObjectType.workoutType(), predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: nil) { [weak self] query, samples, error in
|
|
489
|
+
guard let self = self else { return }
|
|
539
490
|
if let error = error {
|
|
540
|
-
|
|
491
|
+
DispatchQueue.main.async {
|
|
492
|
+
call.reject("Error querying workouts: \(error.localizedDescription)")
|
|
493
|
+
}
|
|
541
494
|
return
|
|
542
495
|
}
|
|
543
|
-
|
|
544
496
|
guard let workouts = samples as? [HKWorkout] else {
|
|
545
|
-
|
|
497
|
+
DispatchQueue.main.async {
|
|
498
|
+
call.resolve(["workouts": []])
|
|
499
|
+
}
|
|
546
500
|
return
|
|
547
501
|
}
|
|
548
|
-
|
|
549
|
-
|
|
502
|
+
let outerGroup = DispatchGroup()
|
|
503
|
+
let resultsQueue = DispatchQueue(label: "com.flomentum.healthplugin.workoutResults")
|
|
504
|
+
var workoutResults: [[String: Any]] = []
|
|
550
505
|
var errors: [String: String] = [:]
|
|
551
|
-
let dispatchGroup = DispatchGroup()
|
|
552
506
|
|
|
553
|
-
// Process each workout
|
|
554
507
|
for workout in workouts {
|
|
555
|
-
|
|
508
|
+
outerGroup.enter()
|
|
509
|
+
var localDict: [String: Any] = [
|
|
556
510
|
"startDate": workout.startDate,
|
|
557
511
|
"endDate": workout.endDate,
|
|
558
512
|
"workoutType": self.workoutTypeMapping[workout.workoutActivityType.rawValue, default: "other"],
|
|
@@ -563,59 +517,56 @@ public class HealthPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
563
517
|
"calories": workout.totalEnergyBurned?.doubleValue(for: .kilocalorie()) ?? 0,
|
|
564
518
|
"distance": workout.totalDistance?.doubleValue(for: .meter()) ?? 0
|
|
565
519
|
]
|
|
520
|
+
let innerGroup = DispatchGroup()
|
|
521
|
+
var localHeartRates: [[String: Any]] = []
|
|
522
|
+
var localRoutes: [[String: Any]] = []
|
|
566
523
|
|
|
567
|
-
|
|
568
|
-
var heartRateSamples: [[String: Any]] = []
|
|
569
|
-
var routeSamples: [[String: Any]] = []
|
|
570
|
-
|
|
571
|
-
// Query heart rate data if requested
|
|
572
524
|
if includeHeartRate {
|
|
573
|
-
|
|
574
|
-
self.queryHeartRate(for: workout
|
|
575
|
-
|
|
576
|
-
|
|
525
|
+
innerGroup.enter()
|
|
526
|
+
self.queryHeartRate(for: workout) { rates, error in
|
|
527
|
+
localHeartRates = rates
|
|
528
|
+
if let error = error {
|
|
529
|
+
resultsQueue.async {
|
|
530
|
+
errors["heart-rate"] = error
|
|
531
|
+
}
|
|
577
532
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
})
|
|
533
|
+
innerGroup.leave()
|
|
534
|
+
}
|
|
581
535
|
}
|
|
582
|
-
|
|
583
|
-
// Query route data if requested
|
|
584
536
|
if includeRoute {
|
|
585
|
-
|
|
586
|
-
self.queryRoute(for: workout
|
|
587
|
-
|
|
588
|
-
|
|
537
|
+
innerGroup.enter()
|
|
538
|
+
self.queryRoute(for: workout) { routes, error in
|
|
539
|
+
localRoutes = routes
|
|
540
|
+
if let error = error {
|
|
541
|
+
resultsQueue.async {
|
|
542
|
+
errors["route"] = error
|
|
543
|
+
}
|
|
589
544
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
})
|
|
545
|
+
innerGroup.leave()
|
|
546
|
+
}
|
|
593
547
|
}
|
|
594
|
-
|
|
595
548
|
if includeSteps {
|
|
596
|
-
|
|
597
|
-
self.queryAggregated(for: workout.startDate, for: workout.endDate, for: HKObjectType.quantityType(forIdentifier: .stepCount)
|
|
598
|
-
if
|
|
599
|
-
|
|
549
|
+
innerGroup.enter()
|
|
550
|
+
self.queryAggregated(for: workout.startDate, for: workout.endDate, for: HKObjectType.quantityType(forIdentifier: .stepCount)) { steps in
|
|
551
|
+
if let steps = steps {
|
|
552
|
+
localDict["steps"] = steps
|
|
600
553
|
}
|
|
601
|
-
|
|
602
|
-
}
|
|
554
|
+
innerGroup.leave()
|
|
555
|
+
}
|
|
603
556
|
}
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
557
|
+
innerGroup.notify(queue: .main) {
|
|
558
|
+
localDict["heartRate"] = localHeartRates
|
|
559
|
+
localDict["route"] = localRoutes
|
|
560
|
+
resultsQueue.async {
|
|
561
|
+
workoutResults.append(localDict)
|
|
562
|
+
}
|
|
563
|
+
outerGroup.leave()
|
|
609
564
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
565
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
call.resolve(["workouts": workoutList, "errors": errors])
|
|
566
|
+
outerGroup.notify(queue: .main) {
|
|
567
|
+
call.resolve(["workouts": workoutResults, "errors": errors])
|
|
616
568
|
}
|
|
617
569
|
}
|
|
618
|
-
|
|
619
570
|
healthStore.execute(workoutQuery)
|
|
620
571
|
}
|
|
621
572
|
|
|
@@ -657,26 +608,29 @@ public class HealthPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
657
608
|
let routeType = HKSeriesType.workoutRoute()
|
|
658
609
|
let predicate = HKQuery.predicateForObjects(from: workout)
|
|
659
610
|
|
|
660
|
-
let routeQuery = HKSampleQuery(sampleType: routeType, predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: nil) {
|
|
661
|
-
guard let
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
611
|
+
let routeQuery = HKSampleQuery(sampleType: routeType, predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: nil) { [weak self] _, samples, error in
|
|
612
|
+
guard let self = self else { return }
|
|
613
|
+
if let routes = samples as? [HKWorkoutRoute], error == nil {
|
|
614
|
+
let routeDispatchGroup = DispatchGroup()
|
|
615
|
+
let allLocationsQueue = DispatchQueue(label: "com.flomentum.healthplugin.allLocations")
|
|
616
|
+
var allLocations: [[String: Any]] = []
|
|
617
|
+
|
|
618
|
+
for route in routes {
|
|
619
|
+
routeDispatchGroup.enter()
|
|
620
|
+
self.queryLocations(for: route) { locations in
|
|
621
|
+
allLocationsQueue.async {
|
|
622
|
+
allLocations.append(contentsOf: locations)
|
|
623
|
+
}
|
|
624
|
+
routeDispatchGroup.leave()
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
routeDispatchGroup.notify(queue: .main) {
|
|
628
|
+
completion(allLocations, nil)
|
|
629
|
+
}
|
|
630
|
+
} else {
|
|
631
|
+
DispatchQueue.main.async {
|
|
632
|
+
completion([], error?.localizedDescription)
|
|
675
633
|
}
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
routeDispatchGroup.notify(queue: .main) {
|
|
679
|
-
completion(routeLocations, nil)
|
|
680
634
|
}
|
|
681
635
|
}
|
|
682
636
|
|
|
@@ -685,16 +639,19 @@ public class HealthPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
685
639
|
|
|
686
640
|
// MARK: - Query Route Locations
|
|
687
641
|
private func queryLocations(for route: HKWorkoutRoute, completion: @escaping @Sendable ([[String: Any]]) -> Void) {
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
let locationQuery = HKWorkoutRouteQuery(route: route) { _, locations, done, error in
|
|
642
|
+
let locationQuery = HKWorkoutRouteQuery(route: route) { [weak self] _, locations, done, error in
|
|
643
|
+
guard let self = self else { return }
|
|
691
644
|
guard let locations = locations, error == nil else {
|
|
692
|
-
|
|
645
|
+
DispatchQueue.main.async {
|
|
646
|
+
completion([])
|
|
647
|
+
}
|
|
693
648
|
return
|
|
694
649
|
}
|
|
695
650
|
|
|
696
|
-
//
|
|
651
|
+
// Process locations on the serial queue to avoid race conditions
|
|
697
652
|
self.routeSyncQueue.async {
|
|
653
|
+
var routeLocations: [[String: Any]] = []
|
|
654
|
+
|
|
698
655
|
for location in locations {
|
|
699
656
|
let locationDict: [String: Any] = [
|
|
700
657
|
"timestamp": location.timestamp,
|
|
@@ -706,7 +663,9 @@ public class HealthPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
706
663
|
}
|
|
707
664
|
|
|
708
665
|
if done {
|
|
709
|
-
|
|
666
|
+
DispatchQueue.main.async {
|
|
667
|
+
completion(routeLocations)
|
|
668
|
+
}
|
|
710
669
|
}
|
|
711
670
|
}
|
|
712
671
|
}
|
package/package.json
CHANGED