@appeeky/expo-healthkit 0.1.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/CHANGELOG.md +26 -0
- package/LICENSE +92 -0
- package/README.md +453 -0
- package/android/build.gradle +22 -0
- package/android/src/main/AndroidManifest.xml +74 -0
- package/android/src/main/java/expo/modules/healthkit/ExpoHealthKitModule.kt +228 -0
- package/android/src/main/java/expo/modules/healthkit/HealthConnectMapping.kt +129 -0
- package/android/src/main/java/expo/modules/healthkit/HealthConnectNutrition.kt +152 -0
- package/android/src/main/java/expo/modules/healthkit/HealthConnectRationaleActivity.kt +62 -0
- package/android/src/main/java/expo/modules/healthkit/HealthConnectService.kt +1054 -0
- package/android/src/main/java/expo/modules/healthkit/HealthConnectUnits.kt +133 -0
- package/android/src/main/java/expo/modules/healthkit/HealthConnectWorkouts.kt +105 -0
- package/android/src/main/java/expo/modules/healthkit/HealthKitExceptions.kt +30 -0
- package/app.plugin.js +1 -0
- package/build/ExpoHealthKitModule.d.ts +39 -0
- package/build/ExpoHealthKitModule.d.ts.map +1 -0
- package/build/ExpoHealthKitModule.js +35 -0
- package/build/ExpoHealthKitModule.js.map +1 -0
- package/build/ExpoHealthKitModule.web.d.ts +39 -0
- package/build/ExpoHealthKitModule.web.d.ts.map +1 -0
- package/build/ExpoHealthKitModule.web.js +105 -0
- package/build/ExpoHealthKitModule.web.js.map +1 -0
- package/build/dates.d.ts +6 -0
- package/build/dates.d.ts.map +1 -0
- package/build/dates.js +13 -0
- package/build/dates.js.map +1 -0
- package/build/errors.d.ts +9 -0
- package/build/errors.d.ts.map +1 -0
- package/build/errors.js +18 -0
- package/build/errors.js.map +1 -0
- package/build/identifiers.d.ts +418 -0
- package/build/identifiers.d.ts.map +1 -0
- package/build/identifiers.js +402 -0
- package/build/identifiers.js.map +1 -0
- package/build/index.d.ts +454 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +418 -0
- package/build/index.js.map +1 -0
- package/build/types.d.ts +542 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/docs/banner.jpg +0 -0
- package/expo-module.config.json +9 -0
- package/ios/ExpoHealthKit.podspec +30 -0
- package/ios/ExpoHealthKitModule.swift +155 -0
- package/ios/HealthKitAdvancedQueries.swift +458 -0
- package/ios/HealthKitExceptions.swift +109 -0
- package/ios/HealthKitIdentifiers.swift +183 -0
- package/ios/HealthKitRecords.swift +214 -0
- package/ios/HealthKitService.swift +626 -0
- package/ios/PrivacyInfo.xcprivacy +14 -0
- package/package.json +105 -0
- package/plugin/build/index.d.ts +30 -0
- package/plugin/build/index.js +138 -0
- package/src/ExpoHealthKitModule.ts +116 -0
- package/src/ExpoHealthKitModule.web.ts +140 -0
- package/src/dates.ts +17 -0
- package/src/errors.ts +22 -0
- package/src/identifiers.ts +494 -0
- package/src/index.ts +563 -0
- package/src/types.ts +625 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
|
|
3
|
+
internal final class HealthUnavailableException: Exception, @unchecked Sendable {
|
|
4
|
+
override var reason: String {
|
|
5
|
+
"HealthKit is not available on this device"
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
internal final class EmptyPermissionsException: Exception, @unchecked Sendable {
|
|
10
|
+
override var reason: String {
|
|
11
|
+
"Provide at least one HealthKit type in toRead or toShare"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
internal final class InvalidIdentifierException: GenericException<String>, @unchecked Sendable {
|
|
16
|
+
override var reason: String {
|
|
17
|
+
"Unknown or unsupported HealthKit identifier: \(param)"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
internal final class InvalidUnitException: GenericException<String>, @unchecked Sendable {
|
|
22
|
+
override var reason: String {
|
|
23
|
+
"Invalid HealthKit unit string: \(param)"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
internal final class InvalidDateException: GenericException<String>, @unchecked Sendable {
|
|
28
|
+
override var reason: String {
|
|
29
|
+
"Invalid ISO-8601 date: \(param)"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
internal final class InvalidSampleTypeException: GenericException<String>, @unchecked Sendable {
|
|
34
|
+
override var reason: String {
|
|
35
|
+
"Identifier is not a writable sample type: \(param)"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
internal final class MissingDeleteTypeException: Exception, @unchecked Sendable {
|
|
40
|
+
override var reason: String {
|
|
41
|
+
"deleteObjects requires a type, optionally with uuid or a date range"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
internal final class MissingUuidException: Exception, @unchecked Sendable {
|
|
46
|
+
override var reason: String {
|
|
47
|
+
"uuid is not a valid UUID string"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
internal final class SampleNotFoundException: GenericException<String>, @unchecked Sendable {
|
|
52
|
+
override var reason: String {
|
|
53
|
+
"No HealthKit sample found for uuid: \(param)"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
internal final class EmptyCorrelationException: Exception, @unchecked Sendable {
|
|
58
|
+
override var reason: String {
|
|
59
|
+
"saveCorrelation requires at least one quantity object"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
internal final class HealthKitNativeException: GenericException<String>, @unchecked Sendable {
|
|
64
|
+
override var code: String {
|
|
65
|
+
"ERR_HEALTHKIT_NATIVE"
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
override var reason: String {
|
|
69
|
+
param
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
func catchingHealthKit<T>(_ work: () throws -> T) throws -> T {
|
|
74
|
+
var result: Result<T, Error>?
|
|
75
|
+
do {
|
|
76
|
+
try withoutActuallyEscaping(work) { escapingWork in
|
|
77
|
+
try EXUtilities.catchException {
|
|
78
|
+
do {
|
|
79
|
+
result = .success(try escapingWork())
|
|
80
|
+
} catch {
|
|
81
|
+
result = .failure(error)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
} catch {
|
|
86
|
+
throw HealthKitNativeException(healthKitNativeMessage(error))
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
switch result {
|
|
90
|
+
case .success(let value):
|
|
91
|
+
return value
|
|
92
|
+
case .failure(let error):
|
|
93
|
+
throw error
|
|
94
|
+
case nil:
|
|
95
|
+
throw HealthKitNativeException("HealthKit failed without a result")
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private func healthKitNativeMessage(_ error: Error) -> String {
|
|
100
|
+
let nsError = error as NSError
|
|
101
|
+
let name = nsError.domain
|
|
102
|
+
if let description = nsError.userInfo[NSLocalizedDescriptionKey] as? String, !description.isEmpty {
|
|
103
|
+
return name.isEmpty || description.contains(name) ? description : "\(name): \(description)"
|
|
104
|
+
}
|
|
105
|
+
if name.isEmpty {
|
|
106
|
+
return nsError.localizedDescription
|
|
107
|
+
}
|
|
108
|
+
return "HealthKit raised \(name). The query arguments are invalid."
|
|
109
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import HealthKit
|
|
2
|
+
|
|
3
|
+
internal enum HealthKitIdentifiers {
|
|
4
|
+
static func objectType(for identifier: String) throws -> HKObjectType {
|
|
5
|
+
if identifier == "HKWorkoutTypeIdentifier" {
|
|
6
|
+
return HKObjectType.workoutType()
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
if identifier.hasPrefix("HKQuantityTypeIdentifier"),
|
|
10
|
+
let type = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier(rawValue: identifier))
|
|
11
|
+
{
|
|
12
|
+
return type
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if identifier.hasPrefix("HKCategoryTypeIdentifier"),
|
|
16
|
+
let type = HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier(rawValue: identifier))
|
|
17
|
+
{
|
|
18
|
+
return type
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if identifier.hasPrefix("HKCharacteristicTypeIdentifier"),
|
|
22
|
+
let type = HKObjectType.characteristicType(forIdentifier: HKCharacteristicTypeIdentifier(rawValue: identifier))
|
|
23
|
+
{
|
|
24
|
+
return type
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if identifier.hasPrefix("HKCorrelationTypeIdentifier"),
|
|
28
|
+
let type = HKObjectType.correlationType(forIdentifier: HKCorrelationTypeIdentifier(rawValue: identifier))
|
|
29
|
+
{
|
|
30
|
+
return type
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if identifier == "HKElectrocardiogramTypeIdentifier" {
|
|
34
|
+
return HKObjectType.electrocardiogramType()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if identifier == "HKAudiogramSampleTypeIdentifier" {
|
|
38
|
+
return HKObjectType.audiogramSampleType()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if identifier == "HKWorkoutRouteTypeIdentifier" {
|
|
42
|
+
return HKSeriesType.workoutRoute()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if identifier == "HKDataTypeIdentifierHeartbeatSeries" {
|
|
46
|
+
return HKSeriesType.heartbeat()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if identifier == "HKActivitySummaryTypeIdentifier" {
|
|
50
|
+
return HKObjectType.activitySummaryType()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if identifier.hasPrefix("HKClinicalTypeIdentifier"),
|
|
54
|
+
let type = HKObjectType.clinicalType(forIdentifier: HKClinicalTypeIdentifier(rawValue: identifier))
|
|
55
|
+
{
|
|
56
|
+
return type
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
throw InvalidIdentifierException(identifier)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
static func sampleType(for identifier: String) throws -> HKSampleType {
|
|
63
|
+
guard let type = try objectType(for: identifier) as? HKSampleType else {
|
|
64
|
+
throw InvalidSampleTypeException(identifier)
|
|
65
|
+
}
|
|
66
|
+
return type
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
static func quantityType(for identifier: String) throws -> HKQuantityType {
|
|
70
|
+
guard let type = try objectType(for: identifier) as? HKQuantityType else {
|
|
71
|
+
throw InvalidIdentifierException(identifier)
|
|
72
|
+
}
|
|
73
|
+
return type
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static func categoryType(for identifier: String) throws -> HKCategoryType {
|
|
77
|
+
guard let type = try objectType(for: identifier) as? HKCategoryType else {
|
|
78
|
+
throw InvalidIdentifierException(identifier)
|
|
79
|
+
}
|
|
80
|
+
return type
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
static func unit(from string: String) throws -> HKUnit {
|
|
84
|
+
let unit = HKUnit(from: string)
|
|
85
|
+
if unit.unitString.isEmpty {
|
|
86
|
+
throw InvalidUnitException(string)
|
|
87
|
+
}
|
|
88
|
+
return unit
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
static func date(from value: String) throws -> Date {
|
|
92
|
+
let withFractional = ISO8601DateFormatter()
|
|
93
|
+
withFractional.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
|
|
94
|
+
if let date = withFractional.date(from: value) {
|
|
95
|
+
return date
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let withoutFractional = ISO8601DateFormatter()
|
|
99
|
+
withoutFractional.formatOptions = [.withInternetDateTime]
|
|
100
|
+
if let date = withoutFractional.date(from: value) {
|
|
101
|
+
return date
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
throw InvalidDateException(value)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
static func optionalDate(from value: String?) throws -> Date? {
|
|
108
|
+
guard let value else {
|
|
109
|
+
return nil
|
|
110
|
+
}
|
|
111
|
+
return try date(from: value)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
static func isoString(from date: Date) -> String {
|
|
115
|
+
let formatter = ISO8601DateFormatter()
|
|
116
|
+
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
|
|
117
|
+
return formatter.string(from: date)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
static func queryLimit(_ limit: Int) -> Int {
|
|
121
|
+
limit <= 0 ? HKObjectQueryNoLimit : limit
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
static func statisticsOptions(from raw: Int, quantityType: HKQuantityType) -> HKStatisticsOptions {
|
|
125
|
+
if raw == 0 {
|
|
126
|
+
return quantityType.aggregationStyle == .cumulative ? .cumulativeSum : .discreteAverage
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
var options: HKStatisticsOptions = []
|
|
130
|
+
if raw & 1 != 0 { options.insert(.discreteAverage) }
|
|
131
|
+
if raw & 2 != 0 { options.insert(.discreteMin) }
|
|
132
|
+
if raw & 4 != 0 { options.insert(.discreteMax) }
|
|
133
|
+
if raw & 8 != 0 { options.insert(.cumulativeSum) }
|
|
134
|
+
if raw & 32 != 0 { options.insert(.discreteMostRecent) }
|
|
135
|
+
return options
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
static func displayUnit(for quantityType: HKQuantityType) -> HKUnit {
|
|
139
|
+
let candidates: [HKUnit] = [
|
|
140
|
+
HKUnit.millimeterOfMercury(),
|
|
141
|
+
HKUnit.percent(),
|
|
142
|
+
HKUnit.count().unitDivided(by: .minute()),
|
|
143
|
+
HKUnit.count(),
|
|
144
|
+
HKUnit.kilocalorie(),
|
|
145
|
+
HKUnit.internationalUnit(),
|
|
146
|
+
HKUnit.gram(),
|
|
147
|
+
HKUnit.literUnit(with: .milli),
|
|
148
|
+
HKUnit.meter(),
|
|
149
|
+
HKUnit.degreeCelsius(),
|
|
150
|
+
HKUnit.siemenUnit(with: .micro),
|
|
151
|
+
HKUnit.decibelAWeightedSoundPressureLevel(),
|
|
152
|
+
HKUnit.second()
|
|
153
|
+
]
|
|
154
|
+
return candidates.first { quantityType.is(compatibleWith: $0) } ?? HKUnit.count()
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
static func stringifyMetadata(_ metadata: [String: Any]?) -> [String: String]? {
|
|
158
|
+
guard let metadata, !metadata.isEmpty else {
|
|
159
|
+
return nil
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
var result: [String: String] = [:]
|
|
163
|
+
for (key, value) in metadata {
|
|
164
|
+
result[key] = String(describing: value)
|
|
165
|
+
}
|
|
166
|
+
return result
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
static func encodeAnchor(_ anchor: HKQueryAnchor?) throws -> String? {
|
|
170
|
+
guard let anchor else {
|
|
171
|
+
return nil
|
|
172
|
+
}
|
|
173
|
+
let data = try NSKeyedArchiver.archivedData(withRootObject: anchor, requiringSecureCoding: true)
|
|
174
|
+
return data.base64EncodedString()
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
static func decodeAnchor(_ value: String?) throws -> HKQueryAnchor? {
|
|
178
|
+
guard let value, let data = Data(base64Encoded: value) else {
|
|
179
|
+
return nil
|
|
180
|
+
}
|
|
181
|
+
return try NSKeyedUnarchiver.unarchivedObject(ofClass: HKQueryAnchor.self, from: data)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
|
|
3
|
+
internal struct AuthorizationOptions: Record {
|
|
4
|
+
@Field var toRead: [String] = []
|
|
5
|
+
@Field var toShare: [String] = []
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
internal struct QuantityQueryOptions: Record {
|
|
9
|
+
@Field var type: String = ""
|
|
10
|
+
@Field var unit: String = ""
|
|
11
|
+
@Field var from: String?
|
|
12
|
+
@Field var to: String?
|
|
13
|
+
@Field var limit: Int = 0
|
|
14
|
+
@Field var ascending: Bool = false
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
internal struct CategoryQueryOptions: Record {
|
|
18
|
+
@Field var type: String = ""
|
|
19
|
+
@Field var from: String?
|
|
20
|
+
@Field var to: String?
|
|
21
|
+
@Field var limit: Int = 0
|
|
22
|
+
@Field var ascending: Bool = false
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
internal struct WorkoutQueryOptions: Record {
|
|
26
|
+
@Field var from: String?
|
|
27
|
+
@Field var to: String?
|
|
28
|
+
@Field var limit: Int = 0
|
|
29
|
+
@Field var ascending: Bool = false
|
|
30
|
+
@Field var activityType: Int?
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
internal struct StatisticsQueryOptions: Record {
|
|
34
|
+
@Field var type: String = ""
|
|
35
|
+
@Field var unit: String = ""
|
|
36
|
+
@Field var from: String?
|
|
37
|
+
@Field var to: String?
|
|
38
|
+
@Field var options: Int = 0
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
internal struct StatisticsCollectionQueryOptions: Record {
|
|
42
|
+
@Field var type: String = ""
|
|
43
|
+
@Field var unit: String = ""
|
|
44
|
+
@Field var from: String?
|
|
45
|
+
@Field var to: String?
|
|
46
|
+
@Field var options: Int = 0
|
|
47
|
+
@Field var year: Int = 0
|
|
48
|
+
@Field var month: Int = 0
|
|
49
|
+
@Field var day: Int = 0
|
|
50
|
+
@Field var hour: Int = 0
|
|
51
|
+
@Field var minute: Int = 0
|
|
52
|
+
@Field var second: Int = 0
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
internal struct AnchoredQueryOptions: Record {
|
|
56
|
+
@Field var type: String = ""
|
|
57
|
+
@Field var unit: String?
|
|
58
|
+
@Field var from: String?
|
|
59
|
+
@Field var to: String?
|
|
60
|
+
@Field var limit: Int = 0
|
|
61
|
+
@Field var anchor: String?
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
internal struct QuantitySampleInput: Record {
|
|
65
|
+
@Field var type: String = ""
|
|
66
|
+
@Field var unit: String = ""
|
|
67
|
+
@Field var value: Double = 0
|
|
68
|
+
@Field var startDate: String = ""
|
|
69
|
+
@Field var endDate: String = ""
|
|
70
|
+
@Field var metadata: [String: String]?
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
internal struct CategorySampleInput: Record {
|
|
74
|
+
@Field var type: String = ""
|
|
75
|
+
@Field var value: Int = 0
|
|
76
|
+
@Field var startDate: String = ""
|
|
77
|
+
@Field var endDate: String = ""
|
|
78
|
+
@Field var metadata: [String: String]?
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
internal struct WorkoutInput: Record {
|
|
82
|
+
@Field var activityType: Int = 0
|
|
83
|
+
@Field var startDate: String = ""
|
|
84
|
+
@Field var endDate: String = ""
|
|
85
|
+
@Field var energyBurned: Double?
|
|
86
|
+
@Field var energyBurnedUnit: String?
|
|
87
|
+
@Field var distance: Double?
|
|
88
|
+
@Field var distanceUnit: String?
|
|
89
|
+
@Field var metadata: [String: String]?
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
internal struct DeleteObjectsOptions: Record {
|
|
93
|
+
@Field var uuid: String?
|
|
94
|
+
@Field var type: String?
|
|
95
|
+
@Field var from: String?
|
|
96
|
+
@Field var to: String?
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
internal struct DateRangeQueryOptions: Record {
|
|
100
|
+
@Field var from: String?
|
|
101
|
+
@Field var to: String?
|
|
102
|
+
@Field var limit: Int = 0
|
|
103
|
+
@Field var ascending: Bool = false
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
internal struct ElectrocardiogramQueryOptions: Record {
|
|
107
|
+
@Field var from: String?
|
|
108
|
+
@Field var to: String?
|
|
109
|
+
@Field var limit: Int = 0
|
|
110
|
+
@Field var ascending: Bool = false
|
|
111
|
+
@Field var includeVoltage: Bool = false
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
internal struct ActivitySummaryQueryOptions: Record {
|
|
115
|
+
@Field var from: String?
|
|
116
|
+
@Field var to: String?
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
internal struct ClinicalRecordQueryOptions: Record {
|
|
120
|
+
@Field var type: String = ""
|
|
121
|
+
@Field var from: String?
|
|
122
|
+
@Field var to: String?
|
|
123
|
+
@Field var limit: Int = 0
|
|
124
|
+
@Field var ascending: Bool = false
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
internal struct WorkoutRouteQueryOptions: Record {
|
|
128
|
+
@Field var workoutUUID: String = ""
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
internal struct CorrelationQueryOptions: Record {
|
|
132
|
+
@Field var type: String = ""
|
|
133
|
+
@Field var from: String?
|
|
134
|
+
@Field var to: String?
|
|
135
|
+
@Field var limit: Int = 0
|
|
136
|
+
@Field var ascending: Bool = false
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
internal struct CorrelationInput: Record {
|
|
140
|
+
@Field var type: String = ""
|
|
141
|
+
@Field var startDate: String = ""
|
|
142
|
+
@Field var endDate: String = ""
|
|
143
|
+
@Field var objects: [QuantitySampleInput] = []
|
|
144
|
+
@Field var metadata: [String: String]?
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
internal struct HeartbeatSeriesQueryOptions: Record {
|
|
148
|
+
@Field var from: String?
|
|
149
|
+
@Field var to: String?
|
|
150
|
+
@Field var limit: Int = 0
|
|
151
|
+
@Field var ascending: Bool = false
|
|
152
|
+
@Field var includeBeats: Bool = true
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
internal struct QuantitySampleRecord: Record {
|
|
156
|
+
@Field var uuid: String = ""
|
|
157
|
+
@Field var type: String = ""
|
|
158
|
+
@Field var startDate: String = ""
|
|
159
|
+
@Field var endDate: String = ""
|
|
160
|
+
@Field var value: Double = 0
|
|
161
|
+
@Field var unit: String = ""
|
|
162
|
+
@Field var sourceName: String?
|
|
163
|
+
@Field var sourceId: String?
|
|
164
|
+
@Field var metadata: [String: String]?
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
internal struct CategorySampleRecord: Record {
|
|
168
|
+
@Field var uuid: String = ""
|
|
169
|
+
@Field var type: String = ""
|
|
170
|
+
@Field var startDate: String = ""
|
|
171
|
+
@Field var endDate: String = ""
|
|
172
|
+
@Field var value: Int = 0
|
|
173
|
+
@Field var sourceName: String?
|
|
174
|
+
@Field var sourceId: String?
|
|
175
|
+
@Field var metadata: [String: String]?
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
internal struct WorkoutSampleRecord: Record {
|
|
179
|
+
@Field var uuid: String = ""
|
|
180
|
+
@Field var type: String = "HKWorkoutTypeIdentifier"
|
|
181
|
+
@Field var startDate: String = ""
|
|
182
|
+
@Field var endDate: String = ""
|
|
183
|
+
@Field var duration: Double = 0
|
|
184
|
+
@Field var workoutActivityType: Int = 0
|
|
185
|
+
@Field var totalEnergyBurned: Double?
|
|
186
|
+
@Field var totalEnergyBurnedUnit: String?
|
|
187
|
+
@Field var totalDistance: Double?
|
|
188
|
+
@Field var totalDistanceUnit: String?
|
|
189
|
+
@Field var sourceName: String?
|
|
190
|
+
@Field var sourceId: String?
|
|
191
|
+
@Field var metadata: [String: String]?
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
internal struct DeletedSampleRecord: Record {
|
|
195
|
+
@Field var uuid: String = ""
|
|
196
|
+
@Field var type: String?
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
internal struct StatisticsRecord: Record {
|
|
200
|
+
@Field var startDate: String = ""
|
|
201
|
+
@Field var endDate: String = ""
|
|
202
|
+
@Field var unit: String = ""
|
|
203
|
+
@Field var sum: Double?
|
|
204
|
+
@Field var min: Double?
|
|
205
|
+
@Field var max: Double?
|
|
206
|
+
@Field var average: Double?
|
|
207
|
+
@Field var mostRecent: Double?
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
internal struct AnchoredQueryResultRecord: Record {
|
|
211
|
+
@Field var added: [QuantitySampleRecord] = []
|
|
212
|
+
@Field var deleted: [DeletedSampleRecord] = []
|
|
213
|
+
@Field var anchor: String?
|
|
214
|
+
}
|