@tryterra/react-native 0.0.0-stage → 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/LICENSE +21 -0
- package/README.md +142 -2
- package/TerraReactNative.podspec +48 -0
- package/android/build.gradle +87 -0
- package/android/src/bridgeCore/kotlin/README.md +1 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/BridgeBootstrap.kt +133 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/BridgeHost.kt +32 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/Conversions.kt +557 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/LauncherSlots.kt +41 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/ReadConversions.kt +250 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/Rejections.kt +150 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/StreamRegistry.kt +77 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TerraBridge.kt +86 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TerraBridgeOpsImpl.kt +300 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TerraBridgeStreamsImpl.kt +50 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TokenBroker.kt +198 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/generated/BridgeErrors.kt +217 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/generated/BridgeOps.kt +582 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/generated/BridgeTypes.kt +1569 -0
- package/android/src/healthConnectOnly/kotlin/co/tryterra/sdk/reactnative/TerraStores.kt +11 -0
- package/android/src/main/AndroidManifest.xml +24 -0
- package/android/src/main/java/co/tryterra/sdk/reactnative/TerraBridgeModule.kt +96 -0
- package/android/src/main/java/co/tryterra/sdk/reactnative/TerraReactNativePackage.kt +26 -0
- package/android/src/main/java/co/tryterra/sdk/reactnative/TerraStartup.kt +46 -0
- package/android/src/samsung/kotlin/co/tryterra/sdk/reactnative/TerraStores.kt +13 -0
- package/docs/availability.md +396 -0
- package/frameworks/TerraSDK.xcframework/Info.plist +48 -0
- 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 +82672 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.package.swiftinterface +2186 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +2186 -0
- 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 +2163 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/PrivacyInfo.xcprivacy +96 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/TerraSDK +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/dSYMs/TerraSDK.framework.dSYM/Contents/Info.plist +20 -0
- 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 +6594 -0
- 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 +82672 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.package.swiftinterface +2186 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +2186 -0
- 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 +2163 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +82672 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.package.swiftinterface +2186 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +2186 -0
- 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 +2163 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/PrivacyInfo.xcprivacy +96 -0
- 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 +223 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Info.plist +20 -0
- 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 +6595 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/Relocations/x86_64/TerraSDK.yml +6513 -0
- package/ios/TerraBridgeCore/BridgeBootstrap.swift +89 -0
- package/ios/TerraBridgeCore/BridgeHost.swift +13 -0
- package/ios/TerraBridgeCore/Conversions+Reads.swift +259 -0
- package/ios/TerraBridgeCore/Conversions.swift +594 -0
- package/ios/TerraBridgeCore/Generated/BridgeErrors.swift +179 -0
- package/ios/TerraBridgeCore/Generated/BridgeOps.swift +967 -0
- package/ios/TerraBridgeCore/Generated/BridgeTypes.swift +2050 -0
- package/ios/TerraBridgeCore/Locked.swift +15 -0
- package/ios/TerraBridgeCore/README.md +1 -0
- package/ios/TerraBridgeCore/Rejections.swift +130 -0
- package/ios/TerraBridgeCore/RuntimeAttachment.swift +60 -0
- package/ios/TerraBridgeCore/StreamRegistry.swift +59 -0
- package/ios/TerraBridgeCore/TerraBridge.swift +122 -0
- package/ios/TerraBridgeCore/TerraBridgeOpsImpl.swift +318 -0
- package/ios/TerraBridgeCore/TerraBridgeStreamsImpl.swift +60 -0
- package/ios/TerraBridgeCore/TokenBroker.swift +147 -0
- package/ios/TerraBridgeModule.mm +79 -0
- package/ios/TerraBridgeModuleImpl.swift +49 -0
- package/ios/TerraReactNative.swift +19 -0
- package/lib/EndUser.d.ts +12 -0
- package/lib/EndUser.js +13 -0
- package/lib/NativeTerraBridge.d.ts +23 -0
- package/lib/NativeTerraBridge.js +13 -0
- package/lib/Terra.d.ts +223 -0
- package/lib/Terra.js +336 -0
- package/lib/TerraError.d.ts +42 -0
- package/lib/TerraError.js +95 -0
- package/lib/createTerra.d.ts +8 -0
- package/lib/createTerra.js +19 -0
- package/lib/generated/client.d.ts +128 -0
- package/lib/generated/client.js +256 -0
- package/lib/generated/codecs.d.ts +219 -0
- package/lib/generated/codecs.js +2066 -0
- package/lib/generated/errors.d.ts +13 -0
- package/lib/generated/errors.js +68 -0
- package/lib/generated/ops.d.ts +13 -0
- package/lib/generated/ops.js +161 -0
- package/lib/generated/streams.d.ts +23 -0
- package/lib/generated/streams.js +58 -0
- package/lib/generated/types.d.ts +794 -0
- package/lib/generated/types.js +224 -0
- package/lib/index.d.ts +29 -0
- package/lib/index.js +55 -0
- package/package.json +71 -4
- package/src/EndUser.ts +15 -0
- package/src/NativeTerraBridge.ts +27 -0
- package/src/Terra.ts +592 -0
- package/src/TerraError.ts +116 -0
- package/src/createTerra.ts +18 -0
- package/src/generated/client.ts +315 -0
- package/src/generated/codecs.ts +1989 -0
- package/src/generated/errors.ts +113 -0
- package/src/generated/ops.ts +233 -0
- package/src/generated/streams.ts +74 -0
- package/src/generated/types.ts +1222 -0
- package/src/index.ts +54 -0
|
@@ -0,0 +1,2050 @@
|
|
|
1
|
+
// Generated from surface/terra-surface.json by surface/gen. Do not edit by hand.
|
|
2
|
+
// Regenerate with: node surface/gen/src/cli.ts generate
|
|
3
|
+
|
|
4
|
+
import Foundation
|
|
5
|
+
|
|
6
|
+
/// The one spelling of an instant on the wire: UTC, and always exactly three fractional digits.
|
|
7
|
+
///
|
|
8
|
+
/// `.iso8601` is not it. `ISO8601DateFormatter` without `.withFractionalSeconds` drops the
|
|
9
|
+
/// sub-second part, and HealthKit stores sub-second, so a round trip through the bridge would
|
|
10
|
+
/// silently move every sample to the nearest whole second. Three digits rather than however many
|
|
11
|
+
/// the source had, so that the same instant produces the same bytes in Swift, Kotlin and
|
|
12
|
+
/// JavaScript - which is what lets a vector be compared rather than interpreted.
|
|
13
|
+
public enum BridgeInstant {
|
|
14
|
+
/// `ISO8601DateFormatter` is not `Sendable`, and building one per call is not free on a read
|
|
15
|
+
/// of a month of samples. One pair behind a lock is the cheap end of correct here.
|
|
16
|
+
private final class Formatters: @unchecked Sendable {
|
|
17
|
+
let lock = NSLock()
|
|
18
|
+
let withFraction = ISO8601DateFormatter()
|
|
19
|
+
let withoutFraction = ISO8601DateFormatter()
|
|
20
|
+
|
|
21
|
+
init() {
|
|
22
|
+
withFraction.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
|
|
23
|
+
withFraction.timeZone = TimeZone(secondsFromGMT: 0)
|
|
24
|
+
withoutFraction.formatOptions = [.withInternetDateTime]
|
|
25
|
+
withoutFraction.timeZone = TimeZone(secondsFromGMT: 0)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private static let formatters = Formatters()
|
|
30
|
+
|
|
31
|
+
public static func format(_ date: Date) -> String {
|
|
32
|
+
formatters.lock.lock()
|
|
33
|
+
defer { formatters.lock.unlock() }
|
|
34
|
+
return formatters.withFraction.string(from: date)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// Tolerant on the way in: a peer that omitted the fractional part is still understood.
|
|
38
|
+
public static func parse(_ text: String) -> Date? {
|
|
39
|
+
formatters.lock.lock()
|
|
40
|
+
defer { formatters.lock.unlock() }
|
|
41
|
+
return formatters.withFraction.date(from: text) ?? formatters.withoutFraction.date(from: text)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// A JSON value the bridge carries without interpreting: an error's detail bag, an event's payload.
|
|
46
|
+
///
|
|
47
|
+
/// The SDKs already encode those shapes, and re-describing them here would be a second opinion
|
|
48
|
+
/// about a spelling `DiagnosticsWire` and Apple's `Encodable` already agree on.
|
|
49
|
+
public struct BridgeJSON: Codable, Equatable, Sendable {
|
|
50
|
+
public let storage: [String: BridgeValue]
|
|
51
|
+
public init(_ storage: [String: BridgeValue]) { self.storage = storage }
|
|
52
|
+
public init(from decoder: any Decoder) throws {
|
|
53
|
+
storage = try decoder.singleValueContainer().decode([String: BridgeValue].self)
|
|
54
|
+
}
|
|
55
|
+
public func encode(to encoder: any Encoder) throws {
|
|
56
|
+
var container = encoder.singleValueContainer()
|
|
57
|
+
try container.encode(storage)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/// `indirect` because the cases hold `BridgeValue`: without it the enum has no finite size and
|
|
62
|
+
/// the file does not compile.
|
|
63
|
+
public indirect enum BridgeValue: Codable, Equatable, Sendable {
|
|
64
|
+
case string(String)
|
|
65
|
+
case number(Double)
|
|
66
|
+
case bool(Bool)
|
|
67
|
+
case array([BridgeValue])
|
|
68
|
+
case object([String: BridgeValue])
|
|
69
|
+
case null
|
|
70
|
+
|
|
71
|
+
public init(from decoder: any Decoder) throws {
|
|
72
|
+
let container = try decoder.singleValueContainer()
|
|
73
|
+
if container.decodeNil() { self = .null; return }
|
|
74
|
+
if let value = try? container.decode(Bool.self) { self = .bool(value); return }
|
|
75
|
+
if let value = try? container.decode(Double.self) { self = .number(value); return }
|
|
76
|
+
if let value = try? container.decode(String.self) { self = .string(value); return }
|
|
77
|
+
if let value = try? container.decode([BridgeValue].self) { self = .array(value); return }
|
|
78
|
+
self = .object(try container.decode([String: BridgeValue].self))
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public func encode(to encoder: any Encoder) throws {
|
|
82
|
+
var container = encoder.singleValueContainer()
|
|
83
|
+
switch self {
|
|
84
|
+
case .string(let value): try container.encode(value)
|
|
85
|
+
case .number(let value): try container.encode(value)
|
|
86
|
+
case .bool(let value): try container.encode(value)
|
|
87
|
+
case .array(let value): try container.encode(value)
|
|
88
|
+
case .object(let value): try container.encode(value)
|
|
89
|
+
case .null: try container.encodeNil()
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// A list of enum cases that is a set on the wire: de-duplicated and sorted by wire spelling
|
|
95
|
+
/// whenever it is stored, decoded or encoded, as the Kotlin and TypeScript codecs do.
|
|
96
|
+
@propertyWrapper
|
|
97
|
+
public struct CaseSet<Element: RawRepresentable & Codable & Hashable & Sendable>: Codable, Equatable, Sendable
|
|
98
|
+
where Element.RawValue == String {
|
|
99
|
+
private var storage: [Element]
|
|
100
|
+
|
|
101
|
+
public var wrappedValue: [Element] {
|
|
102
|
+
get { storage }
|
|
103
|
+
set { storage = Self.canonical(newValue) }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
public init(wrappedValue: [Element]) { storage = Self.canonical(wrappedValue) }
|
|
107
|
+
|
|
108
|
+
public init(from decoder: any Decoder) throws {
|
|
109
|
+
storage = Self.canonical(try decoder.singleValueContainer().decode([Element].self))
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
public func encode(to encoder: any Encoder) throws {
|
|
113
|
+
var container = encoder.singleValueContainer()
|
|
114
|
+
try container.encode(storage)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private static func canonical(_ value: [Element]) -> [Element] {
|
|
118
|
+
Array(Set(value)).sorted { $0.rawValue < $1.rawValue }
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/// A health store the SDK can link. One connection per source per device.
|
|
123
|
+
public enum Source: String, Codable, Sendable, CaseIterable {
|
|
124
|
+
case health = "health"
|
|
125
|
+
/// Samsung Health, through the terra-android-samsung add-on. Connect, disconnect, permissions
|
|
126
|
+
/// and diagnostics take it; reads, writes and change streams are Health Connect's.
|
|
127
|
+
///
|
|
128
|
+
/// Available on android only.
|
|
129
|
+
case samsungHealth = "samsungHealth"
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/// The unit permissions are asked for and changes are reported against.
|
|
133
|
+
public enum DataCategory: String, Codable, Sendable, CaseIterable {
|
|
134
|
+
case activity = "activity"
|
|
135
|
+
case body = "body"
|
|
136
|
+
case daily = "daily"
|
|
137
|
+
case sleep = "sleep"
|
|
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"
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
public enum PermissionStatus: String, Codable, Sendable, CaseIterable {
|
|
149
|
+
case granted = "granted"
|
|
150
|
+
case denied = "denied"
|
|
151
|
+
case notDetermined = "notDetermined"
|
|
152
|
+
/// A read grant HealthKit will not report. Not an error: it is the documented answer for reads
|
|
153
|
+
/// on iOS.
|
|
154
|
+
case notDisclosed = "notDisclosed"
|
|
155
|
+
case unsupported = "unsupported"
|
|
156
|
+
/// Android answers unsupported where Apple would answer unavailable.
|
|
157
|
+
///
|
|
158
|
+
/// Available on ios only.
|
|
159
|
+
case unavailable = "unavailable"
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
public enum MotionContext: String, Codable, Sendable, CaseIterable {
|
|
163
|
+
case unknown = "unknown"
|
|
164
|
+
case sedentary = "sedentary"
|
|
165
|
+
case active = "active"
|
|
166
|
+
}
|
|
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
|
+
|
|
252
|
+
/// Why background delivery is restricted. Android discloses which; Apple reports the state
|
|
253
|
+
/// without a reason.
|
|
254
|
+
///
|
|
255
|
+
/// Available on android only.
|
|
256
|
+
public enum BackgroundRestrictionReason: String, Codable, Sendable, CaseIterable {
|
|
257
|
+
case oemRestricted = "oemRestricted"
|
|
258
|
+
case backgroundReadDenied = "backgroundReadDenied"
|
|
259
|
+
case healthConnectFeatureUnavailable = "healthConnectFeatureUnavailable"
|
|
260
|
+
case storeFeatureUnavailable = "storeFeatureUnavailable"
|
|
261
|
+
case ticksNotRunning = "ticksNotRunning"
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
public enum DiagnosticsReason: String, Codable, Sendable, CaseIterable {
|
|
265
|
+
case uploadFailed = "uploadFailed"
|
|
266
|
+
case backfillAbandoned = "backfillAbandoned"
|
|
267
|
+
case backgroundObservationUnavailable = "backgroundObservationUnavailable"
|
|
268
|
+
case backgroundRestricted = "backgroundRestricted"
|
|
269
|
+
case backgroundRefreshUnscheduled = "backgroundRefreshUnscheduled"
|
|
270
|
+
case quiet = "quiet"
|
|
271
|
+
case requested = "requested"
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
public enum DeviceKeyRevokeOutcome: String, Codable, Sendable, CaseIterable {
|
|
275
|
+
case revoked = "revoked"
|
|
276
|
+
case alreadyRevoked = "alreadyRevoked"
|
|
277
|
+
case routeAbsent = "routeAbsent"
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/// The redacted error name an event carries. Deliberately not TerraErrorCode: an event names
|
|
281
|
+
/// the failure without the detail bag, because a support report leaves the device.
|
|
282
|
+
public enum FailureCode: String, Codable, Sendable, CaseIterable {
|
|
283
|
+
case healthDataUnavailable = "healthDataUnavailable"
|
|
284
|
+
case notConnected = "notConnected"
|
|
285
|
+
case tokenUnavailable = "tokenUnavailable"
|
|
286
|
+
case tokenRejected = "tokenRejected"
|
|
287
|
+
case tokenProviderFailed = "tokenProviderFailed"
|
|
288
|
+
case offline = "offline"
|
|
289
|
+
case timedOut = "timedOut"
|
|
290
|
+
case serverError = "serverError"
|
|
291
|
+
case transport = "transport"
|
|
292
|
+
case malformedResponse = "malformedResponse"
|
|
293
|
+
case userLimitReached = "userLimitReached"
|
|
294
|
+
case upgradeRequired = "upgradeRequired"
|
|
295
|
+
case platform = "platform"
|
|
296
|
+
case deviceKeyUnavailable = "deviceKeyUnavailable"
|
|
297
|
+
case signatureRejected = "signatureRejected"
|
|
298
|
+
case linkRevoked = "linkRevoked"
|
|
299
|
+
case linkConflict = "linkConflict"
|
|
300
|
+
case clockSkewed = "clockSkewed"
|
|
301
|
+
case devIDMismatch = "devIDMismatch"
|
|
302
|
+
case syncAlreadyRunning = "syncAlreadyRunning"
|
|
303
|
+
case linkAlreadyRunning = "linkAlreadyRunning"
|
|
304
|
+
case storeLocked = "storeLocked"
|
|
305
|
+
case storeUnreadable = "storeUnreadable"
|
|
306
|
+
case deadlineExceeded = "deadlineExceeded"
|
|
307
|
+
case permissionDenied = "permissionDenied"
|
|
308
|
+
case authorizationNotDetermined = "authorizationNotDetermined"
|
|
309
|
+
case sampleNotFound = "sampleNotFound"
|
|
310
|
+
case rollbackIncomplete = "rollbackIncomplete"
|
|
311
|
+
case distanceNotAttributable = "distanceNotAttributable"
|
|
312
|
+
case bloodPressureIncomplete = "bloodPressureIncomplete"
|
|
313
|
+
case authorizationUnavailableInExtension = "authorizationUnavailableInExtension"
|
|
314
|
+
case routeAwaitingConverter = "routeAwaitingConverter"
|
|
315
|
+
/// Available on android only.
|
|
316
|
+
case readQuotaExceeded = "readQuotaExceeded"
|
|
317
|
+
/// Apple's FailureCode carries it; Android's does not, because there is no Android
|
|
318
|
+
/// planned-workout model.
|
|
319
|
+
///
|
|
320
|
+
/// Available on ios only.
|
|
321
|
+
case plannedWorkoutUnnamed = "plannedWorkoutUnnamed"
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/// Whether Terra's plans can be put on the person's Apple Watch. unsupported before iOS 17 or
|
|
325
|
+
/// where WorkoutKit cannot schedule.
|
|
326
|
+
///
|
|
327
|
+
/// Available on ios only.
|
|
328
|
+
public enum WorkoutSchedulingStatus: String, Codable, Sendable, CaseIterable {
|
|
329
|
+
case unsupported = "unsupported"
|
|
330
|
+
case notDetermined = "notDetermined"
|
|
331
|
+
case denied = "denied"
|
|
332
|
+
case authorized = "authorized"
|
|
333
|
+
}
|
|
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
|
+
|
|
439
|
+
/// A `CodingKey` over a plain string, for the dictionary-key conformances below.
|
|
440
|
+
public struct StringCodingKey: CodingKey {
|
|
441
|
+
public let stringValue: String
|
|
442
|
+
public var intValue: Int? { nil }
|
|
443
|
+
public init(_ value: String) { stringValue = value }
|
|
444
|
+
public init?(stringValue: String) { self.stringValue = stringValue }
|
|
445
|
+
public init?(intValue: Int) { return nil }
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/// So that a `[DataCategory: T]` encodes as a JSON object rather than an alternating array.
|
|
449
|
+
extension DataCategory: CodingKeyRepresentable {
|
|
450
|
+
public var codingKey: any CodingKey { StringCodingKey(rawValue) }
|
|
451
|
+
public init?<T: CodingKey>(codingKey: T) {
|
|
452
|
+
self.init(rawValue: codingKey.stringValue)
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/// The person a connect is for. A stored link is restored only for the person it was made for;
|
|
457
|
+
/// anybody else gets a fresh link. Never sent to Terra as identity.
|
|
458
|
+
public enum EndUser: Codable, Equatable, Sendable {
|
|
459
|
+
case reference(id: String)
|
|
460
|
+
case anonymous
|
|
461
|
+
|
|
462
|
+
private enum CodingKeys: String, CodingKey {
|
|
463
|
+
case kind
|
|
464
|
+
case id
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
public init(from decoder: any Decoder) throws {
|
|
468
|
+
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
469
|
+
let tag = try container.decode(String.self, forKey: .kind)
|
|
470
|
+
switch tag {
|
|
471
|
+
case "reference":
|
|
472
|
+
let id = try container.decode(String.self, forKey: .id)
|
|
473
|
+
self = .reference(id: id)
|
|
474
|
+
case "anonymous":
|
|
475
|
+
self = .anonymous
|
|
476
|
+
default:
|
|
477
|
+
throw DecodingError.dataCorruptedError(forKey: .kind, in: container, debugDescription: "unknown EndUser \(tag)")
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
public func encode(to encoder: any Encoder) throws {
|
|
482
|
+
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
483
|
+
switch self {
|
|
484
|
+
case .reference(let id):
|
|
485
|
+
try container.encode("reference", forKey: .kind)
|
|
486
|
+
try container.encode(id, forKey: .id)
|
|
487
|
+
case .anonymous:
|
|
488
|
+
try container.encode("anonymous", forKey: .kind)
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/// A half-open window [start, end). Resolved against the device's calendar natively.
|
|
494
|
+
public enum TimeRange: Codable, Equatable, Sendable {
|
|
495
|
+
case lastHours(hours: Int)
|
|
496
|
+
case lastDays(days: Int)
|
|
497
|
+
case today
|
|
498
|
+
case between(start: String, end: String)
|
|
499
|
+
|
|
500
|
+
private enum CodingKeys: String, CodingKey {
|
|
501
|
+
case kind
|
|
502
|
+
case hours
|
|
503
|
+
case days
|
|
504
|
+
case start
|
|
505
|
+
case end
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
public init(from decoder: any Decoder) throws {
|
|
509
|
+
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
510
|
+
let tag = try container.decode(String.self, forKey: .kind)
|
|
511
|
+
switch tag {
|
|
512
|
+
case "lastHours":
|
|
513
|
+
let hours = try container.decode(Int.self, forKey: .hours)
|
|
514
|
+
self = .lastHours(hours: hours)
|
|
515
|
+
case "lastDays":
|
|
516
|
+
let days = try container.decode(Int.self, forKey: .days)
|
|
517
|
+
self = .lastDays(days: days)
|
|
518
|
+
case "today":
|
|
519
|
+
self = .today
|
|
520
|
+
case "between":
|
|
521
|
+
let start = try container.decode(String.self, forKey: .start)
|
|
522
|
+
let end = try container.decode(String.self, forKey: .end)
|
|
523
|
+
self = .between(start: start, end: end)
|
|
524
|
+
default:
|
|
525
|
+
throw DecodingError.dataCorruptedError(forKey: .kind, in: container, debugDescription: "unknown TimeRange \(tag)")
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
public func encode(to encoder: any Encoder) throws {
|
|
530
|
+
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
531
|
+
switch self {
|
|
532
|
+
case .lastHours(let hours):
|
|
533
|
+
try container.encode("lastHours", forKey: .kind)
|
|
534
|
+
try container.encode(hours, forKey: .hours)
|
|
535
|
+
case .lastDays(let days):
|
|
536
|
+
try container.encode("lastDays", forKey: .kind)
|
|
537
|
+
try container.encode(days, forKey: .days)
|
|
538
|
+
case .today:
|
|
539
|
+
try container.encode("today", forKey: .kind)
|
|
540
|
+
case .between(let start, let end):
|
|
541
|
+
try container.encode("between", forKey: .kind)
|
|
542
|
+
try container.encode(start, forKey: .start)
|
|
543
|
+
try container.encode(end, forKey: .end)
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
public enum SleepStage: Codable, Equatable, Sendable {
|
|
549
|
+
case inBed
|
|
550
|
+
case awake
|
|
551
|
+
case asleep
|
|
552
|
+
case light
|
|
553
|
+
case deep
|
|
554
|
+
case rem
|
|
555
|
+
case unknown(raw: String)
|
|
556
|
+
|
|
557
|
+
private enum CodingKeys: String, CodingKey {
|
|
558
|
+
case kind
|
|
559
|
+
case raw
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
public init(from decoder: any Decoder) throws {
|
|
563
|
+
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
564
|
+
let tag = try container.decode(String.self, forKey: .kind)
|
|
565
|
+
switch tag {
|
|
566
|
+
case "inBed":
|
|
567
|
+
self = .inBed
|
|
568
|
+
case "awake":
|
|
569
|
+
self = .awake
|
|
570
|
+
case "asleep":
|
|
571
|
+
self = .asleep
|
|
572
|
+
case "light":
|
|
573
|
+
self = .light
|
|
574
|
+
case "deep":
|
|
575
|
+
self = .deep
|
|
576
|
+
case "rem":
|
|
577
|
+
self = .rem
|
|
578
|
+
case "unknown":
|
|
579
|
+
let raw = try container.decode(String.self, forKey: .raw)
|
|
580
|
+
self = .unknown(raw: raw)
|
|
581
|
+
default:
|
|
582
|
+
throw DecodingError.dataCorruptedError(forKey: .kind, in: container, debugDescription: "unknown SleepStage \(tag)")
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
public func encode(to encoder: any Encoder) throws {
|
|
587
|
+
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
588
|
+
switch self {
|
|
589
|
+
case .inBed:
|
|
590
|
+
try container.encode("inBed", forKey: .kind)
|
|
591
|
+
case .awake:
|
|
592
|
+
try container.encode("awake", forKey: .kind)
|
|
593
|
+
case .asleep:
|
|
594
|
+
try container.encode("asleep", forKey: .kind)
|
|
595
|
+
case .light:
|
|
596
|
+
try container.encode("light", forKey: .kind)
|
|
597
|
+
case .deep:
|
|
598
|
+
try container.encode("deep", forKey: .kind)
|
|
599
|
+
case .rem:
|
|
600
|
+
try container.encode("rem", forKey: .kind)
|
|
601
|
+
case .unknown(let raw):
|
|
602
|
+
try container.encode("unknown", forKey: .kind)
|
|
603
|
+
try container.encode(raw, forKey: .raw)
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
public enum WorkoutActivity: Codable, Equatable, Sendable {
|
|
609
|
+
case running
|
|
610
|
+
case walking
|
|
611
|
+
case cycling
|
|
612
|
+
case swimming
|
|
613
|
+
case strengthTraining
|
|
614
|
+
case other(label: String)
|
|
615
|
+
|
|
616
|
+
private enum CodingKeys: String, CodingKey {
|
|
617
|
+
case kind
|
|
618
|
+
case label
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
public init(from decoder: any Decoder) throws {
|
|
622
|
+
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
623
|
+
let tag = try container.decode(String.self, forKey: .kind)
|
|
624
|
+
switch tag {
|
|
625
|
+
case "running":
|
|
626
|
+
self = .running
|
|
627
|
+
case "walking":
|
|
628
|
+
self = .walking
|
|
629
|
+
case "cycling":
|
|
630
|
+
self = .cycling
|
|
631
|
+
case "swimming":
|
|
632
|
+
self = .swimming
|
|
633
|
+
case "strengthTraining":
|
|
634
|
+
self = .strengthTraining
|
|
635
|
+
case "other":
|
|
636
|
+
let label = try container.decode(String.self, forKey: .label)
|
|
637
|
+
self = .other(label: label)
|
|
638
|
+
default:
|
|
639
|
+
throw DecodingError.dataCorruptedError(forKey: .kind, in: container, debugDescription: "unknown WorkoutActivity \(tag)")
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
public func encode(to encoder: any Encoder) throws {
|
|
644
|
+
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
645
|
+
switch self {
|
|
646
|
+
case .running:
|
|
647
|
+
try container.encode("running", forKey: .kind)
|
|
648
|
+
case .walking:
|
|
649
|
+
try container.encode("walking", forKey: .kind)
|
|
650
|
+
case .cycling:
|
|
651
|
+
try container.encode("cycling", forKey: .kind)
|
|
652
|
+
case .swimming:
|
|
653
|
+
try container.encode("swimming", forKey: .kind)
|
|
654
|
+
case .strengthTraining:
|
|
655
|
+
try container.encode("strengthTraining", forKey: .kind)
|
|
656
|
+
case .other(let label):
|
|
657
|
+
try container.encode("other", forKey: .kind)
|
|
658
|
+
try container.encode(label, forKey: .label)
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/// What the SDK believes about delivery right now. Wire spelling matches DiagnosticsWire.state
|
|
664
|
+
/// and Apple's DiagnosticsState Encodable.
|
|
665
|
+
public enum DiagnosticsState: Codable, Equatable, Sendable {
|
|
666
|
+
case healthy
|
|
667
|
+
case quiet(since: String)
|
|
668
|
+
case backgroundRestricted(reason: BackgroundRestrictionReason?)
|
|
669
|
+
case backgroundRefreshUnscheduled
|
|
670
|
+
case pendingRetry
|
|
671
|
+
case linkRevoked
|
|
672
|
+
case devIDMismatch
|
|
673
|
+
|
|
674
|
+
private enum CodingKeys: String, CodingKey {
|
|
675
|
+
case state
|
|
676
|
+
case since
|
|
677
|
+
case reason
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
public init(from decoder: any Decoder) throws {
|
|
681
|
+
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
682
|
+
let tag = try container.decode(String.self, forKey: .state)
|
|
683
|
+
switch tag {
|
|
684
|
+
case "healthy":
|
|
685
|
+
self = .healthy
|
|
686
|
+
case "quiet":
|
|
687
|
+
let since = try container.decode(String.self, forKey: .since)
|
|
688
|
+
self = .quiet(since: since)
|
|
689
|
+
case "backgroundRestricted":
|
|
690
|
+
let reason = try container.decodeIfPresent(BackgroundRestrictionReason.self, forKey: .reason)
|
|
691
|
+
self = .backgroundRestricted(reason: reason)
|
|
692
|
+
case "backgroundRefreshUnscheduled":
|
|
693
|
+
self = .backgroundRefreshUnscheduled
|
|
694
|
+
case "pendingRetry":
|
|
695
|
+
self = .pendingRetry
|
|
696
|
+
case "linkRevoked":
|
|
697
|
+
self = .linkRevoked
|
|
698
|
+
case "devIDMismatch":
|
|
699
|
+
self = .devIDMismatch
|
|
700
|
+
default:
|
|
701
|
+
throw DecodingError.dataCorruptedError(forKey: .state, in: container, debugDescription: "unknown DiagnosticsState \(tag)")
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
public func encode(to encoder: any Encoder) throws {
|
|
706
|
+
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
707
|
+
switch self {
|
|
708
|
+
case .healthy:
|
|
709
|
+
try container.encode("healthy", forKey: .state)
|
|
710
|
+
case .quiet(let since):
|
|
711
|
+
try container.encode("quiet", forKey: .state)
|
|
712
|
+
try container.encode(since, forKey: .since)
|
|
713
|
+
case .backgroundRestricted(let reason):
|
|
714
|
+
try container.encode("backgroundRestricted", forKey: .state)
|
|
715
|
+
try container.encodeIfPresent(reason, forKey: .reason)
|
|
716
|
+
case .backgroundRefreshUnscheduled:
|
|
717
|
+
try container.encode("backgroundRefreshUnscheduled", forKey: .state)
|
|
718
|
+
case .pendingRetry:
|
|
719
|
+
try container.encode("pendingRetry", forKey: .state)
|
|
720
|
+
case .linkRevoked:
|
|
721
|
+
try container.encode("linkRevoked", forKey: .state)
|
|
722
|
+
case .devIDMismatch:
|
|
723
|
+
try container.encode("devIDMismatch", forKey: .state)
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/// What a subscription delivers. A stream can fail or finish, and without saying so the
|
|
729
|
+
/// framework layer has to invent a convention for both - two of those, one per wrapper, is a
|
|
730
|
+
/// contract nobody wrote down.
|
|
731
|
+
public enum StreamMessage: Codable, Equatable, Sendable {
|
|
732
|
+
case data(element: BridgeJSON)
|
|
733
|
+
case error(error: TerraErrorPayload)
|
|
734
|
+
case complete
|
|
735
|
+
|
|
736
|
+
private enum CodingKeys: String, CodingKey {
|
|
737
|
+
case kind
|
|
738
|
+
case element
|
|
739
|
+
case error
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
public init(from decoder: any Decoder) throws {
|
|
743
|
+
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
744
|
+
let tag = try container.decode(String.self, forKey: .kind)
|
|
745
|
+
switch tag {
|
|
746
|
+
case "data":
|
|
747
|
+
let element = try container.decode(BridgeJSON.self, forKey: .element)
|
|
748
|
+
self = .data(element: element)
|
|
749
|
+
case "error":
|
|
750
|
+
let error = try container.decode(TerraErrorPayload.self, forKey: .error)
|
|
751
|
+
self = .error(error: error)
|
|
752
|
+
case "complete":
|
|
753
|
+
self = .complete
|
|
754
|
+
default:
|
|
755
|
+
throw DecodingError.dataCorruptedError(forKey: .kind, in: container, debugDescription: "unknown StreamMessage \(tag)")
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
public func encode(to encoder: any Encoder) throws {
|
|
760
|
+
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
761
|
+
switch self {
|
|
762
|
+
case .data(let element):
|
|
763
|
+
try container.encode("data", forKey: .kind)
|
|
764
|
+
try container.encode(element, forKey: .element)
|
|
765
|
+
case .error(let error):
|
|
766
|
+
try container.encode("error", forKey: .kind)
|
|
767
|
+
try container.encode(error, forKey: .error)
|
|
768
|
+
case .complete:
|
|
769
|
+
try container.encode("complete", forKey: .kind)
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}
|
|
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
|
+
|
|
945
|
+
/// Who recorded a sample. The only stable handle an app has for filtering by app or device.
|
|
946
|
+
public struct SampleSource: Codable, Equatable, Sendable {
|
|
947
|
+
public var name: String
|
|
948
|
+
public var bundleId: String
|
|
949
|
+
public var productType: String?
|
|
950
|
+
public var deviceType: String?
|
|
951
|
+
|
|
952
|
+
public init(name: String, bundleId: String, productType: String? = nil, deviceType: String? = nil) {
|
|
953
|
+
self.name = name
|
|
954
|
+
self.bundleId = bundleId
|
|
955
|
+
self.productType = productType
|
|
956
|
+
self.deviceType = deviceType
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
/// A link record. A value, not a handle: Apple's Connection carries sync and disconnect
|
|
961
|
+
/// methods, and an object with behaviour cannot cross the bridge, so those are ops on the
|
|
962
|
+
/// client.
|
|
963
|
+
public struct Connection: Codable, Equatable, Sendable {
|
|
964
|
+
public var source: Source
|
|
965
|
+
public var userId: String
|
|
966
|
+
/// Android's StoredConnection carries the store-side id. Apple's Connection does not.
|
|
967
|
+
///
|
|
968
|
+
/// Available on android only.
|
|
969
|
+
public var providerUserId: String?
|
|
970
|
+
|
|
971
|
+
public init(source: Source, userId: String, providerUserId: String? = nil) {
|
|
972
|
+
self.source = source
|
|
973
|
+
self.userId = userId
|
|
974
|
+
self.providerUserId = providerUserId
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
public struct HeartRateSample: Codable, Equatable, Sendable {
|
|
979
|
+
public var id: String
|
|
980
|
+
public var time: String
|
|
981
|
+
public var beatsPerMinute: Double
|
|
982
|
+
public var source: SampleSource
|
|
983
|
+
public var motionContext: MotionContext?
|
|
984
|
+
public var userEntered: Bool
|
|
985
|
+
public var timeZone: String?
|
|
986
|
+
|
|
987
|
+
public init(id: String, time: String, beatsPerMinute: Double, source: SampleSource, motionContext: MotionContext? = nil, userEntered: Bool, timeZone: String? = nil) {
|
|
988
|
+
self.id = id
|
|
989
|
+
self.time = time
|
|
990
|
+
self.beatsPerMinute = beatsPerMinute
|
|
991
|
+
self.source = source
|
|
992
|
+
self.motionContext = motionContext
|
|
993
|
+
self.userEntered = userEntered
|
|
994
|
+
self.timeZone = timeZone
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
/// A step total over an interval. Apple derives the id from the source and start; Android
|
|
999
|
+
/// carries a RecordId when it has one, so the id is optional on the wire.
|
|
1000
|
+
public struct StepCount: Codable, Equatable, Sendable {
|
|
1001
|
+
public var id: String?
|
|
1002
|
+
public var start: String
|
|
1003
|
+
public var end: String
|
|
1004
|
+
public var steps: Int
|
|
1005
|
+
public var source: SampleSource
|
|
1006
|
+
public var userEntered: Bool
|
|
1007
|
+
public var timeZone: String?
|
|
1008
|
+
|
|
1009
|
+
public init(id: String? = nil, start: String, end: String, steps: Int, source: SampleSource, userEntered: Bool, timeZone: String? = nil) {
|
|
1010
|
+
self.id = id
|
|
1011
|
+
self.start = start
|
|
1012
|
+
self.end = end
|
|
1013
|
+
self.steps = steps
|
|
1014
|
+
self.source = source
|
|
1015
|
+
self.userEntered = userEntered
|
|
1016
|
+
self.timeZone = timeZone
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
public struct SleepSegment: Codable, Equatable, Sendable {
|
|
1021
|
+
public var id: String
|
|
1022
|
+
public var start: String
|
|
1023
|
+
public var end: String
|
|
1024
|
+
public var stage: SleepStage
|
|
1025
|
+
public var source: SampleSource
|
|
1026
|
+
public var userEntered: Bool
|
|
1027
|
+
public var timeZone: String?
|
|
1028
|
+
|
|
1029
|
+
public init(id: String, start: String, end: String, stage: SleepStage, source: SampleSource, userEntered: Bool, timeZone: String? = nil) {
|
|
1030
|
+
self.id = id
|
|
1031
|
+
self.start = start
|
|
1032
|
+
self.end = end
|
|
1033
|
+
self.stage = stage
|
|
1034
|
+
self.source = source
|
|
1035
|
+
self.userEntered = userEntered
|
|
1036
|
+
self.timeZone = timeZone
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
/// indoor is optional because Health Connect does not always record it; Apple always has an
|
|
1041
|
+
/// answer.
|
|
1042
|
+
public struct Workout: Codable, Equatable, Sendable {
|
|
1043
|
+
public var id: String
|
|
1044
|
+
public var start: String
|
|
1045
|
+
public var end: String
|
|
1046
|
+
public var activity: WorkoutActivity
|
|
1047
|
+
public var activityTypeCode: Int
|
|
1048
|
+
public var indoor: Bool?
|
|
1049
|
+
public var activeEnergyKilocalories: Double?
|
|
1050
|
+
public var distanceMeters: Double?
|
|
1051
|
+
public var source: SampleSource
|
|
1052
|
+
public var userEntered: Bool
|
|
1053
|
+
public var timeZone: String?
|
|
1054
|
+
/// ExerciseSessionRecord.title on Android; HKMetadataKeyWorkoutBrandName on Apple, which has no
|
|
1055
|
+
/// title field.
|
|
1056
|
+
public var title: String?
|
|
1057
|
+
|
|
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) {
|
|
1059
|
+
self.id = id
|
|
1060
|
+
self.start = start
|
|
1061
|
+
self.end = end
|
|
1062
|
+
self.activity = activity
|
|
1063
|
+
self.activityTypeCode = activityTypeCode
|
|
1064
|
+
self.indoor = indoor
|
|
1065
|
+
self.activeEnergyKilocalories = activeEnergyKilocalories
|
|
1066
|
+
self.distanceMeters = distanceMeters
|
|
1067
|
+
self.source = source
|
|
1068
|
+
self.userEntered = userEntered
|
|
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
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
/// A day whose data changed. Carries no health value by construction.
|
|
1145
|
+
public struct HealthChange: Codable, Equatable, Sendable {
|
|
1146
|
+
public var category: DataCategory
|
|
1147
|
+
public var day: String
|
|
1148
|
+
|
|
1149
|
+
public init(category: DataCategory, day: String) {
|
|
1150
|
+
self.category = category
|
|
1151
|
+
self.day = day
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
public struct WorkoutScope: Codable, Equatable, Sendable {
|
|
1156
|
+
public var activity: WorkoutActivity
|
|
1157
|
+
public var energy: Bool
|
|
1158
|
+
public var distance: Bool
|
|
1159
|
+
|
|
1160
|
+
public init(activity: WorkoutActivity, energy: Bool, distance: Bool) {
|
|
1161
|
+
self.activity = activity
|
|
1162
|
+
self.energy = energy
|
|
1163
|
+
self.distance = distance
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
/// Lists rather than sets: JSON has no set, so the category lists are marked `set` and every
|
|
1168
|
+
/// generated codec de-duplicates and sorts them, so two orderings compare equal.
|
|
1169
|
+
public struct HealthAccess: Codable, Equatable, Sendable {
|
|
1170
|
+
@CaseSet public var read: [DataCategory]
|
|
1171
|
+
@CaseSet public var write: [DataCategory]
|
|
1172
|
+
public var workouts: [WorkoutScope]
|
|
1173
|
+
|
|
1174
|
+
public init(read: [DataCategory], write: [DataCategory], workouts: [WorkoutScope]) {
|
|
1175
|
+
self.read = read
|
|
1176
|
+
self.write = write
|
|
1177
|
+
self.workouts = workouts
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
public struct HeartRateEntry: Codable, Equatable, Sendable {
|
|
1182
|
+
public var time: String
|
|
1183
|
+
public var beatsPerMinute: Double
|
|
1184
|
+
public var recordId: String?
|
|
1185
|
+
|
|
1186
|
+
public init(time: String, beatsPerMinute: Double, recordId: String? = nil) {
|
|
1187
|
+
self.time = time
|
|
1188
|
+
self.beatsPerMinute = beatsPerMinute
|
|
1189
|
+
self.recordId = recordId
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
public struct StepEntry: Codable, Equatable, Sendable {
|
|
1194
|
+
public var start: String
|
|
1195
|
+
public var end: String
|
|
1196
|
+
public var steps: Int
|
|
1197
|
+
public var recordId: String?
|
|
1198
|
+
|
|
1199
|
+
public init(start: String, end: String, steps: Int, recordId: String? = nil) {
|
|
1200
|
+
self.start = start
|
|
1201
|
+
self.end = end
|
|
1202
|
+
self.steps = steps
|
|
1203
|
+
self.recordId = recordId
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
public struct SleepEntry: Codable, Equatable, Sendable {
|
|
1208
|
+
public var start: String
|
|
1209
|
+
public var end: String
|
|
1210
|
+
public var stage: SleepStage
|
|
1211
|
+
public var recordId: String?
|
|
1212
|
+
|
|
1213
|
+
public init(start: String, end: String, stage: SleepStage, recordId: String? = nil) {
|
|
1214
|
+
self.start = start
|
|
1215
|
+
self.end = end
|
|
1216
|
+
self.stage = stage
|
|
1217
|
+
self.recordId = recordId
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
public struct WorkoutEntry: Codable, Equatable, Sendable {
|
|
1222
|
+
public var start: String
|
|
1223
|
+
public var end: String
|
|
1224
|
+
public var activity: WorkoutActivity
|
|
1225
|
+
public var totalEnergyKilocalories: Double?
|
|
1226
|
+
public var totalDistanceMeters: Double?
|
|
1227
|
+
public var recordId: String?
|
|
1228
|
+
|
|
1229
|
+
public init(start: String, end: String, activity: WorkoutActivity, totalEnergyKilocalories: Double? = nil, totalDistanceMeters: Double? = nil, recordId: String? = nil) {
|
|
1230
|
+
self.start = start
|
|
1231
|
+
self.end = end
|
|
1232
|
+
self.activity = activity
|
|
1233
|
+
self.totalEnergyKilocalories = totalEnergyKilocalories
|
|
1234
|
+
self.totalDistanceMeters = totalDistanceMeters
|
|
1235
|
+
self.recordId = recordId
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
public struct BodyEntry: Codable, Equatable, Sendable {
|
|
1240
|
+
public var time: String
|
|
1241
|
+
public var bodyMassKilograms: Double?
|
|
1242
|
+
public var bodyFatPercent: Double?
|
|
1243
|
+
public var bodyMassIndex: Double?
|
|
1244
|
+
public var heightMeters: Double?
|
|
1245
|
+
public var leanBodyMassKilograms: Double?
|
|
1246
|
+
public var bodyTemperatureCelsius: Double?
|
|
1247
|
+
public var oxygenSaturationPercent: Double?
|
|
1248
|
+
public var bloodGlucoseMilligramsPerDeciliter: Double?
|
|
1249
|
+
public var bloodPressureSystolicMillimetersOfMercury: Double?
|
|
1250
|
+
public var bloodPressureDiastolicMillimetersOfMercury: Double?
|
|
1251
|
+
public var recordId: String?
|
|
1252
|
+
|
|
1253
|
+
public init(time: String, bodyMassKilograms: Double? = nil, bodyFatPercent: Double? = nil, bodyMassIndex: Double? = nil, heightMeters: Double? = nil, leanBodyMassKilograms: Double? = nil, bodyTemperatureCelsius: Double? = nil, oxygenSaturationPercent: Double? = nil, bloodGlucoseMilligramsPerDeciliter: Double? = nil, bloodPressureSystolicMillimetersOfMercury: Double? = nil, bloodPressureDiastolicMillimetersOfMercury: Double? = nil, recordId: String? = nil) {
|
|
1254
|
+
self.time = time
|
|
1255
|
+
self.bodyMassKilograms = bodyMassKilograms
|
|
1256
|
+
self.bodyFatPercent = bodyFatPercent
|
|
1257
|
+
self.bodyMassIndex = bodyMassIndex
|
|
1258
|
+
self.heightMeters = heightMeters
|
|
1259
|
+
self.leanBodyMassKilograms = leanBodyMassKilograms
|
|
1260
|
+
self.bodyTemperatureCelsius = bodyTemperatureCelsius
|
|
1261
|
+
self.oxygenSaturationPercent = oxygenSaturationPercent
|
|
1262
|
+
self.bloodGlucoseMilligramsPerDeciliter = bloodGlucoseMilligramsPerDeciliter
|
|
1263
|
+
self.bloodPressureSystolicMillimetersOfMercury = bloodPressureSystolicMillimetersOfMercury
|
|
1264
|
+
self.bloodPressureDiastolicMillimetersOfMercury = bloodPressureDiastolicMillimetersOfMercury
|
|
1265
|
+
self.recordId = recordId
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
/// Every field optional and every unit in the name, matching NutritionEntry.Nutrients on both
|
|
1270
|
+
/// platforms field for field.
|
|
1271
|
+
public struct Nutrients: Codable, Equatable, Sendable {
|
|
1272
|
+
public var energyKilocalories: Double?
|
|
1273
|
+
public var proteinGrams: Double?
|
|
1274
|
+
public var carbohydratesGrams: Double?
|
|
1275
|
+
public var fiberGrams: Double?
|
|
1276
|
+
public var sugarGrams: Double?
|
|
1277
|
+
public var fatTotalGrams: Double?
|
|
1278
|
+
public var fatSaturatedGrams: Double?
|
|
1279
|
+
public var fatMonounsaturatedGrams: Double?
|
|
1280
|
+
public var fatPolyunsaturatedGrams: Double?
|
|
1281
|
+
public var cholesterolMilligrams: Double?
|
|
1282
|
+
public var sodiumMilligrams: Double?
|
|
1283
|
+
public var potassiumMilligrams: Double?
|
|
1284
|
+
public var calciumMilligrams: Double?
|
|
1285
|
+
public var ironMilligrams: Double?
|
|
1286
|
+
public var magnesiumMilligrams: Double?
|
|
1287
|
+
public var zincMilligrams: Double?
|
|
1288
|
+
public var waterLiters: Double?
|
|
1289
|
+
public var caffeineMilligrams: Double?
|
|
1290
|
+
public var vitaminAMicrograms: Double?
|
|
1291
|
+
public var vitaminCMilligrams: Double?
|
|
1292
|
+
public var vitaminDMicrograms: Double?
|
|
1293
|
+
public var vitaminEMilligrams: Double?
|
|
1294
|
+
public var vitaminKMicrograms: Double?
|
|
1295
|
+
public var vitaminB6Milligrams: Double?
|
|
1296
|
+
public var vitaminB12Micrograms: Double?
|
|
1297
|
+
public var folateMicrograms: Double?
|
|
1298
|
+
public var thiaminMilligrams: Double?
|
|
1299
|
+
public var riboflavinMilligrams: Double?
|
|
1300
|
+
public var niacinMilligrams: Double?
|
|
1301
|
+
public var biotinMicrograms: Double?
|
|
1302
|
+
public var pantothenicAcidMilligrams: Double?
|
|
1303
|
+
public var phosphorusMilligrams: Double?
|
|
1304
|
+
public var iodineMicrograms: Double?
|
|
1305
|
+
public var chromiumMicrograms: Double?
|
|
1306
|
+
public var copperMilligrams: Double?
|
|
1307
|
+
public var manganeseMilligrams: Double?
|
|
1308
|
+
public var molybdenumMicrograms: Double?
|
|
1309
|
+
public var seleniumMicrograms: Double?
|
|
1310
|
+
public var chlorideMilligrams: Double?
|
|
1311
|
+
|
|
1312
|
+
public init(energyKilocalories: Double? = nil, proteinGrams: Double? = nil, carbohydratesGrams: Double? = nil, fiberGrams: Double? = nil, sugarGrams: Double? = nil, fatTotalGrams: Double? = nil, fatSaturatedGrams: Double? = nil, fatMonounsaturatedGrams: Double? = nil, fatPolyunsaturatedGrams: Double? = nil, cholesterolMilligrams: Double? = nil, sodiumMilligrams: Double? = nil, potassiumMilligrams: Double? = nil, calciumMilligrams: Double? = nil, ironMilligrams: Double? = nil, magnesiumMilligrams: Double? = nil, zincMilligrams: Double? = nil, waterLiters: Double? = nil, caffeineMilligrams: Double? = nil, vitaminAMicrograms: Double? = nil, vitaminCMilligrams: Double? = nil, vitaminDMicrograms: Double? = nil, vitaminEMilligrams: Double? = nil, vitaminKMicrograms: Double? = nil, vitaminB6Milligrams: Double? = nil, vitaminB12Micrograms: Double? = nil, folateMicrograms: Double? = nil, thiaminMilligrams: Double? = nil, riboflavinMilligrams: Double? = nil, niacinMilligrams: Double? = nil, biotinMicrograms: Double? = nil, pantothenicAcidMilligrams: Double? = nil, phosphorusMilligrams: Double? = nil, iodineMicrograms: Double? = nil, chromiumMicrograms: Double? = nil, copperMilligrams: Double? = nil, manganeseMilligrams: Double? = nil, molybdenumMicrograms: Double? = nil, seleniumMicrograms: Double? = nil, chlorideMilligrams: Double? = nil) {
|
|
1313
|
+
self.energyKilocalories = energyKilocalories
|
|
1314
|
+
self.proteinGrams = proteinGrams
|
|
1315
|
+
self.carbohydratesGrams = carbohydratesGrams
|
|
1316
|
+
self.fiberGrams = fiberGrams
|
|
1317
|
+
self.sugarGrams = sugarGrams
|
|
1318
|
+
self.fatTotalGrams = fatTotalGrams
|
|
1319
|
+
self.fatSaturatedGrams = fatSaturatedGrams
|
|
1320
|
+
self.fatMonounsaturatedGrams = fatMonounsaturatedGrams
|
|
1321
|
+
self.fatPolyunsaturatedGrams = fatPolyunsaturatedGrams
|
|
1322
|
+
self.cholesterolMilligrams = cholesterolMilligrams
|
|
1323
|
+
self.sodiumMilligrams = sodiumMilligrams
|
|
1324
|
+
self.potassiumMilligrams = potassiumMilligrams
|
|
1325
|
+
self.calciumMilligrams = calciumMilligrams
|
|
1326
|
+
self.ironMilligrams = ironMilligrams
|
|
1327
|
+
self.magnesiumMilligrams = magnesiumMilligrams
|
|
1328
|
+
self.zincMilligrams = zincMilligrams
|
|
1329
|
+
self.waterLiters = waterLiters
|
|
1330
|
+
self.caffeineMilligrams = caffeineMilligrams
|
|
1331
|
+
self.vitaminAMicrograms = vitaminAMicrograms
|
|
1332
|
+
self.vitaminCMilligrams = vitaminCMilligrams
|
|
1333
|
+
self.vitaminDMicrograms = vitaminDMicrograms
|
|
1334
|
+
self.vitaminEMilligrams = vitaminEMilligrams
|
|
1335
|
+
self.vitaminKMicrograms = vitaminKMicrograms
|
|
1336
|
+
self.vitaminB6Milligrams = vitaminB6Milligrams
|
|
1337
|
+
self.vitaminB12Micrograms = vitaminB12Micrograms
|
|
1338
|
+
self.folateMicrograms = folateMicrograms
|
|
1339
|
+
self.thiaminMilligrams = thiaminMilligrams
|
|
1340
|
+
self.riboflavinMilligrams = riboflavinMilligrams
|
|
1341
|
+
self.niacinMilligrams = niacinMilligrams
|
|
1342
|
+
self.biotinMicrograms = biotinMicrograms
|
|
1343
|
+
self.pantothenicAcidMilligrams = pantothenicAcidMilligrams
|
|
1344
|
+
self.phosphorusMilligrams = phosphorusMilligrams
|
|
1345
|
+
self.iodineMicrograms = iodineMicrograms
|
|
1346
|
+
self.chromiumMicrograms = chromiumMicrograms
|
|
1347
|
+
self.copperMilligrams = copperMilligrams
|
|
1348
|
+
self.manganeseMilligrams = manganeseMilligrams
|
|
1349
|
+
self.molybdenumMicrograms = molybdenumMicrograms
|
|
1350
|
+
self.seleniumMicrograms = seleniumMicrograms
|
|
1351
|
+
self.chlorideMilligrams = chlorideMilligrams
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
public struct NutritionEntry: Codable, Equatable, Sendable {
|
|
1356
|
+
public var start: String
|
|
1357
|
+
public var end: String
|
|
1358
|
+
public var name: String?
|
|
1359
|
+
public var nutrients: Nutrients
|
|
1360
|
+
public var recordId: String?
|
|
1361
|
+
|
|
1362
|
+
public init(start: String, end: String, name: String? = nil, nutrients: Nutrients, recordId: String? = nil) {
|
|
1363
|
+
self.start = start
|
|
1364
|
+
self.end = end
|
|
1365
|
+
self.name = name
|
|
1366
|
+
self.nutrients = nutrients
|
|
1367
|
+
self.recordId = recordId
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
/// An error carried as a value rather than thrown, inside a sync result. The same shape the
|
|
1372
|
+
/// bridge rejects a promise with.
|
|
1373
|
+
public struct TerraErrorPayload: Codable, Equatable, Sendable {
|
|
1374
|
+
public var code: String
|
|
1375
|
+
public var retryable: Bool
|
|
1376
|
+
public var message: String
|
|
1377
|
+
public var source: Source?
|
|
1378
|
+
public var details: BridgeJSON?
|
|
1379
|
+
|
|
1380
|
+
public init(code: String, retryable: Bool, message: String, source: Source? = nil, details: BridgeJSON? = nil) {
|
|
1381
|
+
self.code = code
|
|
1382
|
+
self.retryable = retryable
|
|
1383
|
+
self.message = message
|
|
1384
|
+
self.source = source
|
|
1385
|
+
self.details = details
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
/// Available on ios only.
|
|
1390
|
+
public struct PlannedWorkout: Codable, Equatable, Sendable {
|
|
1391
|
+
public var id: String
|
|
1392
|
+
public var plannedOn: String
|
|
1393
|
+
public var activity: WorkoutActivity
|
|
1394
|
+
public var title: String?
|
|
1395
|
+
public var completedAt: String?
|
|
1396
|
+
|
|
1397
|
+
public init(id: String, plannedOn: String, activity: WorkoutActivity, title: String? = nil, completedAt: String? = nil) {
|
|
1398
|
+
self.id = id
|
|
1399
|
+
self.plannedOn = plannedOn
|
|
1400
|
+
self.activity = activity
|
|
1401
|
+
self.title = title
|
|
1402
|
+
self.completedAt = completedAt
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
/// The flat object both SDKs already emit: `at`, `event`, and whatever keys that kind carries.
|
|
1407
|
+
/// Carried through unchanged rather than reshaped, because reshaping it would be a hand-written
|
|
1408
|
+
/// second contract on each platform that no vector could pin - and the encoding it would be
|
|
1409
|
+
/// translating is one DiagnosticsWire and Apple's Encodable already agree on, and that
|
|
1410
|
+
/// EventPrivacyTest already guards against carrying a health value.
|
|
1411
|
+
public typealias TerraEvent = BridgeJSON
|
|
1412
|
+
|
|
1413
|
+
/// The redacted report, carried as the wire object DiagnosticsWire.report and Apple's
|
|
1414
|
+
/// SupportReport.json already produce.
|
|
1415
|
+
public struct SupportReport: Codable, Equatable, Sendable {
|
|
1416
|
+
public var sdkVersion: String
|
|
1417
|
+
public var generatedAt: String
|
|
1418
|
+
public var state: DiagnosticsState
|
|
1419
|
+
public var lastDelivery: String?
|
|
1420
|
+
public var isConnected: Bool
|
|
1421
|
+
public var requestedCategories: [String]
|
|
1422
|
+
public var requestedWrites: [String]
|
|
1423
|
+
public var requestedWorkouts: [String]
|
|
1424
|
+
public var recentEvents: [TerraEvent]
|
|
1425
|
+
|
|
1426
|
+
public init(sdkVersion: String, generatedAt: String, state: DiagnosticsState, lastDelivery: String? = nil, isConnected: Bool, requestedCategories: [String], requestedWrites: [String], requestedWorkouts: [String], recentEvents: [TerraEvent]) {
|
|
1427
|
+
self.sdkVersion = sdkVersion
|
|
1428
|
+
self.generatedAt = generatedAt
|
|
1429
|
+
self.state = state
|
|
1430
|
+
self.lastDelivery = lastDelivery
|
|
1431
|
+
self.isConnected = isConnected
|
|
1432
|
+
self.requestedCategories = requestedCategories
|
|
1433
|
+
self.requestedWrites = requestedWrites
|
|
1434
|
+
self.requestedWorkouts = requestedWorkouts
|
|
1435
|
+
self.recentEvents = recentEvents
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
/// What the JS side needs to know about the native client it is attaching to. bootstrapped
|
|
1440
|
+
/// false is a host setup bug, and the wrapper says so with the exact line to add.
|
|
1441
|
+
public struct BootstrapState: Codable, Equatable, Sendable {
|
|
1442
|
+
public var bootstrapped: Bool
|
|
1443
|
+
public var devId: String?
|
|
1444
|
+
public var platform: String
|
|
1445
|
+
public var sdkVersion: String
|
|
1446
|
+
public var backgroundRegistered: Bool
|
|
1447
|
+
|
|
1448
|
+
public init(bootstrapped: Bool, devId: String? = nil, platform: String, sdkVersion: String, backgroundRegistered: Bool) {
|
|
1449
|
+
self.bootstrapped = bootstrapped
|
|
1450
|
+
self.devId = devId
|
|
1451
|
+
self.platform = platform
|
|
1452
|
+
self.sdkVersion = sdkVersion
|
|
1453
|
+
self.backgroundRegistered = backgroundRegistered
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
/// The broker asking JS for a link token. Answered by token.resolve or token.reject with the
|
|
1458
|
+
/// same requestId.
|
|
1459
|
+
public struct TokenRequest: Codable, Equatable, Sendable {
|
|
1460
|
+
public var requestId: String
|
|
1461
|
+
|
|
1462
|
+
public init(requestId: String) {
|
|
1463
|
+
self.requestId = requestId
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
/// No request fields, or no response value.
|
|
1468
|
+
public struct Empty: Codable, Equatable, Sendable {
|
|
1469
|
+
public init() {}
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
public struct SourceRequest: Codable, Equatable, Sendable {
|
|
1473
|
+
public var source: Source?
|
|
1474
|
+
|
|
1475
|
+
public init(source: Source? = nil) {
|
|
1476
|
+
self.source = source
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
public struct RangeRequest: Codable, Equatable, Sendable {
|
|
1481
|
+
public var range: TimeRange
|
|
1482
|
+
|
|
1483
|
+
public init(range: TimeRange) {
|
|
1484
|
+
self.range = range
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
public struct CategoryRequest: Codable, Equatable, Sendable {
|
|
1489
|
+
public var category: DataCategory
|
|
1490
|
+
public var source: Source?
|
|
1491
|
+
|
|
1492
|
+
public init(category: DataCategory, source: Source? = nil) {
|
|
1493
|
+
self.category = category
|
|
1494
|
+
self.source = source
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
public struct RecordIdRequest: Codable, Equatable, Sendable {
|
|
1499
|
+
public var id: String
|
|
1500
|
+
|
|
1501
|
+
public init(id: String) {
|
|
1502
|
+
self.id = id
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
public struct ConnectRequest: Codable, Equatable, Sendable {
|
|
1507
|
+
public var source: Source?
|
|
1508
|
+
public var endUser: EndUser
|
|
1509
|
+
/// A grant already in hand, from a QR code or a deep link. Absent means the token provider is
|
|
1510
|
+
/// asked.
|
|
1511
|
+
public var token: String?
|
|
1512
|
+
|
|
1513
|
+
public init(source: Source? = nil, endUser: EndUser, token: String? = nil) {
|
|
1514
|
+
self.source = source
|
|
1515
|
+
self.endUser = endUser
|
|
1516
|
+
self.token = token
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
public struct PermissionRequest: Codable, Equatable, Sendable {
|
|
1521
|
+
@CaseSet public var read: [DataCategory]
|
|
1522
|
+
@CaseSet public var write: [DataCategory]
|
|
1523
|
+
public var workouts: [WorkoutScope]
|
|
1524
|
+
public var source: Source?
|
|
1525
|
+
|
|
1526
|
+
public init(read: [DataCategory], write: [DataCategory], workouts: [WorkoutScope], source: Source? = nil) {
|
|
1527
|
+
self.read = read
|
|
1528
|
+
self.write = write
|
|
1529
|
+
self.workouts = workouts
|
|
1530
|
+
self.source = source
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
/// What the sheet granted. Android answers with the access this call obtained; HealthKit will
|
|
1535
|
+
/// not report read grants, so Apple answers nothing at all.
|
|
1536
|
+
public struct PermissionGrant: Codable, Equatable, Sendable {
|
|
1537
|
+
/// Available on android only.
|
|
1538
|
+
public var granted: HealthAccess?
|
|
1539
|
+
|
|
1540
|
+
public init(granted: HealthAccess? = nil) {
|
|
1541
|
+
self.granted = granted
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
public struct TokenResolveRequest: Codable, Equatable, Sendable {
|
|
1546
|
+
public var requestId: String
|
|
1547
|
+
public var token: String
|
|
1548
|
+
|
|
1549
|
+
public init(requestId: String, token: String) {
|
|
1550
|
+
self.requestId = requestId
|
|
1551
|
+
self.token = token
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
public struct TokenRejectRequest: Codable, Equatable, Sendable {
|
|
1556
|
+
public var requestId: String
|
|
1557
|
+
public var message: String
|
|
1558
|
+
|
|
1559
|
+
public init(requestId: String, message: String) {
|
|
1560
|
+
self.requestId = requestId
|
|
1561
|
+
self.message = message
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
public struct TokenProviderRequest: Codable, Equatable, Sendable {
|
|
1566
|
+
public var present: Bool
|
|
1567
|
+
public var timeoutMs: Int
|
|
1568
|
+
|
|
1569
|
+
public init(present: Bool, timeoutMs: Int) {
|
|
1570
|
+
self.present = present
|
|
1571
|
+
self.timeoutMs = timeoutMs
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
/// Available on ios only.
|
|
1576
|
+
public struct MarkCompleteRequest: Codable, Equatable, Sendable {
|
|
1577
|
+
public var id: String
|
|
1578
|
+
public var at: String?
|
|
1579
|
+
|
|
1580
|
+
public init(id: String, at: String? = nil) {
|
|
1581
|
+
self.id = id
|
|
1582
|
+
self.at = at
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
public struct BoolResult: Codable, Equatable, Sendable {
|
|
1587
|
+
public var value: Bool
|
|
1588
|
+
|
|
1589
|
+
public init(value: Bool) {
|
|
1590
|
+
self.value = value
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
public struct StatusResult: Codable, Equatable, Sendable {
|
|
1595
|
+
public var status: PermissionStatus
|
|
1596
|
+
|
|
1597
|
+
public init(status: PermissionStatus) {
|
|
1598
|
+
self.status = status
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
/// Available on android only.
|
|
1603
|
+
public struct StatusesResult: Codable, Equatable, Sendable {
|
|
1604
|
+
public var statuses: [DataCategory: PermissionStatus]?
|
|
1605
|
+
|
|
1606
|
+
public init(statuses: [DataCategory: PermissionStatus]? = nil) {
|
|
1607
|
+
self.statuses = statuses
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
public struct RecordIdList: Codable, Equatable, Sendable {
|
|
1612
|
+
public var ids: [String]
|
|
1613
|
+
|
|
1614
|
+
public init(ids: [String]) {
|
|
1615
|
+
self.ids = ids
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
public struct ConnectionList: Codable, Equatable, Sendable {
|
|
1620
|
+
public var connections: [Connection]
|
|
1621
|
+
|
|
1622
|
+
public init(connections: [Connection]) {
|
|
1623
|
+
self.connections = connections
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
public struct HeartRateList: Codable, Equatable, Sendable {
|
|
1628
|
+
public var samples: [HeartRateSample]
|
|
1629
|
+
|
|
1630
|
+
public init(samples: [HeartRateSample]) {
|
|
1631
|
+
self.samples = samples
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
public struct StepCountList: Codable, Equatable, Sendable {
|
|
1636
|
+
public var samples: [StepCount]
|
|
1637
|
+
|
|
1638
|
+
public init(samples: [StepCount]) {
|
|
1639
|
+
self.samples = samples
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
public struct SleepSegmentList: Codable, Equatable, Sendable {
|
|
1644
|
+
public var samples: [SleepSegment]
|
|
1645
|
+
|
|
1646
|
+
public init(samples: [SleepSegment]) {
|
|
1647
|
+
self.samples = samples
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
public struct WorkoutList: Codable, Equatable, Sendable {
|
|
1652
|
+
public var samples: [Workout]
|
|
1653
|
+
|
|
1654
|
+
public init(samples: [Workout]) {
|
|
1655
|
+
self.samples = samples
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
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
|
+
|
|
1720
|
+
/// Available on ios only.
|
|
1721
|
+
public struct PlannedWorkoutList: Codable, Equatable, Sendable {
|
|
1722
|
+
public var workouts: [PlannedWorkout]
|
|
1723
|
+
|
|
1724
|
+
public init(workouts: [PlannedWorkout]) {
|
|
1725
|
+
self.workouts = workouts
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
/// Once authorized, every sync run keeps the Watch schedule in step with Terra.
|
|
1730
|
+
///
|
|
1731
|
+
/// Available on ios only.
|
|
1732
|
+
public struct WorkoutScheduling: Codable, Equatable, Sendable {
|
|
1733
|
+
public var status: WorkoutSchedulingStatus
|
|
1734
|
+
|
|
1735
|
+
public init(status: WorkoutSchedulingStatus) {
|
|
1736
|
+
self.status = status
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
public struct LastDeliveryResult: Codable, Equatable, Sendable {
|
|
1741
|
+
public var at: String?
|
|
1742
|
+
|
|
1743
|
+
public init(at: String? = nil) {
|
|
1744
|
+
self.at = at
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
public struct HeartRateEntryList: Codable, Equatable, Sendable {
|
|
1749
|
+
public var entries: [HeartRateEntry]
|
|
1750
|
+
|
|
1751
|
+
public init(entries: [HeartRateEntry]) {
|
|
1752
|
+
self.entries = entries
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
public struct BodyEntryList: Codable, Equatable, Sendable {
|
|
1757
|
+
public var entries: [BodyEntry]
|
|
1758
|
+
|
|
1759
|
+
public init(entries: [BodyEntry]) {
|
|
1760
|
+
self.entries = entries
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
public struct StepEntryList: Codable, Equatable, Sendable {
|
|
1765
|
+
public var entries: [StepEntry]
|
|
1766
|
+
|
|
1767
|
+
public init(entries: [StepEntry]) {
|
|
1768
|
+
self.entries = entries
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
public struct SleepEntryList: Codable, Equatable, Sendable {
|
|
1773
|
+
public var entries: [SleepEntry]
|
|
1774
|
+
|
|
1775
|
+
public init(entries: [SleepEntry]) {
|
|
1776
|
+
self.entries = entries
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
public struct WorkoutEntryList: Codable, Equatable, Sendable {
|
|
1781
|
+
public var entries: [WorkoutEntry]
|
|
1782
|
+
|
|
1783
|
+
public init(entries: [WorkoutEntry]) {
|
|
1784
|
+
self.entries = entries
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
public struct NutritionEntryList: Codable, Equatable, Sendable {
|
|
1789
|
+
public var entries: [NutritionEntry]
|
|
1790
|
+
|
|
1791
|
+
public init(entries: [NutritionEntry]) {
|
|
1792
|
+
self.entries = entries
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
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
|
+
|
|
2028
|
+
public enum TerraEventEnvelope {
|
|
2029
|
+
public static func validate(_ value: TerraEvent) throws {
|
|
2030
|
+
guard case .string = value.storage["at"] else {
|
|
2031
|
+
throw DecodingError.dataCorrupted(
|
|
2032
|
+
.init(codingPath: [], debugDescription: "TerraEvent.at is not a string")
|
|
2033
|
+
)
|
|
2034
|
+
}
|
|
2035
|
+
guard case .string = value.storage["event"] else {
|
|
2036
|
+
throw DecodingError.dataCorrupted(
|
|
2037
|
+
.init(codingPath: [], debugDescription: "TerraEvent.event is not a string")
|
|
2038
|
+
)
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
public enum SupportReportEnvelope {
|
|
2044
|
+
public static func validate(_ value: SupportReport) throws {
|
|
2045
|
+
for level0 in value.recentEvents {
|
|
2046
|
+
try TerraEventEnvelope.validate(level0)
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
|