@tryterra/react-native 2.1.0 → 2.2.1
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 +31 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/Conversions.kt +103 -14
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/ReadConversions.kt +250 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TerraBridgeOpsImpl.kt +47 -5
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TerraBridgeStreamsImpl.kt +1 -3
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/generated/BridgeOps.kt +89 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/generated/BridgeTypes.kt +672 -0
- package/docs/availability.md +154 -4
- package/frameworks/TerraSDK.xcframework/Info.plist +5 -5
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Info.plist +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.abi.json +1385 -226
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.package.swiftinterface +37 -1
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +37 -1
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.swiftinterface +37 -1
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/TerraSDK +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/dSYMs/TerraSDK.framework.dSYM/Contents/Info.plist +2 -2
- package/frameworks/TerraSDK.xcframework/ios-arm64/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/DWARF/TerraSDK +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/Relocations/aarch64/TerraSDK.yml +6589 -6468
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Info.plist +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +1385 -226
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.package.swiftinterface +37 -1
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +37 -1
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +37 -1
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +1385 -226
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.package.swiftinterface +37 -1
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +37 -1
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +37 -1
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/TerraSDK +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/_CodeSignature/CodeResources +21 -21
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Info.plist +2 -2
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/DWARF/TerraSDK +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/Relocations/aarch64/TerraSDK.yml +6590 -6469
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/Relocations/x86_64/TerraSDK.yml +6508 -6387
- package/ios/TerraBridgeCore/Conversions+Reads.swift +259 -0
- package/ios/TerraBridgeCore/Conversions.swift +129 -2
- package/ios/TerraBridgeCore/Generated/BridgeOps.swift +172 -0
- package/ios/TerraBridgeCore/Generated/BridgeTypes.swift +734 -1
- package/ios/TerraBridgeCore/TerraBridgeOpsImpl.swift +64 -0
- package/lib/Terra.d.ts +74 -5
- package/lib/Terra.js +69 -13
- package/lib/generated/client.d.ts +37 -1
- package/lib/generated/client.js +60 -0
- package/lib/generated/codecs.d.ts +85 -1
- package/lib/generated/codecs.js +710 -2
- package/lib/generated/ops.d.ts +1 -1
- package/lib/generated/ops.js +24 -0
- package/lib/generated/types.d.ts +314 -1
- package/lib/generated/types.js +133 -1
- package/lib/index.d.ts +1 -1
- package/package.json +3 -3
- package/src/Terra.ts +146 -16
- package/src/generated/client.ts +74 -2
- package/src/generated/codecs.ts +705 -3
- package/src/generated/ops.ts +36 -0
- package/src/generated/types.ts +556 -1
- package/src/index.ts +2 -0
|
@@ -136,6 +136,13 @@ public enum DataCategory: String, Codable, Sendable, CaseIterable {
|
|
|
136
136
|
case daily = "daily"
|
|
137
137
|
case sleep = "sleep"
|
|
138
138
|
case nutrition = "nutrition"
|
|
139
|
+
/// Read-only on both platforms: menstruation.flow reads it and neither SDK has a write route
|
|
140
|
+
/// for it.
|
|
141
|
+
case menstruation = "menstruation"
|
|
142
|
+
/// HealthKit's characteristics; Health Connect has no counterpart.
|
|
143
|
+
///
|
|
144
|
+
/// Available on ios only.
|
|
145
|
+
case athlete = "athlete"
|
|
139
146
|
}
|
|
140
147
|
|
|
141
148
|
public enum PermissionStatus: String, Codable, Sendable, CaseIterable {
|
|
@@ -158,6 +165,90 @@ public enum MotionContext: String, Codable, Sendable, CaseIterable {
|
|
|
158
165
|
case active = "active"
|
|
159
166
|
}
|
|
160
167
|
|
|
168
|
+
/// On both platforms, wire names strip HKQuantityTypeIdentifierDietary from HealthKit
|
|
169
|
+
/// identifiers and lowercase the first letter. Units: kcal for energyConsumed; grams for
|
|
170
|
+
/// protein, carbohydrates, the four fats, sugar and fiber; milligrams for everything else,
|
|
171
|
+
/// never micrograms.
|
|
172
|
+
public enum Nutrient: String, Codable, Sendable, CaseIterable {
|
|
173
|
+
case energyConsumed = "energyConsumed"
|
|
174
|
+
case protein = "protein"
|
|
175
|
+
case carbohydrates = "carbohydrates"
|
|
176
|
+
case fatTotal = "fatTotal"
|
|
177
|
+
case fatSaturated = "fatSaturated"
|
|
178
|
+
case fatMonounsaturated = "fatMonounsaturated"
|
|
179
|
+
case fatPolyunsaturated = "fatPolyunsaturated"
|
|
180
|
+
case sugar = "sugar"
|
|
181
|
+
case fiber = "fiber"
|
|
182
|
+
case cholesterol = "cholesterol"
|
|
183
|
+
case sodium = "sodium"
|
|
184
|
+
case potassium = "potassium"
|
|
185
|
+
case calcium = "calcium"
|
|
186
|
+
case iron = "iron"
|
|
187
|
+
case magnesium = "magnesium"
|
|
188
|
+
case zinc = "zinc"
|
|
189
|
+
case phosphorus = "phosphorus"
|
|
190
|
+
case iodine = "iodine"
|
|
191
|
+
case manganese = "manganese"
|
|
192
|
+
case molybdenum = "molybdenum"
|
|
193
|
+
case selenium = "selenium"
|
|
194
|
+
case copper = "copper"
|
|
195
|
+
case chromium = "chromium"
|
|
196
|
+
case chloride = "chloride"
|
|
197
|
+
case caffeine = "caffeine"
|
|
198
|
+
case vitaminA = "vitaminA"
|
|
199
|
+
case vitaminC = "vitaminC"
|
|
200
|
+
case vitaminD = "vitaminD"
|
|
201
|
+
case vitaminE = "vitaminE"
|
|
202
|
+
case vitaminK = "vitaminK"
|
|
203
|
+
case vitaminB6 = "vitaminB6"
|
|
204
|
+
case vitaminB12 = "vitaminB12"
|
|
205
|
+
case thiamin = "thiamin"
|
|
206
|
+
case riboflavin = "riboflavin"
|
|
207
|
+
case niacin = "niacin"
|
|
208
|
+
case folate = "folate"
|
|
209
|
+
case biotin = "biotin"
|
|
210
|
+
case pantothenicAcid = "pantothenicAcid"
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/// Which meal a quantity was logged against. unknown is the store's explicit answer, not an
|
|
214
|
+
/// absence: HealthKit has no meal type at all, so every iOS quantity is unknown.
|
|
215
|
+
public enum MealType: String, Codable, Sendable, CaseIterable {
|
|
216
|
+
case breakfast = "breakfast"
|
|
217
|
+
case lunch = "lunch"
|
|
218
|
+
case dinner = "dinner"
|
|
219
|
+
case snack = "snack"
|
|
220
|
+
case unknown = "unknown"
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/// How heavy a logged day's flow was.
|
|
224
|
+
public enum MenstruationFlow: String, Codable, Sendable, CaseIterable {
|
|
225
|
+
case unspecified = "unspecified"
|
|
226
|
+
case light = "light"
|
|
227
|
+
case medium = "medium"
|
|
228
|
+
case heavy = "heavy"
|
|
229
|
+
/// noFlow avoids ambiguity with Optional.none. Health Connect has no constant for it, so
|
|
230
|
+
/// Android never produces it.
|
|
231
|
+
case noFlow = "none"
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/// Available on ios only.
|
|
235
|
+
public enum BiologicalSex: String, Codable, Sendable, CaseIterable {
|
|
236
|
+
case female = "female"
|
|
237
|
+
case male = "male"
|
|
238
|
+
case other = "other"
|
|
239
|
+
/// The characteristic exists and holds no value, or reading it failed. The store's explicit
|
|
240
|
+
/// unknown.
|
|
241
|
+
case notSet = "notSet"
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/// A move-time user's daily totals are minutes rather than active energy.
|
|
245
|
+
///
|
|
246
|
+
/// Available on ios only.
|
|
247
|
+
public enum ActivityMoveMode: String, Codable, Sendable, CaseIterable {
|
|
248
|
+
case activeEnergy = "activeEnergy"
|
|
249
|
+
case appleMoveTime = "appleMoveTime"
|
|
250
|
+
}
|
|
251
|
+
|
|
161
252
|
/// Why background delivery is restricted. Android discloses which; Apple reports the state
|
|
162
253
|
/// without a reason.
|
|
163
254
|
///
|
|
@@ -241,6 +332,110 @@ public enum WorkoutSchedulingStatus: String, Codable, Sendable, CaseIterable {
|
|
|
241
332
|
case authorized = "authorized"
|
|
242
333
|
}
|
|
243
334
|
|
|
335
|
+
/// distance* are metres, *EnergyBurned kilocalories, flightsClimbed a count, and
|
|
336
|
+
/// appleExerciseTime minutes.
|
|
337
|
+
public enum DayTotalMetric: String, Codable, Sendable, CaseIterable {
|
|
338
|
+
case distanceWalkingRunning = "distanceWalkingRunning"
|
|
339
|
+
/// Health Connect does not split distance by activity, so only Apple ever answers it.
|
|
340
|
+
case distanceCycling = "distanceCycling"
|
|
341
|
+
case distanceSwimming = "distanceSwimming"
|
|
342
|
+
case distanceWheelchair = "distanceWheelchair"
|
|
343
|
+
case activeEnergyBurned = "activeEnergyBurned"
|
|
344
|
+
case basalEnergyBurned = "basalEnergyBurned"
|
|
345
|
+
case flightsClimbed = "flightsClimbed"
|
|
346
|
+
/// The exercise ring, in minutes. Health Connect has no such figure: summing session durations
|
|
347
|
+
/// is a different number.
|
|
348
|
+
///
|
|
349
|
+
/// Available on ios only.
|
|
350
|
+
case appleExerciseTime = "appleExerciseTime"
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/// activeEnergy and basalEnergy are kilocalories, distance metres, floorsClimbed a count.
|
|
354
|
+
public enum IntradayMetric: String, Codable, Sendable, CaseIterable {
|
|
355
|
+
case activeEnergy = "activeEnergy"
|
|
356
|
+
/// Health Connect has no basal energy record; its basal metabolic rate is a body measurement
|
|
357
|
+
/// and is not this.
|
|
358
|
+
///
|
|
359
|
+
/// Available on ios only.
|
|
360
|
+
case basalEnergy = "basalEnergy"
|
|
361
|
+
case distance = "distance"
|
|
362
|
+
case floorsClimbed = "floorsClimbed"
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/// speed is metres per second, power watts, cadence pedal revolutions per minute.
|
|
366
|
+
public enum WorkoutIntensityMetric: String, Codable, Sendable, CaseIterable {
|
|
367
|
+
case speed = "speed"
|
|
368
|
+
case power = "power"
|
|
369
|
+
case cadence = "cadence"
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/// The body composition figure a measurement carries. weight and leanBodyMass are kilograms,
|
|
373
|
+
/// height centimetres, bodyFatPercentage a fraction in 0..1, bmi dimensionless.
|
|
374
|
+
public enum BodyMetric: String, Codable, Sendable, CaseIterable {
|
|
375
|
+
case weight = "weight"
|
|
376
|
+
case height = "height"
|
|
377
|
+
case bodyFatPercentage = "bodyFatPercentage"
|
|
378
|
+
case leanBodyMass = "leanBodyMass"
|
|
379
|
+
/// Health Connect has no BMI record.
|
|
380
|
+
///
|
|
381
|
+
/// Available on ios only.
|
|
382
|
+
case bmi = "bmi"
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/// The unit the writing app chose. Never converted on the device.
|
|
386
|
+
public enum BloodGlucoseUnit: String, Codable, Sendable, CaseIterable {
|
|
387
|
+
case milligramsPerDecilitre = "mg/dL"
|
|
388
|
+
case millimolesPerLitre = "mmol/L"
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/// unknown is the store saying nothing about the meal, distinct from absent.
|
|
392
|
+
public enum GlucoseMealTime: String, Codable, Sendable, CaseIterable {
|
|
393
|
+
case preprandial = "preprandial"
|
|
394
|
+
case postprandial = "postprandial"
|
|
395
|
+
case unknown = "unknown"
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/// interstitial is a continuous monitor.
|
|
399
|
+
public enum GlucoseSpecimen: String, Codable, Sendable, CaseIterable {
|
|
400
|
+
case capillary = "capillary"
|
|
401
|
+
case interstitial = "interstitial"
|
|
402
|
+
case plasma = "plasma"
|
|
403
|
+
case serum = "serum"
|
|
404
|
+
case tears = "tears"
|
|
405
|
+
case wholeBlood = "wholeBlood"
|
|
406
|
+
case unknown = "unknown"
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/// body and basal are absolute; wrist and skin are deltas from a baseline and routinely
|
|
410
|
+
/// negative.
|
|
411
|
+
public enum BodyTemperatureMetric: String, Codable, Sendable, CaseIterable {
|
|
412
|
+
case body = "body"
|
|
413
|
+
case basal = "basal"
|
|
414
|
+
/// HealthKit's sleeping wrist temperature. Health Connect has no such record.
|
|
415
|
+
///
|
|
416
|
+
/// Available on ios only.
|
|
417
|
+
case wrist = "wrist"
|
|
418
|
+
case skin = "skin"
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/// HealthKit records SDNN only and Health Connect RMSSD only; they are never one series.
|
|
422
|
+
public enum HeartRateVariabilityMetric: String, Codable, Sendable, CaseIterable {
|
|
423
|
+
case sdnn = "sdnn"
|
|
424
|
+
case rmssd = "rmssd"
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
public enum VO2MaxTestType: String, Codable, Sendable, CaseIterable {
|
|
428
|
+
case maxExercise = "maxExercise"
|
|
429
|
+
case predictedSubMaxExercise = "predictedSubMaxExercise"
|
|
430
|
+
case predictedNonExercise = "predictedNonExercise"
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
public enum MenstrualEventKind: String, Codable, Sendable, CaseIterable {
|
|
434
|
+
case intermenstrualBleeding = "intermenstrualBleeding"
|
|
435
|
+
case ovulationTest = "ovulationTest"
|
|
436
|
+
case cervicalMucus = "cervicalMucus"
|
|
437
|
+
}
|
|
438
|
+
|
|
244
439
|
/// A `CodingKey` over a plain string, for the dictionary-key conformances below.
|
|
245
440
|
public struct StringCodingKey: CodingKey {
|
|
246
441
|
public let stringValue: String
|
|
@@ -576,6 +771,177 @@ public enum StreamMessage: Codable, Equatable, Sendable {
|
|
|
576
771
|
}
|
|
577
772
|
}
|
|
578
773
|
|
|
774
|
+
/// What a BodyReading measured, with the unit fixed by the variant. Blood glucose alone keeps
|
|
775
|
+
/// the writer's unit.
|
|
776
|
+
public enum BodyReadingKind: Codable, Equatable, Sendable {
|
|
777
|
+
case measurement(metric: BodyMetric, value: Double)
|
|
778
|
+
case bloodPressure(systolic: Double, diastolic: Double)
|
|
779
|
+
case bloodGlucose(value: Double, unit: BloodGlucoseUnit, mealTime: GlucoseMealTime?, specimen: GlucoseSpecimen?)
|
|
780
|
+
case oxygenSaturation(fraction: Double)
|
|
781
|
+
case temperature(metric: BodyTemperatureMetric, celsius: Double)
|
|
782
|
+
case heartRateVariability(metric: HeartRateVariabilityMetric, milliseconds: Double)
|
|
783
|
+
case respiratoryRate(breathsPerMinute: Double)
|
|
784
|
+
case vo2Max(millilitresPerKilogramMinute: Double, testType: VO2MaxTestType?)
|
|
785
|
+
|
|
786
|
+
private enum CodingKeys: String, CodingKey {
|
|
787
|
+
case type
|
|
788
|
+
case metric
|
|
789
|
+
case value
|
|
790
|
+
case systolic
|
|
791
|
+
case diastolic
|
|
792
|
+
case unit
|
|
793
|
+
case mealTime
|
|
794
|
+
case specimen
|
|
795
|
+
case fraction
|
|
796
|
+
case celsius
|
|
797
|
+
case milliseconds
|
|
798
|
+
case breathsPerMinute
|
|
799
|
+
case millilitresPerKilogramMinute
|
|
800
|
+
case testType
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
public init(from decoder: any Decoder) throws {
|
|
804
|
+
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
805
|
+
let tag = try container.decode(String.self, forKey: .type)
|
|
806
|
+
switch tag {
|
|
807
|
+
case "measurement":
|
|
808
|
+
let metric = try container.decode(BodyMetric.self, forKey: .metric)
|
|
809
|
+
let value = try container.decode(Double.self, forKey: .value)
|
|
810
|
+
self = .measurement(metric: metric, value: value)
|
|
811
|
+
case "bloodPressure":
|
|
812
|
+
let systolic = try container.decode(Double.self, forKey: .systolic)
|
|
813
|
+
let diastolic = try container.decode(Double.self, forKey: .diastolic)
|
|
814
|
+
self = .bloodPressure(systolic: systolic, diastolic: diastolic)
|
|
815
|
+
case "bloodGlucose":
|
|
816
|
+
let value = try container.decode(Double.self, forKey: .value)
|
|
817
|
+
let unit = try container.decode(BloodGlucoseUnit.self, forKey: .unit)
|
|
818
|
+
let mealTime = try container.decodeIfPresent(GlucoseMealTime.self, forKey: .mealTime)
|
|
819
|
+
let specimen = try container.decodeIfPresent(GlucoseSpecimen.self, forKey: .specimen)
|
|
820
|
+
self = .bloodGlucose(value: value, unit: unit, mealTime: mealTime, specimen: specimen)
|
|
821
|
+
case "oxygenSaturation":
|
|
822
|
+
let fraction = try container.decode(Double.self, forKey: .fraction)
|
|
823
|
+
self = .oxygenSaturation(fraction: fraction)
|
|
824
|
+
case "temperature":
|
|
825
|
+
let metric = try container.decode(BodyTemperatureMetric.self, forKey: .metric)
|
|
826
|
+
let celsius = try container.decode(Double.self, forKey: .celsius)
|
|
827
|
+
self = .temperature(metric: metric, celsius: celsius)
|
|
828
|
+
case "heartRateVariability":
|
|
829
|
+
let metric = try container.decode(HeartRateVariabilityMetric.self, forKey: .metric)
|
|
830
|
+
let milliseconds = try container.decode(Double.self, forKey: .milliseconds)
|
|
831
|
+
self = .heartRateVariability(metric: metric, milliseconds: milliseconds)
|
|
832
|
+
case "respiratoryRate":
|
|
833
|
+
let breathsPerMinute = try container.decode(Double.self, forKey: .breathsPerMinute)
|
|
834
|
+
self = .respiratoryRate(breathsPerMinute: breathsPerMinute)
|
|
835
|
+
case "vo2Max":
|
|
836
|
+
let millilitresPerKilogramMinute = try container.decode(Double.self, forKey: .millilitresPerKilogramMinute)
|
|
837
|
+
let testType = try container.decodeIfPresent(VO2MaxTestType.self, forKey: .testType)
|
|
838
|
+
self = .vo2Max(millilitresPerKilogramMinute: millilitresPerKilogramMinute, testType: testType)
|
|
839
|
+
default:
|
|
840
|
+
throw DecodingError.dataCorruptedError(forKey: .type, in: container, debugDescription: "unknown BodyReadingKind \(tag)")
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
public func encode(to encoder: any Encoder) throws {
|
|
845
|
+
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
846
|
+
switch self {
|
|
847
|
+
case .measurement(let metric, let value):
|
|
848
|
+
try container.encode("measurement", forKey: .type)
|
|
849
|
+
try container.encode(metric, forKey: .metric)
|
|
850
|
+
try container.encode(value, forKey: .value)
|
|
851
|
+
case .bloodPressure(let systolic, let diastolic):
|
|
852
|
+
try container.encode("bloodPressure", forKey: .type)
|
|
853
|
+
try container.encode(systolic, forKey: .systolic)
|
|
854
|
+
try container.encode(diastolic, forKey: .diastolic)
|
|
855
|
+
case .bloodGlucose(let value, let unit, let mealTime, let specimen):
|
|
856
|
+
try container.encode("bloodGlucose", forKey: .type)
|
|
857
|
+
try container.encode(value, forKey: .value)
|
|
858
|
+
try container.encode(unit, forKey: .unit)
|
|
859
|
+
try container.encodeIfPresent(mealTime, forKey: .mealTime)
|
|
860
|
+
try container.encodeIfPresent(specimen, forKey: .specimen)
|
|
861
|
+
case .oxygenSaturation(let fraction):
|
|
862
|
+
try container.encode("oxygenSaturation", forKey: .type)
|
|
863
|
+
try container.encode(fraction, forKey: .fraction)
|
|
864
|
+
case .temperature(let metric, let celsius):
|
|
865
|
+
try container.encode("temperature", forKey: .type)
|
|
866
|
+
try container.encode(metric, forKey: .metric)
|
|
867
|
+
try container.encode(celsius, forKey: .celsius)
|
|
868
|
+
case .heartRateVariability(let metric, let milliseconds):
|
|
869
|
+
try container.encode("heartRateVariability", forKey: .type)
|
|
870
|
+
try container.encode(metric, forKey: .metric)
|
|
871
|
+
try container.encode(milliseconds, forKey: .milliseconds)
|
|
872
|
+
case .respiratoryRate(let breathsPerMinute):
|
|
873
|
+
try container.encode("respiratoryRate", forKey: .type)
|
|
874
|
+
try container.encode(breathsPerMinute, forKey: .breathsPerMinute)
|
|
875
|
+
case .vo2Max(let millilitresPerKilogramMinute, let testType):
|
|
876
|
+
try container.encode("vo2Max", forKey: .type)
|
|
877
|
+
try container.encode(millilitresPerKilogramMinute, forKey: .millilitresPerKilogramMinute)
|
|
878
|
+
try container.encodeIfPresent(testType, forKey: .testType)
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
/// Apple's store is part of the OS, so it is there or the device cannot have it; Health Connect
|
|
884
|
+
/// is an app a person can lack.
|
|
885
|
+
public enum StoreAvailability: Codable, Equatable, Sendable {
|
|
886
|
+
case available
|
|
887
|
+
case unsupportedDevice
|
|
888
|
+
case providerAppMissing
|
|
889
|
+
case providerAppTooOld(installed: String)
|
|
890
|
+
case moduleMissing
|
|
891
|
+
case partnerPolicyMissing
|
|
892
|
+
case featureUnavailable
|
|
893
|
+
|
|
894
|
+
private enum CodingKeys: String, CodingKey {
|
|
895
|
+
case kind
|
|
896
|
+
case installed
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
public init(from decoder: any Decoder) throws {
|
|
900
|
+
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
901
|
+
let tag = try container.decode(String.self, forKey: .kind)
|
|
902
|
+
switch tag {
|
|
903
|
+
case "available":
|
|
904
|
+
self = .available
|
|
905
|
+
case "unsupportedDevice":
|
|
906
|
+
self = .unsupportedDevice
|
|
907
|
+
case "providerAppMissing":
|
|
908
|
+
self = .providerAppMissing
|
|
909
|
+
case "providerAppTooOld":
|
|
910
|
+
let installed = try container.decode(String.self, forKey: .installed)
|
|
911
|
+
self = .providerAppTooOld(installed: installed)
|
|
912
|
+
case "moduleMissing":
|
|
913
|
+
self = .moduleMissing
|
|
914
|
+
case "partnerPolicyMissing":
|
|
915
|
+
self = .partnerPolicyMissing
|
|
916
|
+
case "featureUnavailable":
|
|
917
|
+
self = .featureUnavailable
|
|
918
|
+
default:
|
|
919
|
+
throw DecodingError.dataCorruptedError(forKey: .kind, in: container, debugDescription: "unknown StoreAvailability \(tag)")
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
public func encode(to encoder: any Encoder) throws {
|
|
924
|
+
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
925
|
+
switch self {
|
|
926
|
+
case .available:
|
|
927
|
+
try container.encode("available", forKey: .kind)
|
|
928
|
+
case .unsupportedDevice:
|
|
929
|
+
try container.encode("unsupportedDevice", forKey: .kind)
|
|
930
|
+
case .providerAppMissing:
|
|
931
|
+
try container.encode("providerAppMissing", forKey: .kind)
|
|
932
|
+
case .providerAppTooOld(let installed):
|
|
933
|
+
try container.encode("providerAppTooOld", forKey: .kind)
|
|
934
|
+
try container.encode(installed, forKey: .installed)
|
|
935
|
+
case .moduleMissing:
|
|
936
|
+
try container.encode("moduleMissing", forKey: .kind)
|
|
937
|
+
case .partnerPolicyMissing:
|
|
938
|
+
try container.encode("partnerPolicyMissing", forKey: .kind)
|
|
939
|
+
case .featureUnavailable:
|
|
940
|
+
try container.encode("featureUnavailable", forKey: .kind)
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
|
|
579
945
|
/// Who recorded a sample. The only stable handle an app has for filtering by app or device.
|
|
580
946
|
public struct SampleSource: Codable, Equatable, Sendable {
|
|
581
947
|
public var name: String
|
|
@@ -685,8 +1051,11 @@ public struct Workout: Codable, Equatable, Sendable {
|
|
|
685
1051
|
public var source: SampleSource
|
|
686
1052
|
public var userEntered: Bool
|
|
687
1053
|
public var timeZone: String?
|
|
1054
|
+
/// ExerciseSessionRecord.title on Android; HKMetadataKeyWorkoutBrandName on Apple, which has no
|
|
1055
|
+
/// title field.
|
|
1056
|
+
public var title: String?
|
|
688
1057
|
|
|
689
|
-
public init(id: String, start: String, end: String, activity: WorkoutActivity, activityTypeCode: Int, indoor: Bool? = nil, activeEnergyKilocalories: Double? = nil, distanceMeters: Double? = nil, source: SampleSource, userEntered: Bool, timeZone: String? = nil) {
|
|
1058
|
+
public init(id: String, start: String, end: String, activity: WorkoutActivity, activityTypeCode: Int, indoor: Bool? = nil, activeEnergyKilocalories: Double? = nil, distanceMeters: Double? = nil, source: SampleSource, userEntered: Bool, timeZone: String? = nil, title: String? = nil) {
|
|
690
1059
|
self.id = id
|
|
691
1060
|
self.start = start
|
|
692
1061
|
self.end = end
|
|
@@ -698,6 +1067,77 @@ public struct Workout: Codable, Equatable, Sendable {
|
|
|
698
1067
|
self.source = source
|
|
699
1068
|
self.userEntered = userEntered
|
|
700
1069
|
self.timeZone = timeZone
|
|
1070
|
+
self.title = title
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
/// Quantities of one meal share foodCorrelationId.
|
|
1075
|
+
public struct DietaryQuantity: Codable, Equatable, Sendable {
|
|
1076
|
+
public var id: String
|
|
1077
|
+
public var nutrient: Nutrient
|
|
1078
|
+
public var value: Double
|
|
1079
|
+
public var start: String
|
|
1080
|
+
public var end: String
|
|
1081
|
+
public var mealType: MealType
|
|
1082
|
+
public var name: String?
|
|
1083
|
+
/// HealthKit's food correlation, absent for a loose quantity. Always set on Android, where it
|
|
1084
|
+
/// is the NutritionRecord's id - the id nutrition.delete takes for a meal this app logged.
|
|
1085
|
+
public var foodCorrelationId: String?
|
|
1086
|
+
public var source: SampleSource
|
|
1087
|
+
public var userEntered: Bool
|
|
1088
|
+
public var timeZone: String?
|
|
1089
|
+
|
|
1090
|
+
public init(id: String, nutrient: Nutrient, value: Double, start: String, end: String, mealType: MealType, name: String? = nil, foodCorrelationId: String? = nil, source: SampleSource, userEntered: Bool, timeZone: String? = nil) {
|
|
1091
|
+
self.id = id
|
|
1092
|
+
self.nutrient = nutrient
|
|
1093
|
+
self.value = value
|
|
1094
|
+
self.start = start
|
|
1095
|
+
self.end = end
|
|
1096
|
+
self.mealType = mealType
|
|
1097
|
+
self.name = name
|
|
1098
|
+
self.foodCorrelationId = foodCorrelationId
|
|
1099
|
+
self.source = source
|
|
1100
|
+
self.userEntered = userEntered
|
|
1101
|
+
self.timeZone = timeZone
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
/// start and end are the local midnights of that day in timeZone.
|
|
1106
|
+
public struct MenstruationFlowDay: Codable, Equatable, Sendable {
|
|
1107
|
+
public var id: String
|
|
1108
|
+
public var start: String
|
|
1109
|
+
public var end: String
|
|
1110
|
+
public var flow: MenstruationFlow
|
|
1111
|
+
public var cycleStart: Bool
|
|
1112
|
+
public var source: SampleSource
|
|
1113
|
+
public var userEntered: Bool
|
|
1114
|
+
public var timeZone: String?
|
|
1115
|
+
|
|
1116
|
+
public init(id: String, start: String, end: String, flow: MenstruationFlow, cycleStart: Bool, source: SampleSource, userEntered: Bool, timeZone: String? = nil) {
|
|
1117
|
+
self.id = id
|
|
1118
|
+
self.start = start
|
|
1119
|
+
self.end = end
|
|
1120
|
+
self.flow = flow
|
|
1121
|
+
self.cycleStart = cycleStart
|
|
1122
|
+
self.source = source
|
|
1123
|
+
self.userEntered = userEntered
|
|
1124
|
+
self.timeZone = timeZone
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
/// Available on ios only.
|
|
1129
|
+
public struct AthleteProfile: Codable, Equatable, Sendable {
|
|
1130
|
+
public var biologicalSex: BiologicalSex
|
|
1131
|
+
/// A calendar day, as HealthKit stores it, never an instant.
|
|
1132
|
+
public var dateOfBirth: String?
|
|
1133
|
+
public var wheelchairUse: Bool?
|
|
1134
|
+
public var activityMoveMode: ActivityMoveMode?
|
|
1135
|
+
|
|
1136
|
+
public init(biologicalSex: BiologicalSex, dateOfBirth: String? = nil, wheelchairUse: Bool? = nil, activityMoveMode: ActivityMoveMode? = nil) {
|
|
1137
|
+
self.biologicalSex = biologicalSex
|
|
1138
|
+
self.dateOfBirth = dateOfBirth
|
|
1139
|
+
self.wheelchairUse = wheelchairUse
|
|
1140
|
+
self.activityMoveMode = activityMoveMode
|
|
701
1141
|
}
|
|
702
1142
|
}
|
|
703
1143
|
|
|
@@ -1216,6 +1656,67 @@ public struct WorkoutList: Codable, Equatable, Sendable {
|
|
|
1216
1656
|
}
|
|
1217
1657
|
}
|
|
1218
1658
|
|
|
1659
|
+
public struct DietaryQuantityList: Codable, Equatable, Sendable {
|
|
1660
|
+
public var samples: [DietaryQuantity]
|
|
1661
|
+
|
|
1662
|
+
public init(samples: [DietaryQuantity]) {
|
|
1663
|
+
self.samples = samples
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
public struct MenstruationFlowDayList: Codable, Equatable, Sendable {
|
|
1668
|
+
public var samples: [MenstruationFlowDay]
|
|
1669
|
+
|
|
1670
|
+
public init(samples: [MenstruationFlowDay]) {
|
|
1671
|
+
self.samples = samples
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
/// A cycle event logged at an instant.
|
|
1676
|
+
public struct MenstrualEvent: Codable, Equatable, Sendable {
|
|
1677
|
+
public var id: String
|
|
1678
|
+
public var kind: MenstrualEventKind
|
|
1679
|
+
public var time: String
|
|
1680
|
+
/// ovulationTest: negative, positive, high, indeterminate or luteinizingHormoneSurge.
|
|
1681
|
+
/// cervicalMucus: dry, sticky, creamy, watery, eggWhite or unusual. Absent for
|
|
1682
|
+
/// intermenstrualBleeding, which is a flag.
|
|
1683
|
+
public var value: String?
|
|
1684
|
+
/// The store's own record type, passed through: OvulationTestRecord on Android,
|
|
1685
|
+
/// HKCategoryTypeIdentifierOvulationTestResult on Apple.
|
|
1686
|
+
public var storeIdentifier: String
|
|
1687
|
+
public var source: SampleSource
|
|
1688
|
+
public var userEntered: Bool
|
|
1689
|
+
public var timeZone: String?
|
|
1690
|
+
|
|
1691
|
+
public init(id: String, kind: MenstrualEventKind, time: String, value: String? = nil, storeIdentifier: String, source: SampleSource, userEntered: Bool, timeZone: String? = nil) {
|
|
1692
|
+
self.id = id
|
|
1693
|
+
self.kind = kind
|
|
1694
|
+
self.time = time
|
|
1695
|
+
self.value = value
|
|
1696
|
+
self.storeIdentifier = storeIdentifier
|
|
1697
|
+
self.source = source
|
|
1698
|
+
self.userEntered = userEntered
|
|
1699
|
+
self.timeZone = timeZone
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
public struct MenstrualEventList: Codable, Equatable, Sendable {
|
|
1704
|
+
public var samples: [MenstrualEvent]
|
|
1705
|
+
|
|
1706
|
+
public init(samples: [MenstrualEvent]) {
|
|
1707
|
+
self.samples = samples
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
/// Available on ios only.
|
|
1712
|
+
public struct AthleteProfileResult: Codable, Equatable, Sendable {
|
|
1713
|
+
public var profile: AthleteProfile?
|
|
1714
|
+
|
|
1715
|
+
public init(profile: AthleteProfile? = nil) {
|
|
1716
|
+
self.profile = profile
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1219
1720
|
/// Available on ios only.
|
|
1220
1721
|
public struct PlannedWorkoutList: Codable, Equatable, Sendable {
|
|
1221
1722
|
public var workouts: [PlannedWorkout]
|
|
@@ -1292,6 +1793,238 @@ public struct NutritionEntryList: Codable, Equatable, Sendable {
|
|
|
1292
1793
|
}
|
|
1293
1794
|
}
|
|
1294
1795
|
|
|
1796
|
+
/// The store's own total for one local day, from its statistics engine. start and end are that
|
|
1797
|
+
/// day's midnights.
|
|
1798
|
+
public struct DayTotal: Codable, Equatable, Sendable {
|
|
1799
|
+
public var metric: DayTotalMetric
|
|
1800
|
+
public var start: String
|
|
1801
|
+
public var end: String
|
|
1802
|
+
public var value: Double
|
|
1803
|
+
public var unit: String
|
|
1804
|
+
public var timeZone: String?
|
|
1805
|
+
|
|
1806
|
+
public init(metric: DayTotalMetric, start: String, end: String, value: Double, unit: String, timeZone: String? = nil) {
|
|
1807
|
+
self.metric = metric
|
|
1808
|
+
self.start = start
|
|
1809
|
+
self.end = end
|
|
1810
|
+
self.value = value
|
|
1811
|
+
self.unit = unit
|
|
1812
|
+
self.timeZone = timeZone
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
/// One record a day total is made of, as the store holds it. A record crossing midnight is
|
|
1817
|
+
/// whole.
|
|
1818
|
+
public struct IntradaySample: Codable, Equatable, Sendable {
|
|
1819
|
+
public var id: String
|
|
1820
|
+
public var metric: IntradayMetric
|
|
1821
|
+
public var start: String
|
|
1822
|
+
public var end: String
|
|
1823
|
+
public var value: Double
|
|
1824
|
+
public var source: SampleSource
|
|
1825
|
+
public var userEntered: Bool
|
|
1826
|
+
public var timeZone: String?
|
|
1827
|
+
|
|
1828
|
+
public init(id: String, metric: IntradayMetric, start: String, end: String, value: Double, source: SampleSource, userEntered: Bool, timeZone: String? = nil) {
|
|
1829
|
+
self.id = id
|
|
1830
|
+
self.metric = metric
|
|
1831
|
+
self.start = start
|
|
1832
|
+
self.end = end
|
|
1833
|
+
self.value = value
|
|
1834
|
+
self.source = source
|
|
1835
|
+
self.userEntered = userEntered
|
|
1836
|
+
self.timeZone = timeZone
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
/// The store's own resting heart rate for a day, never derived from heart rate readings.
|
|
1841
|
+
public struct RestingHeartRateSample: Codable, Equatable, Sendable {
|
|
1842
|
+
public var id: String
|
|
1843
|
+
public var start: String
|
|
1844
|
+
public var end: String
|
|
1845
|
+
public var beatsPerMinute: Double
|
|
1846
|
+
public var source: SampleSource
|
|
1847
|
+
public var userEntered: Bool
|
|
1848
|
+
public var timeZone: String?
|
|
1849
|
+
|
|
1850
|
+
public init(id: String, start: String, end: String, beatsPerMinute: Double, source: SampleSource, userEntered: Bool, timeZone: String? = nil) {
|
|
1851
|
+
self.id = id
|
|
1852
|
+
self.start = start
|
|
1853
|
+
self.end = end
|
|
1854
|
+
self.beatsPerMinute = beatsPerMinute
|
|
1855
|
+
self.source = source
|
|
1856
|
+
self.userEntered = userEntered
|
|
1857
|
+
self.timeZone = timeZone
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
/// A point reading has start equal to end; skin and wrist temperature span a stretch.
|
|
1862
|
+
public struct BodyReading: Codable, Equatable, Sendable {
|
|
1863
|
+
public var id: String
|
|
1864
|
+
public var kind: BodyReadingKind
|
|
1865
|
+
public var start: String
|
|
1866
|
+
public var end: String
|
|
1867
|
+
public var source: SampleSource
|
|
1868
|
+
public var userEntered: Bool
|
|
1869
|
+
public var timeZone: String?
|
|
1870
|
+
|
|
1871
|
+
public init(id: String, kind: BodyReadingKind, start: String, end: String, source: SampleSource, userEntered: Bool, timeZone: String? = nil) {
|
|
1872
|
+
self.id = id
|
|
1873
|
+
self.kind = kind
|
|
1874
|
+
self.start = start
|
|
1875
|
+
self.end = end
|
|
1876
|
+
self.source = source
|
|
1877
|
+
self.userEntered = userEntered
|
|
1878
|
+
self.timeZone = timeZone
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
public struct RoutePoint: Codable, Equatable, Sendable {
|
|
1883
|
+
public var time: String
|
|
1884
|
+
public var latitude: Double
|
|
1885
|
+
public var longitude: Double
|
|
1886
|
+
|
|
1887
|
+
public init(time: String, latitude: Double, longitude: Double) {
|
|
1888
|
+
self.time = time
|
|
1889
|
+
self.latitude = latitude
|
|
1890
|
+
self.longitude = longitude
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
/// The GPS track a workout left, thinned to one point a second and at most 5,000 points. start
|
|
1895
|
+
/// and end are the first and last point.
|
|
1896
|
+
public struct WorkoutRoute: Codable, Equatable, Sendable {
|
|
1897
|
+
public var id: String
|
|
1898
|
+
public var workoutId: String
|
|
1899
|
+
public var start: String
|
|
1900
|
+
public var end: String
|
|
1901
|
+
public var points: [RoutePoint]
|
|
1902
|
+
public var source: SampleSource
|
|
1903
|
+
public var timeZone: String?
|
|
1904
|
+
|
|
1905
|
+
public init(id: String, workoutId: String, start: String, end: String, points: [RoutePoint], source: SampleSource, timeZone: String? = nil) {
|
|
1906
|
+
self.id = id
|
|
1907
|
+
self.workoutId = workoutId
|
|
1908
|
+
self.start = start
|
|
1909
|
+
self.end = end
|
|
1910
|
+
self.points = points
|
|
1911
|
+
self.source = source
|
|
1912
|
+
self.timeZone = timeZone
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
/// One speed, power or cadence reading. Android's are instants, so start equals end there.
|
|
1917
|
+
public struct WorkoutIntensitySample: Codable, Equatable, Sendable {
|
|
1918
|
+
public var id: String
|
|
1919
|
+
public var metric: WorkoutIntensityMetric
|
|
1920
|
+
/// Set on Apple, where HealthKit stores a reading inside its workout. Health Connect stores
|
|
1921
|
+
/// them free-standing, so Android leaves it absent rather than guessing by time.
|
|
1922
|
+
public var workoutId: String?
|
|
1923
|
+
public var start: String
|
|
1924
|
+
public var end: String
|
|
1925
|
+
public var value: Double
|
|
1926
|
+
public var source: SampleSource
|
|
1927
|
+
public var userEntered: Bool
|
|
1928
|
+
public var timeZone: String?
|
|
1929
|
+
|
|
1930
|
+
public init(id: String, metric: WorkoutIntensityMetric, workoutId: String? = nil, start: String, end: String, value: Double, source: SampleSource, userEntered: Bool, timeZone: String? = nil) {
|
|
1931
|
+
self.id = id
|
|
1932
|
+
self.metric = metric
|
|
1933
|
+
self.workoutId = workoutId
|
|
1934
|
+
self.start = start
|
|
1935
|
+
self.end = end
|
|
1936
|
+
self.value = value
|
|
1937
|
+
self.source = source
|
|
1938
|
+
self.userEntered = userEntered
|
|
1939
|
+
self.timeZone = timeZone
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
/// One drink, in millilitres.
|
|
1944
|
+
public struct HydrationSample: Codable, Equatable, Sendable {
|
|
1945
|
+
public var id: String
|
|
1946
|
+
public var start: String
|
|
1947
|
+
public var end: String
|
|
1948
|
+
public var millilitres: Double
|
|
1949
|
+
public var source: SampleSource
|
|
1950
|
+
public var userEntered: Bool
|
|
1951
|
+
public var timeZone: String?
|
|
1952
|
+
|
|
1953
|
+
public init(id: String, start: String, end: String, millilitres: Double, source: SampleSource, userEntered: Bool, timeZone: String? = nil) {
|
|
1954
|
+
self.id = id
|
|
1955
|
+
self.start = start
|
|
1956
|
+
self.end = end
|
|
1957
|
+
self.millilitres = millilitres
|
|
1958
|
+
self.source = source
|
|
1959
|
+
self.userEntered = userEntered
|
|
1960
|
+
self.timeZone = timeZone
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
public struct DayTotalList: Codable, Equatable, Sendable {
|
|
1965
|
+
public var totals: [DayTotal]
|
|
1966
|
+
|
|
1967
|
+
public init(totals: [DayTotal]) {
|
|
1968
|
+
self.totals = totals
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
public struct IntradaySampleList: Codable, Equatable, Sendable {
|
|
1973
|
+
public var samples: [IntradaySample]
|
|
1974
|
+
|
|
1975
|
+
public init(samples: [IntradaySample]) {
|
|
1976
|
+
self.samples = samples
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
public struct RestingHeartRateList: Codable, Equatable, Sendable {
|
|
1981
|
+
public var samples: [RestingHeartRateSample]
|
|
1982
|
+
|
|
1983
|
+
public init(samples: [RestingHeartRateSample]) {
|
|
1984
|
+
self.samples = samples
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
public struct BodyReadingList: Codable, Equatable, Sendable {
|
|
1989
|
+
public var samples: [BodyReading]
|
|
1990
|
+
|
|
1991
|
+
public init(samples: [BodyReading]) {
|
|
1992
|
+
self.samples = samples
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
public struct WorkoutRouteList: Codable, Equatable, Sendable {
|
|
1997
|
+
public var routes: [WorkoutRoute]
|
|
1998
|
+
|
|
1999
|
+
public init(routes: [WorkoutRoute]) {
|
|
2000
|
+
self.routes = routes
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
public struct WorkoutIntensityList: Codable, Equatable, Sendable {
|
|
2005
|
+
public var samples: [WorkoutIntensitySample]
|
|
2006
|
+
|
|
2007
|
+
public init(samples: [WorkoutIntensitySample]) {
|
|
2008
|
+
self.samples = samples
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
|
|
2012
|
+
public struct HydrationList: Codable, Equatable, Sendable {
|
|
2013
|
+
public var samples: [HydrationSample]
|
|
2014
|
+
|
|
2015
|
+
public init(samples: [HydrationSample]) {
|
|
2016
|
+
self.samples = samples
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
public struct AvailabilityResult: Codable, Equatable, Sendable {
|
|
2021
|
+
public var availability: StoreAvailability
|
|
2022
|
+
|
|
2023
|
+
public init(availability: StoreAvailability) {
|
|
2024
|
+
self.availability = availability
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
|
|
1295
2028
|
public enum TerraEventEnvelope {
|
|
1296
2029
|
public static func validate(_ value: TerraEvent) throws {
|
|
1297
2030
|
guard case .string = value.storage["at"] else {
|