@flomentumsolutions/capacitor-health-extended 0.0.4 → 0.0.6
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/Package.swift +2 -2
- package/README.md +6 -2
- package/android/src/main/java/com/flomentum/health/capacitor/HealthPlugin.kt +47 -4
- package/dist/esm/definitions.d.ts +2 -2
- package/dist/esm/definitions.js.map +1 -1
- package/dist/plugin.js +1 -1
- package/ios/Sources/HealthPluginPlugin/HealthPlugin.swift +62 -0
- package/package.json +1 -1
package/Package.swift
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import PackageDescription
|
|
3
3
|
|
|
4
4
|
let package = Package(
|
|
5
|
-
name: "
|
|
5
|
+
name: "CapacitorHealthExtended",
|
|
6
6
|
platforms: [.iOS(.v13)],
|
|
7
7
|
products: [
|
|
8
8
|
.library(
|
|
9
|
-
name: "
|
|
9
|
+
name: "CapacitorHealthExtended",
|
|
10
10
|
targets: ["HealthPluginPlugin"])
|
|
11
11
|
],
|
|
12
12
|
dependencies: [
|
package/README.md
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
# capacitor-health
|
|
1
|
+
# capacitor-health-extended
|
|
2
2
|
|
|
3
3
|
Capacitor plugin to query data from Apple Health and Google Health Connect
|
|
4
4
|
|
|
5
5
|
## Thanks and attribution
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Forked from [capacitor-health](https://github.,com/mley/capacitor-health) and as such...
|
|
8
|
+
- Some parts, concepts and ideas are borrowed from [cordova-plugin-health](https://github.com/dariosalvi78/cordova-plugin-health/).
|
|
9
|
+
- Big thanks to [@dariosalvi78](https://github.com/dariosalvi78) for the support.
|
|
10
|
+
|
|
11
|
+
Thanks [@mley](https://github.com/mley) for the ground work. The goal of this fork is to extend functionality and datapoints and keep up with the ever-changing brand-new Android Health Connect Platform. I'm hoping to create platform parity for capacitor API-based health data access.
|
|
8
12
|
|
|
9
13
|
## Install
|
|
10
14
|
|
|
@@ -32,7 +32,8 @@ import java.util.concurrent.atomic.AtomicReference
|
|
|
32
32
|
import androidx.core.net.toUri
|
|
33
33
|
|
|
34
34
|
enum class CapHealthPermission {
|
|
35
|
-
READ_STEPS, READ_WORKOUTS, READ_HEART_RATE, READ_ACTIVE_CALORIES, READ_TOTAL_CALORIES, READ_DISTANCE, READ_WEIGHT
|
|
35
|
+
READ_STEPS, READ_WORKOUTS, READ_HEART_RATE, READ_ACTIVE_CALORIES, READ_TOTAL_CALORIES, READ_DISTANCE, READ_WEIGHT
|
|
36
|
+
, READ_HRV, READ_BLOOD_PRESSURE;
|
|
36
37
|
|
|
37
38
|
companion object {
|
|
38
39
|
fun from(s: String): CapHealthPermission? {
|
|
@@ -54,7 +55,9 @@ enum class CapHealthPermission {
|
|
|
54
55
|
Permission(alias = "READ_DISTANCE", strings = ["android.permission.health.READ_DISTANCE"]),
|
|
55
56
|
Permission(alias = "READ_ACTIVE_CALORIES", strings = ["android.permission.health.READ_ACTIVE_CALORIES_BURNED"]),
|
|
56
57
|
Permission(alias = "READ_TOTAL_CALORIES", strings = ["android.permission.health.READ_TOTAL_CALORIES_BURNED"]),
|
|
57
|
-
Permission(alias = "READ_HEART_RATE", strings = ["android.permission.health.READ_HEART_RATE"])
|
|
58
|
+
Permission(alias = "READ_HEART_RATE", strings = ["android.permission.health.READ_HEART_RATE"]),
|
|
59
|
+
Permission(alias = "READ_HRV", strings = ["android.permission.health.READ_HEART_RATE_VARIABILITY"]),
|
|
60
|
+
Permission(alias = "READ_BLOOD_PRESSURE", strings = ["android.permission.health.READ_BLOOD_PRESSURE"])
|
|
58
61
|
]
|
|
59
62
|
)
|
|
60
63
|
|
|
@@ -75,7 +78,9 @@ class HealthPlugin : Plugin() {
|
|
|
75
78
|
CapHealthPermission.READ_ACTIVE_CALORIES to HealthPermission.getReadPermission(ActiveCaloriesBurnedRecord::class),
|
|
76
79
|
CapHealthPermission.READ_TOTAL_CALORIES to HealthPermission.getReadPermission(TotalCaloriesBurnedRecord::class),
|
|
77
80
|
CapHealthPermission.READ_DISTANCE to HealthPermission.getReadPermission(DistanceRecord::class),
|
|
78
|
-
CapHealthPermission.READ_WORKOUTS to HealthPermission.getReadPermission(ExerciseSessionRecord::class)
|
|
81
|
+
CapHealthPermission.READ_WORKOUTS to HealthPermission.getReadPermission(ExerciseSessionRecord::class),
|
|
82
|
+
CapHealthPermission.READ_HRV to HealthPermission.getReadPermission(HeartRateVariabilitySdnnRecord::class),
|
|
83
|
+
CapHealthPermission.READ_BLOOD_PRESSURE to HealthPermission.getReadPermission(BloodPressureRecord::class)
|
|
79
84
|
)
|
|
80
85
|
|
|
81
86
|
override fun load() {
|
|
@@ -258,6 +263,7 @@ class HealthPlugin : Plugin() {
|
|
|
258
263
|
TotalCaloriesBurnedRecord.ENERGY_TOTAL
|
|
259
264
|
) { it?.inKilocalories }
|
|
260
265
|
"distance" -> metricAndMapper("distance", CapHealthPermission.READ_DISTANCE, DistanceRecord.DISTANCE_TOTAL) { it?.inMeters }
|
|
266
|
+
"hrv" -> metricAndMapper("hrv", CapHealthPermission.READ_HRV, HeartRateVariabilitySdnnRecord.SDNN_AVG) { it }
|
|
261
267
|
else -> throw RuntimeException("Unsupported dataType: $dataType")
|
|
262
268
|
}
|
|
263
269
|
}
|
|
@@ -277,6 +283,8 @@ class HealthPlugin : Plugin() {
|
|
|
277
283
|
"heart-rate" -> readLatestHeartRate()
|
|
278
284
|
"weight" -> readLatestWeight()
|
|
279
285
|
"steps" -> readLatestSteps()
|
|
286
|
+
"hrv" -> readLatestHrv()
|
|
287
|
+
"blood-pressure" -> readLatestBloodPressure()
|
|
280
288
|
else -> {
|
|
281
289
|
call.reject("Unsupported data type: $dataType")
|
|
282
290
|
return@launch
|
|
@@ -284,7 +292,7 @@ class HealthPlugin : Plugin() {
|
|
|
284
292
|
}
|
|
285
293
|
call.resolve(result)
|
|
286
294
|
} catch (e: Exception) {
|
|
287
|
-
Log.e(tag, "queryLatestSample: Error fetching latest
|
|
295
|
+
Log.e(tag, "queryLatestSample: Error fetching latest $dataType", e)
|
|
288
296
|
call.reject("Error fetching latest $dataType: ${e.message}")
|
|
289
297
|
}
|
|
290
298
|
}
|
|
@@ -344,6 +352,41 @@ class HealthPlugin : Plugin() {
|
|
|
344
352
|
}
|
|
345
353
|
}
|
|
346
354
|
|
|
355
|
+
private suspend fun readLatestHrv(): JSObject {
|
|
356
|
+
if (!hasPermission(CapHealthPermission.READ_HRV)) {
|
|
357
|
+
throw Exception("Permission for HRV not granted")
|
|
358
|
+
}
|
|
359
|
+
val request = ReadRecordsRequest(
|
|
360
|
+
recordType = HeartRateVariabilitySdnnRecord::class,
|
|
361
|
+
timeRangeFilter = TimeRangeFilter.after(Instant.EPOCH),
|
|
362
|
+
pageSize = 1
|
|
363
|
+
)
|
|
364
|
+
val result = healthConnectClient.readRecords(request)
|
|
365
|
+
val record = result.records.firstOrNull() ?: throw Exception("No HRV data found")
|
|
366
|
+
return JSObject().apply {
|
|
367
|
+
put("timestamp", record.time.toString())
|
|
368
|
+
put("value", record.sdnnMillis)
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
private suspend fun readLatestBloodPressure(): JSObject {
|
|
373
|
+
if (!hasPermission(CapHealthPermission.READ_BLOOD_PRESSURE)) {
|
|
374
|
+
throw Exception("Permission for blood pressure not granted")
|
|
375
|
+
}
|
|
376
|
+
val request = ReadRecordsRequest(
|
|
377
|
+
recordType = BloodPressureRecord::class,
|
|
378
|
+
timeRangeFilter = TimeRangeFilter.after(Instant.EPOCH),
|
|
379
|
+
pageSize = 1
|
|
380
|
+
)
|
|
381
|
+
val result = healthConnectClient.readRecords(request)
|
|
382
|
+
val record = result.records.firstOrNull() ?: throw Exception("No blood pressure data found")
|
|
383
|
+
return JSObject().apply {
|
|
384
|
+
put("timestamp", record.time.toString())
|
|
385
|
+
put("systolic", record.systolic.inMillimetersOfMercury)
|
|
386
|
+
put("diastolic", record.diastolic.inMillimetersOfMercury)
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
347
390
|
@PluginMethod
|
|
348
391
|
fun queryAggregated(call: PluginCall) {
|
|
349
392
|
if (!ensureClientInitialized(call)) return
|
|
@@ -51,7 +51,7 @@ export interface HealthPlugin {
|
|
|
51
51
|
*/
|
|
52
52
|
queryWorkouts(request: QueryWorkoutRequest): Promise<QueryWorkoutResponse>;
|
|
53
53
|
}
|
|
54
|
-
export declare type HealthPermission = 'READ_STEPS' | 'READ_WORKOUTS' | 'READ_ACTIVE_CALORIES' | 'READ_TOTAL_CALORIES' | 'READ_DISTANCE' | 'READ_HEART_RATE' | 'READ_ROUTE' | 'READ_MINDFULNESS';
|
|
54
|
+
export declare type HealthPermission = 'READ_STEPS' | 'READ_WORKOUTS' | 'READ_ACTIVE_CALORIES' | 'READ_TOTAL_CALORIES' | 'READ_DISTANCE' | 'READ_HEART_RATE' | 'READ_ROUTE' | 'READ_MINDFULNESS' | 'READ_HRV' | 'READ_BLOOD_PRESSURE';
|
|
55
55
|
export interface PermissionsRequest {
|
|
56
56
|
permissions: HealthPermission[];
|
|
57
57
|
}
|
|
@@ -97,7 +97,7 @@ export interface Workout {
|
|
|
97
97
|
export interface QueryAggregatedRequest {
|
|
98
98
|
startDate: string;
|
|
99
99
|
endDate: string;
|
|
100
|
-
dataType: 'steps' | 'active-calories' | 'mindfulness';
|
|
100
|
+
dataType: 'steps' | 'active-calories' | 'mindfulness' | 'hrv' | 'blood-pressure';
|
|
101
101
|
bucket: string;
|
|
102
102
|
}
|
|
103
103
|
export interface QueryAggregatedResponse {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["export interface HealthPlugin {\n /**\n * Checks if health API is available.\n * Android: If false is returned, the Google Health Connect app is probably not installed.\n * See showHealthConnectInPlayStore()\n *\n */\n isHealthAvailable(): Promise<{ available: boolean }>;\n\n /**\n * Android only: Returns for each given permission, if it was granted by the underlying health API\n * @param permissions permissions to query\n */\n checkHealthPermissions(permissions: PermissionsRequest): Promise<PermissionResponse>;\n\n /**\n * Requests the permissions from the user.\n *\n * Android: Apps can ask only a few times for permissions, after that the user has to grant them manually in\n * the Health Connect app. See openHealthConnectSettings()\n *\n * iOS: If the permissions are already granted or denied, this method will just return without asking the user. In iOS\n * we can't really detect if a user granted or denied a permission. The return value reflects the assumption that all\n * permissions were granted.\n *\n * @param permissions permissions to request\n */\n requestHealthPermissions(permissions: PermissionsRequest): Promise<PermissionResponse>;\n\n /**\n * Opens the apps settings, which is kind of wrong, because health permissions are configured under:\n * Settings > Apps > (Apple) Health > Access and Devices > [app-name]\n * But we can't go there directly.\n */\n openAppleHealthSettings(): Promise<void>;\n\n /**\n * Opens the Google Health Connect app\n */\n openHealthConnectSettings(): Promise<void>;\n\n /**\n * Opens the Google Health Connect app in PlayStore\n */\n showHealthConnectInPlayStore(): Promise<void>;\n\n /**\n * Query aggregated data\n * @param request\n */\n queryAggregated(request: QueryAggregatedRequest): Promise<QueryAggregatedResponse>;\n\n /**\n * Query workouts\n * @param request\n */\n queryWorkouts(request: QueryWorkoutRequest): Promise<QueryWorkoutResponse>;\n}\n\nexport declare type HealthPermission =\n | 'READ_STEPS'\n | 'READ_WORKOUTS'\n | 'READ_ACTIVE_CALORIES'\n | 'READ_TOTAL_CALORIES'\n | 'READ_DISTANCE'\n | 'READ_HEART_RATE'\n | 'READ_ROUTE'\n | 'READ_MINDFULNESS';\n\nexport interface PermissionsRequest {\n permissions: HealthPermission[];\n}\n\nexport interface PermissionResponse {\n permissions: { [key: string]: boolean }[];\n}\n\nexport interface QueryWorkoutRequest {\n startDate: string;\n endDate: string;\n includeHeartRate: boolean;\n includeRoute: boolean;\n includeSteps: boolean;\n}\n\nexport interface HeartRateSample {\n timestamp: string;\n bpm: number;\n}\n\nexport interface RouteSample {\n timestamp: string;\n lat: number;\n lng: number;\n alt?: number;\n}\n\nexport interface QueryWorkoutResponse {\n workouts: Workout[];\n}\n\nexport interface Workout {\n startDate: string;\n endDate: string;\n workoutType: string;\n sourceName: string;\n id?: string;\n duration: number;\n distance?: number;\n steps?: number;\n calories: number;\n sourceBundleId: string;\n route?: RouteSample[];\n heartRate?: HeartRateSample[];\n}\n\nexport interface QueryAggregatedRequest {\n startDate: string;\n endDate: string;\n dataType: 'steps' | 'active-calories' | 'mindfulness';\n bucket: string;\n}\n\nexport interface QueryAggregatedResponse {\n aggregatedData: AggregatedSample[];\n}\n\nexport interface AggregatedSample {\n startDate: string;\n endDate: string;\n value: number;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["export interface HealthPlugin {\n /**\n * Checks if health API is available.\n * Android: If false is returned, the Google Health Connect app is probably not installed.\n * See showHealthConnectInPlayStore()\n *\n */\n isHealthAvailable(): Promise<{ available: boolean }>;\n\n /**\n * Android only: Returns for each given permission, if it was granted by the underlying health API\n * @param permissions permissions to query\n */\n checkHealthPermissions(permissions: PermissionsRequest): Promise<PermissionResponse>;\n\n /**\n * Requests the permissions from the user.\n *\n * Android: Apps can ask only a few times for permissions, after that the user has to grant them manually in\n * the Health Connect app. See openHealthConnectSettings()\n *\n * iOS: If the permissions are already granted or denied, this method will just return without asking the user. In iOS\n * we can't really detect if a user granted or denied a permission. The return value reflects the assumption that all\n * permissions were granted.\n *\n * @param permissions permissions to request\n */\n requestHealthPermissions(permissions: PermissionsRequest): Promise<PermissionResponse>;\n\n /**\n * Opens the apps settings, which is kind of wrong, because health permissions are configured under:\n * Settings > Apps > (Apple) Health > Access and Devices > [app-name]\n * But we can't go there directly.\n */\n openAppleHealthSettings(): Promise<void>;\n\n /**\n * Opens the Google Health Connect app\n */\n openHealthConnectSettings(): Promise<void>;\n\n /**\n * Opens the Google Health Connect app in PlayStore\n */\n showHealthConnectInPlayStore(): Promise<void>;\n\n /**\n * Query aggregated data\n * @param request\n */\n queryAggregated(request: QueryAggregatedRequest): Promise<QueryAggregatedResponse>;\n\n /**\n * Query workouts\n * @param request\n */\n queryWorkouts(request: QueryWorkoutRequest): Promise<QueryWorkoutResponse>;\n}\n\nexport declare type HealthPermission =\n | 'READ_STEPS'\n | 'READ_WORKOUTS'\n | 'READ_ACTIVE_CALORIES'\n | 'READ_TOTAL_CALORIES'\n | 'READ_DISTANCE'\n | 'READ_HEART_RATE'\n | 'READ_ROUTE'\n | 'READ_MINDFULNESS'\n | 'READ_HRV'\n | 'READ_BLOOD_PRESSURE';\n\nexport interface PermissionsRequest {\n permissions: HealthPermission[];\n}\n\nexport interface PermissionResponse {\n permissions: { [key: string]: boolean }[];\n}\n\nexport interface QueryWorkoutRequest {\n startDate: string;\n endDate: string;\n includeHeartRate: boolean;\n includeRoute: boolean;\n includeSteps: boolean;\n}\n\nexport interface HeartRateSample {\n timestamp: string;\n bpm: number;\n}\n\nexport interface RouteSample {\n timestamp: string;\n lat: number;\n lng: number;\n alt?: number;\n}\n\nexport interface QueryWorkoutResponse {\n workouts: Workout[];\n}\n\nexport interface Workout {\n startDate: string;\n endDate: string;\n workoutType: string;\n sourceName: string;\n id?: string;\n duration: number;\n distance?: number;\n steps?: number;\n calories: number;\n sourceBundleId: string;\n route?: RouteSample[];\n heartRate?: HeartRateSample[];\n}\n\nexport interface QueryAggregatedRequest {\n startDate: string;\n endDate: string;\n dataType: 'steps' | 'active-calories' | 'mindfulness' | 'hrv' | 'blood-pressure';\n bucket: string;\n}\n\nexport interface QueryAggregatedResponse {\n aggregatedData: AggregatedSample[];\n}\n\nexport interface AggregatedSample {\n startDate: string;\n endDate: string;\n value: number;\n}\n"]}
|
package/dist/plugin.js
CHANGED
|
@@ -86,6 +86,53 @@ public class HealthPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
86
86
|
call.reject("Missing data type")
|
|
87
87
|
return
|
|
88
88
|
}
|
|
89
|
+
// ---- Special handling for blood‑pressure correlation ----
|
|
90
|
+
if dataTypeString == "blood-pressure" {
|
|
91
|
+
guard let bpType = HKObjectType.correlationType(forIdentifier: .bloodPressure) else {
|
|
92
|
+
call.reject("Blood pressure type not available")
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false)
|
|
97
|
+
let predicate = HKQuery.predicateForSamples(withStart: Date.distantPast, end: Date(), options: .strictEndDate)
|
|
98
|
+
|
|
99
|
+
let query = HKSampleQuery(sampleType: bpType, predicate: predicate, limit: 1, sortDescriptors: [sortDescriptor]) { _, samples, error in
|
|
100
|
+
|
|
101
|
+
guard let bpCorrelation = samples?.first as? HKCorrelation else {
|
|
102
|
+
if let error = error {
|
|
103
|
+
call.reject("Error fetching latest blood pressure sample", "NO_SAMPLE", error)
|
|
104
|
+
} else {
|
|
105
|
+
call.reject("No blood pressure sample found", "NO_SAMPLE")
|
|
106
|
+
}
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let unit = HKUnit.millimeterOfMercury()
|
|
111
|
+
|
|
112
|
+
let systolicSamples = bpCorrelation.objects(for: HKObjectType.quantityType(forIdentifier: .bloodPressureSystolic)!)
|
|
113
|
+
let diastolicSamples = bpCorrelation.objects(for: HKObjectType.quantityType(forIdentifier: .bloodPressureDiastolic)!)
|
|
114
|
+
|
|
115
|
+
guard let systolicSample = systolicSamples.first as? HKQuantitySample,
|
|
116
|
+
let diastolicSample = diastolicSamples.first as? HKQuantitySample else {
|
|
117
|
+
call.reject("Incomplete blood pressure data", "NO_SAMPLE")
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let systolicValue = systolicSample.quantity.doubleValue(for: unit)
|
|
122
|
+
let diastolicValue = diastolicSample.quantity.doubleValue(for: unit)
|
|
123
|
+
let timestamp = bpCorrelation.startDate.timeIntervalSince1970 * 1000
|
|
124
|
+
|
|
125
|
+
call.resolve([
|
|
126
|
+
"systolic": systolicValue,
|
|
127
|
+
"diastolic": diastolicValue,
|
|
128
|
+
"timestamp": timestamp,
|
|
129
|
+
"unit": unit.unitString
|
|
130
|
+
])
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
healthStore.execute(query)
|
|
134
|
+
return
|
|
135
|
+
}
|
|
89
136
|
guard aggregateTypeToHKQuantityType(dataTypeString) != nil else {
|
|
90
137
|
call.reject("Invalid data type")
|
|
91
138
|
return
|
|
@@ -99,6 +146,10 @@ public class HealthPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
99
146
|
return HKObjectType.quantityType(forIdentifier: .bodyMass)
|
|
100
147
|
case "steps":
|
|
101
148
|
return HKObjectType.quantityType(forIdentifier: .stepCount)
|
|
149
|
+
case "hrv":
|
|
150
|
+
return HKObjectType.quantityType(forIdentifier: .heartRateVariabilitySDNN)
|
|
151
|
+
case "blood-pressure":
|
|
152
|
+
return nil // handled above
|
|
102
153
|
default:
|
|
103
154
|
return nil
|
|
104
155
|
}
|
|
@@ -130,6 +181,8 @@ public class HealthPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
130
181
|
unit = HKUnit.count().unitDivided(by: HKUnit.minute())
|
|
131
182
|
} else if dataTypeString == "weight" {
|
|
132
183
|
unit = .gramUnit(with: .kilo)
|
|
184
|
+
} else if dataTypeString == "hrv" {
|
|
185
|
+
unit = HKUnit.secondUnit(with: .milli)
|
|
133
186
|
}
|
|
134
187
|
|
|
135
188
|
let value = quantitySample.quantity.doubleValue(for: unit)
|
|
@@ -180,6 +233,13 @@ public class HealthPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
180
233
|
].compactMap{$0}
|
|
181
234
|
case "READ_MINDFULNESS":
|
|
182
235
|
return [HKObjectType.categoryType(forIdentifier: .mindfulSession)!].compactMap{$0}
|
|
236
|
+
case "READ_HRV":
|
|
237
|
+
return [HKObjectType.quantityType(forIdentifier: .heartRateVariabilitySDNN)].compactMap { $0 }
|
|
238
|
+
case "READ_BLOOD_PRESSURE":
|
|
239
|
+
return [
|
|
240
|
+
HKObjectType.quantityType(forIdentifier: .bloodPressureSystolic),
|
|
241
|
+
HKObjectType.quantityType(forIdentifier: .bloodPressureDiastolic)
|
|
242
|
+
].compactMap { $0 }
|
|
183
243
|
default:
|
|
184
244
|
return []
|
|
185
245
|
}
|
|
@@ -195,6 +255,8 @@ public class HealthPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
195
255
|
return HKObjectType.quantityType(forIdentifier: .heartRate)
|
|
196
256
|
case "weight":
|
|
197
257
|
return HKObjectType.quantityType(forIdentifier: .bodyMass)
|
|
258
|
+
case "hrv":
|
|
259
|
+
return HKObjectType.quantityType(forIdentifier: .heartRateVariabilitySDNN)
|
|
198
260
|
default:
|
|
199
261
|
return nil
|
|
200
262
|
}
|
package/package.json
CHANGED