@flomentumsolutions/capacitor-health-extended 0.0.7 → 0.0.8

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,6 +6,7 @@ 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
9
10
  @objc(HealthPlugin)
10
11
  public class HealthPlugin: CAPPlugin, CAPBridgedPlugin {
11
12
  public let identifier = "HealthPlugin"
@@ -22,6 +23,9 @@ public class HealthPlugin: CAPPlugin, CAPBridgedPlugin {
22
23
 
23
24
  let healthStore = HKHealthStore()
24
25
 
26
+ /// Serial queue to make route‑location mutations thread‑safe without locks
27
+ private let routeSyncQueue = DispatchQueue(label: "com.flomentum.healthplugin.routeSync")
28
+
25
29
  @objc func isHealthAvailable(_ call: CAPPluginCall) {
26
30
  let isAvailable = HKHealthStore.isHealthDataAvailable()
27
31
  call.resolve(["available": isAvailable])
@@ -325,10 +329,26 @@ public class HealthPlugin: CAPPlugin, CAPBridgedPlugin {
325
329
  switch dataType.aggregationStyle {
326
330
  case .cumulative:
327
331
  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
328
347
  case .discrete:
329
- return .discreteAverage // or .discreteMin / Max when needed
348
+ return .discreteAverage
349
+
330
350
  @unknown default:
331
- return .cumulativeSum
351
+ return .discreteAverage
332
352
  }
333
353
  }()
334
354
 
@@ -400,7 +420,7 @@ public class HealthPlugin: CAPPlugin, CAPBridgedPlugin {
400
420
  }
401
421
  }
402
422
 
403
- func queryMindfulnessAggregated(startDate: Date, endDate: Date, completion: @escaping ([[String: Any]]?, Error?) -> Void) {
423
+ func queryMindfulnessAggregated(startDate: Date, endDate: Date, completion: @escaping @Sendable ([[String: Any]]?, Error?) -> Void) {
404
424
  guard let mindfulType = HKObjectType.categoryType(forIdentifier: .mindfulSession) else {
405
425
  completion(nil, NSError(domain: "HealthKit", code: -1, userInfo: [NSLocalizedDescriptionKey: "MindfulSession type unavailable"]))
406
426
  return
@@ -448,7 +468,7 @@ public class HealthPlugin: CAPPlugin, CAPBridgedPlugin {
448
468
 
449
469
 
450
470
 
451
- private func queryAggregated(for startDate: Date, for endDate: Date, for dataType: HKQuantityType?, completion: @escaping(Double?) -> Void) {
471
+ private func queryAggregated(for startDate: Date, for endDate: Date, for dataType: HKQuantityType?, completion: @escaping @Sendable(Double?) -> Void) {
452
472
 
453
473
 
454
474
  guard let quantityType = dataType else {
@@ -602,7 +622,7 @@ public class HealthPlugin: CAPPlugin, CAPBridgedPlugin {
602
622
 
603
623
 
604
624
  // MARK: - Query Heart Rate Data
605
- private func queryHeartRate(for workout: HKWorkout, completion: @escaping ([[String: Any]], String?) -> Void) {
625
+ private func queryHeartRate(for workout: HKWorkout, completion: @escaping @Sendable ([[String: Any]], String?) -> Void) {
606
626
  let heartRateType = HKObjectType.quantityType(forIdentifier: .heartRate)!
607
627
  let predicate = HKQuery.predicateForSamples(withStart: workout.startDate, end: workout.endDate, options: .strictStartDate)
608
628
 
@@ -633,7 +653,7 @@ public class HealthPlugin: CAPPlugin, CAPBridgedPlugin {
633
653
  }
634
654
 
635
655
  // MARK: - Query Route Data
636
- private func queryRoute(for workout: HKWorkout, completion: @escaping ([[String: Any]], String?) -> Void) {
656
+ private func queryRoute(for workout: HKWorkout, completion: @escaping @Sendable ([[String: Any]], String?) -> Void) {
637
657
  let routeType = HKSeriesType.workoutRoute()
638
658
  let predicate = HKQuery.predicateForObjects(from: workout)
639
659
 
@@ -664,30 +684,33 @@ public class HealthPlugin: CAPPlugin, CAPBridgedPlugin {
664
684
  }
665
685
 
666
686
  // MARK: - Query Route Locations
667
- private func queryLocations(for route: HKWorkoutRoute, completion: @escaping ([[String: Any]]) -> Void) {
687
+ private func queryLocations(for route: HKWorkoutRoute, completion: @escaping @Sendable ([[String: Any]]) -> Void) {
668
688
  var routeLocations: [[String: Any]] = []
669
-
670
- let locationQuery = HKWorkoutRouteQuery(route: route) { query, locations, done, error in
689
+
690
+ let locationQuery = HKWorkoutRouteQuery(route: route) { _, locations, done, error in
671
691
  guard let locations = locations, error == nil else {
672
692
  completion([])
673
693
  return
674
694
  }
675
-
676
- for location in locations {
677
- let locationDict: [String: Any] = [
678
- "timestamp": location.timestamp,
679
- "lat": location.coordinate.latitude,
680
- "lng": location.coordinate.longitude,
681
- "alt": location.altitude
682
- ]
683
- routeLocations.append(locationDict)
684
- }
685
-
686
- if done {
687
- completion(routeLocations)
695
+
696
+ // Append on a dedicated serial queue so we’re race‑free without NSLock
697
+ self.routeSyncQueue.async {
698
+ for location in locations {
699
+ let locationDict: [String: Any] = [
700
+ "timestamp": location.timestamp,
701
+ "lat": location.coordinate.latitude,
702
+ "lng": location.coordinate.longitude,
703
+ "alt": location.altitude
704
+ ]
705
+ routeLocations.append(locationDict)
706
+ }
707
+
708
+ if done {
709
+ completion(routeLocations)
710
+ }
688
711
  }
689
712
  }
690
-
713
+
691
714
  healthStore.execute(locationQuery)
692
715
  }
693
716
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flomentumsolutions/capacitor-health-extended",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "Capacitor plugin for Apple HealthKit and Google Health Connect Platform",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",