@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.
Files changed (112) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +142 -2
  3. package/TerraReactNative.podspec +48 -0
  4. package/android/build.gradle +87 -0
  5. package/android/src/bridgeCore/kotlin/README.md +1 -0
  6. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/BridgeBootstrap.kt +133 -0
  7. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/BridgeHost.kt +32 -0
  8. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/Conversions.kt +557 -0
  9. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/LauncherSlots.kt +41 -0
  10. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/ReadConversions.kt +250 -0
  11. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/Rejections.kt +150 -0
  12. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/StreamRegistry.kt +77 -0
  13. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TerraBridge.kt +86 -0
  14. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TerraBridgeOpsImpl.kt +300 -0
  15. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TerraBridgeStreamsImpl.kt +50 -0
  16. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TokenBroker.kt +198 -0
  17. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/generated/BridgeErrors.kt +217 -0
  18. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/generated/BridgeOps.kt +582 -0
  19. package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/generated/BridgeTypes.kt +1569 -0
  20. package/android/src/healthConnectOnly/kotlin/co/tryterra/sdk/reactnative/TerraStores.kt +11 -0
  21. package/android/src/main/AndroidManifest.xml +24 -0
  22. package/android/src/main/java/co/tryterra/sdk/reactnative/TerraBridgeModule.kt +96 -0
  23. package/android/src/main/java/co/tryterra/sdk/reactnative/TerraReactNativePackage.kt +26 -0
  24. package/android/src/main/java/co/tryterra/sdk/reactnative/TerraStartup.kt +46 -0
  25. package/android/src/samsung/kotlin/co/tryterra/sdk/reactnative/TerraStores.kt +13 -0
  26. package/docs/availability.md +396 -0
  27. package/frameworks/TerraSDK.xcframework/Info.plist +48 -0
  28. package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Info.plist +0 -0
  29. package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.abi.json +82672 -0
  30. package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.package.swiftinterface +2186 -0
  31. package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +2186 -0
  32. package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  33. package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.swiftinterface +2163 -0
  34. package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/PrivacyInfo.xcprivacy +96 -0
  35. package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/TerraSDK +0 -0
  36. package/frameworks/TerraSDK.xcframework/ios-arm64/dSYMs/TerraSDK.framework.dSYM/Contents/Info.plist +20 -0
  37. package/frameworks/TerraSDK.xcframework/ios-arm64/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/DWARF/TerraSDK +0 -0
  38. package/frameworks/TerraSDK.xcframework/ios-arm64/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/Relocations/aarch64/TerraSDK.yml +6594 -0
  39. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Info.plist +0 -0
  40. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +82672 -0
  41. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.package.swiftinterface +2186 -0
  42. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +2186 -0
  43. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  44. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +2163 -0
  45. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +82672 -0
  46. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.package.swiftinterface +2186 -0
  47. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +2186 -0
  48. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  49. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +2163 -0
  50. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/PrivacyInfo.xcprivacy +96 -0
  51. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/TerraSDK +0 -0
  52. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/_CodeSignature/CodeResources +223 -0
  53. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Info.plist +20 -0
  54. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/DWARF/TerraSDK +0 -0
  55. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/Relocations/aarch64/TerraSDK.yml +6595 -0
  56. package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/Relocations/x86_64/TerraSDK.yml +6513 -0
  57. package/ios/TerraBridgeCore/BridgeBootstrap.swift +89 -0
  58. package/ios/TerraBridgeCore/BridgeHost.swift +13 -0
  59. package/ios/TerraBridgeCore/Conversions+Reads.swift +259 -0
  60. package/ios/TerraBridgeCore/Conversions.swift +594 -0
  61. package/ios/TerraBridgeCore/Generated/BridgeErrors.swift +179 -0
  62. package/ios/TerraBridgeCore/Generated/BridgeOps.swift +967 -0
  63. package/ios/TerraBridgeCore/Generated/BridgeTypes.swift +2050 -0
  64. package/ios/TerraBridgeCore/Locked.swift +15 -0
  65. package/ios/TerraBridgeCore/README.md +1 -0
  66. package/ios/TerraBridgeCore/Rejections.swift +130 -0
  67. package/ios/TerraBridgeCore/RuntimeAttachment.swift +60 -0
  68. package/ios/TerraBridgeCore/StreamRegistry.swift +59 -0
  69. package/ios/TerraBridgeCore/TerraBridge.swift +122 -0
  70. package/ios/TerraBridgeCore/TerraBridgeOpsImpl.swift +318 -0
  71. package/ios/TerraBridgeCore/TerraBridgeStreamsImpl.swift +60 -0
  72. package/ios/TerraBridgeCore/TokenBroker.swift +147 -0
  73. package/ios/TerraBridgeModule.mm +79 -0
  74. package/ios/TerraBridgeModuleImpl.swift +49 -0
  75. package/ios/TerraReactNative.swift +19 -0
  76. package/lib/EndUser.d.ts +12 -0
  77. package/lib/EndUser.js +13 -0
  78. package/lib/NativeTerraBridge.d.ts +23 -0
  79. package/lib/NativeTerraBridge.js +13 -0
  80. package/lib/Terra.d.ts +223 -0
  81. package/lib/Terra.js +336 -0
  82. package/lib/TerraError.d.ts +42 -0
  83. package/lib/TerraError.js +95 -0
  84. package/lib/createTerra.d.ts +8 -0
  85. package/lib/createTerra.js +19 -0
  86. package/lib/generated/client.d.ts +128 -0
  87. package/lib/generated/client.js +256 -0
  88. package/lib/generated/codecs.d.ts +219 -0
  89. package/lib/generated/codecs.js +2066 -0
  90. package/lib/generated/errors.d.ts +13 -0
  91. package/lib/generated/errors.js +68 -0
  92. package/lib/generated/ops.d.ts +13 -0
  93. package/lib/generated/ops.js +161 -0
  94. package/lib/generated/streams.d.ts +23 -0
  95. package/lib/generated/streams.js +58 -0
  96. package/lib/generated/types.d.ts +794 -0
  97. package/lib/generated/types.js +224 -0
  98. package/lib/index.d.ts +29 -0
  99. package/lib/index.js +55 -0
  100. package/package.json +71 -4
  101. package/src/EndUser.ts +15 -0
  102. package/src/NativeTerraBridge.ts +27 -0
  103. package/src/Terra.ts +592 -0
  104. package/src/TerraError.ts +116 -0
  105. package/src/createTerra.ts +18 -0
  106. package/src/generated/client.ts +315 -0
  107. package/src/generated/codecs.ts +1989 -0
  108. package/src/generated/errors.ts +113 -0
  109. package/src/generated/ops.ts +233 -0
  110. package/src/generated/streams.ts +74 -0
  111. package/src/generated/types.ts +1222 -0
  112. package/src/index.ts +54 -0
@@ -0,0 +1,89 @@
1
+ import Foundation
2
+ import TerraSDK
3
+
4
+ public enum BridgeBootstrap {
5
+ /// The Info.plist key carrying the developer id.
6
+ public static let devIDKey = "TerraDevID"
7
+
8
+ public struct Configuration: Sendable, Equatable {
9
+ public let devID: String
10
+
11
+ public init(devID: String) {
12
+ self.devID = devID
13
+ }
14
+
15
+ /// The configuration in `bundle`'s Info.plist, or nil when it names no dev id.
16
+ public init?(bundle: Bundle) {
17
+ guard let devID = (bundle.object(forInfoDictionaryKey: BridgeBootstrap.devIDKey) as? String)?
18
+ .trimmingCharacters(in: .whitespacesAndNewlines), !devID.isEmpty
19
+ else { return nil }
20
+ self.init(devID: devID)
21
+ }
22
+ }
23
+
24
+ private static let parked = Locked<TerraBridge?>(nil)
25
+
26
+ /// The bridge bootstrap parked, or nil when it has not run.
27
+ public static var current: TerraBridge? { parked.withLock { $0 } }
28
+
29
+ @discardableResult
30
+ public static func bootstrap(bundle: Bundle = .main) -> TerraBridge? {
31
+ guard let configuration = Configuration(bundle: bundle) else { return nil }
32
+ return bootstrap(configuration)
33
+ }
34
+
35
+ @discardableResult
36
+ public static func bootstrap(_ configuration: Configuration) -> TerraBridge {
37
+ bootstrap(configuration, register: registerBackgroundSync) { provider in
38
+ Terra(devID: configuration.devID, tokenProvider: provider)
39
+ }
40
+ }
41
+
42
+ static func bootstrap(
43
+ _ configuration: Configuration,
44
+ register: (Terra) -> Bool,
45
+ client: (_ tokenProvider: @escaping TerraBridge.TokenProvider) -> Terra
46
+ ) -> TerraBridge {
47
+ parked.withLock { parked in
48
+ if let existing = parked { return existing }
49
+ let host = LaunchHost(devID: configuration.devID)
50
+ let bridge = TerraBridge(host: host, client: client)
51
+ host.registered.withLock { $0 = register(bridge.terra) }
52
+ parked = bridge
53
+ return bridge
54
+ }
55
+ }
56
+
57
+ public static func attach(
58
+ missingLine: String,
59
+ emit: @escaping @Sendable (_ subscriptionId: String, _ message: String) -> Void
60
+ ) throws(BridgeRejection) -> BridgeSession {
61
+ guard let bridge = current else {
62
+ throw BridgeRejection(payload: Rejections.payload(for: BridgeError.notBootstrapped(line: missingLine)))
63
+ }
64
+ return bridge.attach(emit: emit)
65
+ }
66
+
67
+ static func reset() {
68
+ parked.withLock { $0 = nil }
69
+ }
70
+
71
+ private static func registerBackgroundSync(_ terra: Terra) -> Bool {
72
+ #if os(iOS)
73
+ return BackgroundSync.register(terra: terra)
74
+ #else
75
+ return false
76
+ #endif
77
+ }
78
+ }
79
+
80
+ final class LaunchHost: BridgeHost {
81
+ let devID: String?
82
+ let registered = Locked(false)
83
+
84
+ init(devID: String) {
85
+ self.devID = devID
86
+ }
87
+
88
+ func backgroundRegistered() async -> Bool { registered.withLock { $0 } }
89
+ }
@@ -0,0 +1,13 @@
1
+ import Foundation
2
+
3
+ /// The parts of an op that are not the SDK: the bootstrap's own record of what it set up.
4
+ ///
5
+ /// A seam so that everything in front of it runs on a macOS host with no simulator. The production
6
+ /// conformance belongs to bootstrap (P5), which is the code that registered the task.
7
+ public protocol BridgeHost: Sendable {
8
+ /// The dev id the native bootstrap was configured with.
9
+ var devID: String? { get }
10
+
11
+ /// Whether bootstrap registered background sync at launch. False is a host setup bug.
12
+ func backgroundRegistered() async -> Bool
13
+ }
@@ -0,0 +1,259 @@
1
+ import Foundation
2
+ import TerraSDK
3
+
4
+ extension Conversions {
5
+
6
+ static func dayTotalMetric(_ metric: TerraSDK.DayTotal.Metric) -> DayTotalMetric? {
7
+ switch metric {
8
+ case .distanceWalkingRunning: return .distanceWalkingRunning
9
+ case .distanceCycling: return .distanceCycling
10
+ case .distanceSwimming: return .distanceSwimming
11
+ case .distanceWheelchair: return .distanceWheelchair
12
+ case .activeEnergyBurned: return .activeEnergyBurned
13
+ case .basalEnergyBurned: return .basalEnergyBurned
14
+ case .flightsClimbed: return .flightsClimbed
15
+ case .appleExerciseTime: return .appleExerciseTime
16
+ case .steps, .swimmingStrokeCount, .appleMoveTime, .appleStandTime, .appleStandHours: return nil
17
+ @unknown default: return nil
18
+ }
19
+ }
20
+
21
+ static func dayTotal(_ total: TerraSDK.DayTotal) -> DayTotal? {
22
+ dayTotalMetric(total.metric).map {
23
+ DayTotal(
24
+ metric: $0,
25
+ start: BridgeInstant.format(total.interval.start),
26
+ end: BridgeInstant.format(total.interval.end),
27
+ value: total.value,
28
+ unit: total.unit,
29
+ timeZone: total.timeZone?.identifier)
30
+ }
31
+ }
32
+
33
+ static func intradayMetric(_ kind: TerraSDK.IntradaySample.Kind) -> IntradayMetric? {
34
+ switch kind {
35
+ case .activeEnergy: return .activeEnergy
36
+ case .basalEnergy: return .basalEnergy
37
+ case .distance: return .distance
38
+ case .floorsClimbed: return .floorsClimbed
39
+ @unknown default: return nil
40
+ }
41
+ }
42
+
43
+ static func intraday(_ sample: TerraSDK.IntradaySample) -> IntradaySample? {
44
+ intradayMetric(sample.kind).map {
45
+ IntradaySample(
46
+ id: sample.id.uuidString,
47
+ metric: $0,
48
+ start: BridgeInstant.format(sample.interval.start),
49
+ end: BridgeInstant.format(sample.interval.end),
50
+ value: sample.value,
51
+ source: sampleSource(sample.source),
52
+ userEntered: sample.userEntered,
53
+ timeZone: sample.timeZone?.identifier)
54
+ }
55
+ }
56
+
57
+ static func restingHeartRate(_ sample: TerraSDK.RestingHeartRateSample) -> RestingHeartRateSample {
58
+ RestingHeartRateSample(
59
+ id: sample.id.uuidString,
60
+ start: BridgeInstant.format(sample.interval.start),
61
+ end: BridgeInstant.format(sample.interval.end),
62
+ beatsPerMinute: sample.beatsPerMinute,
63
+ source: sampleSource(sample.source),
64
+ userEntered: sample.userEntered,
65
+ timeZone: sample.timeZone?.identifier)
66
+ }
67
+
68
+ static func bodyMetric(_ metric: TerraSDK.BodyMetric) -> BodyMetric? {
69
+ switch metric {
70
+ case .weight: return .weight
71
+ case .height: return .height
72
+ case .bodyFatPercentage: return .bodyFatPercentage
73
+ case .leanBodyMass: return .leanBodyMass
74
+ case .bmi: return .bmi
75
+ case .waistCircumference: return nil
76
+ @unknown default: return nil
77
+ }
78
+ }
79
+
80
+ static func glucoseUnit(_ unit: TerraSDK.BloodGlucoseUnit) -> BloodGlucoseUnit {
81
+ switch unit {
82
+ case .milligramsPerDecilitre: return .milligramsPerDecilitre
83
+ case .millimolesPerLitre: return .millimolesPerLitre
84
+ @unknown default: return .milligramsPerDecilitre
85
+ }
86
+ }
87
+
88
+ static func mealTime(_ time: TerraSDK.GlucoseMealTime) -> GlucoseMealTime {
89
+ switch time {
90
+ case .preprandial: return .preprandial
91
+ case .postprandial: return .postprandial
92
+ case .unknown: return .unknown
93
+ @unknown default: return .unknown
94
+ }
95
+ }
96
+
97
+ static func specimen(_ specimen: TerraSDK.GlucoseSpecimen) -> GlucoseSpecimen {
98
+ switch specimen {
99
+ case .capillary: return .capillary
100
+ case .interstitial: return .interstitial
101
+ case .plasma: return .plasma
102
+ case .serum: return .serum
103
+ case .tears: return .tears
104
+ case .wholeBlood: return .wholeBlood
105
+ case .unknown: return .unknown
106
+ @unknown default: return .unknown
107
+ }
108
+ }
109
+
110
+ static func temperatureMetric(_ metric: TerraSDK.BodyTemperatureMetric) -> BodyTemperatureMetric? {
111
+ switch metric {
112
+ case .body: return .body
113
+ case .basal: return .basal
114
+ case .wrist: return .wrist
115
+ case .skin: return .skin
116
+ @unknown default: return nil
117
+ }
118
+ }
119
+
120
+ static func variabilityMetric(_ metric: TerraSDK.HeartRateVariabilityMetric) -> HeartRateVariabilityMetric? {
121
+ switch metric {
122
+ case .sdnn: return .sdnn
123
+ case .rmssd: return .rmssd
124
+ @unknown default: return nil
125
+ }
126
+ }
127
+
128
+ static func testType(_ type: TerraSDK.VO2MaxTestType) -> VO2MaxTestType? {
129
+ switch type {
130
+ case .maxExercise: return .maxExercise
131
+ case .predictedSubMaxExercise: return .predictedSubMaxExercise
132
+ case .predictedNonExercise: return .predictedNonExercise
133
+ @unknown default: return nil
134
+ }
135
+ }
136
+
137
+ static func bodyReadingKind(_ kind: TerraSDK.BodyReading.Kind) -> BodyReadingKind? {
138
+ switch kind {
139
+ case .measurement(let metric, let value):
140
+ return bodyMetric(metric).map { .measurement(metric: $0, value: value) }
141
+ case .bloodPressure(let systolic, let diastolic):
142
+ return .bloodPressure(systolic: systolic, diastolic: diastolic)
143
+ case .bloodGlucose(let value, let unit, let mealTime, let specimen):
144
+ return .bloodGlucose(
145
+ value: value, unit: glucoseUnit(unit), mealTime: mealTime.map(Self.mealTime),
146
+ specimen: specimen.map(Self.specimen))
147
+ case .oxygenSaturation(let fraction):
148
+ return .oxygenSaturation(fraction: fraction)
149
+ case .temperature(let metric, let celsius):
150
+ return temperatureMetric(metric).map { .temperature(metric: $0, celsius: celsius) }
151
+ case .heartRateVariability(let metric, let milliseconds):
152
+ return variabilityMetric(metric).map { .heartRateVariability(metric: $0, milliseconds: milliseconds) }
153
+ case .respiratoryRate(let breathsPerMinute):
154
+ return .respiratoryRate(breathsPerMinute: breathsPerMinute)
155
+ case .vo2Max(let value, let type):
156
+ return .vo2Max(millilitresPerKilogramMinute: value, testType: type.flatMap(testType))
157
+ case .insulinDelivery, .electrocardiogram, .heartbeatSeries:
158
+ return nil
159
+ @unknown default:
160
+ return nil
161
+ }
162
+ }
163
+
164
+ static func bodyReading(_ reading: TerraSDK.BodyReading) -> BodyReading? {
165
+ bodyReadingKind(reading.kind).map {
166
+ BodyReading(
167
+ id: reading.id.uuidString,
168
+ kind: $0,
169
+ start: BridgeInstant.format(reading.interval.start),
170
+ end: BridgeInstant.format(reading.interval.end),
171
+ source: sampleSource(reading.source),
172
+ userEntered: reading.userEntered,
173
+ timeZone: reading.timeZone?.identifier)
174
+ }
175
+ }
176
+
177
+ static func route(_ route: TerraSDK.WorkoutRoute) -> WorkoutRoute {
178
+ WorkoutRoute(
179
+ id: route.id.uuidString,
180
+ workoutId: route.workoutId.uuidString,
181
+ start: BridgeInstant.format(route.interval.start),
182
+ end: BridgeInstant.format(route.interval.end),
183
+ points: route.points.map {
184
+ RoutePoint(time: BridgeInstant.format($0.time), latitude: $0.latitude, longitude: $0.longitude)
185
+ },
186
+ source: sampleSource(route.source),
187
+ timeZone: route.timeZone?.identifier)
188
+ }
189
+
190
+ static func intensityMetric(_ kind: TerraSDK.WorkoutIntensitySample.Kind) -> WorkoutIntensityMetric? {
191
+ switch kind {
192
+ case .speed: return .speed
193
+ case .power: return .power
194
+ case .cadence: return .cadence
195
+ @unknown default: return nil
196
+ }
197
+ }
198
+
199
+ static func intensity(_ sample: TerraSDK.WorkoutIntensitySample) -> WorkoutIntensitySample? {
200
+ intensityMetric(sample.kind).map {
201
+ WorkoutIntensitySample(
202
+ id: sample.id.uuidString,
203
+ metric: $0,
204
+ workoutId: sample.workoutId.uuidString,
205
+ start: BridgeInstant.format(sample.interval.start),
206
+ end: BridgeInstant.format(sample.interval.end),
207
+ value: sample.value,
208
+ source: sampleSource(sample.source),
209
+ userEntered: sample.userEntered,
210
+ timeZone: sample.timeZone?.identifier)
211
+ }
212
+ }
213
+
214
+ static func hydration(_ sample: TerraSDK.HydrationSample) -> HydrationSample {
215
+ HydrationSample(
216
+ id: sample.id.uuidString,
217
+ start: BridgeInstant.format(sample.interval.start),
218
+ end: BridgeInstant.format(sample.interval.end),
219
+ millilitres: sample.millilitres,
220
+ source: sampleSource(sample.source),
221
+ userEntered: sample.userEntered,
222
+ timeZone: sample.timeZone?.identifier)
223
+ }
224
+
225
+ static func availability(_ availability: TerraSDK.StoreAvailability) -> StoreAvailability {
226
+ switch availability {
227
+ case .available: return .available
228
+ case .unsupportedDevice: return .unsupportedDevice
229
+ @unknown default: return .unsupportedDevice
230
+ }
231
+ }
232
+
233
+ static func eventKind(_ kind: TerraSDK.MenstrualEvent.Kind) -> MenstrualEventKind? {
234
+ switch kind {
235
+ case .intermenstrualBleeding: return .intermenstrualBleeding
236
+ case .ovulationTest: return .ovulationTest
237
+ case .cervicalMucus: return .cervicalMucus
238
+ case .sexualActivity, .pregnancyTest, .progesteroneTest, .pregnancy, .lactation, .contraceptive,
239
+ .persistentIntermenstrualBleeding, .infrequentCycles, .irregularCycles, .prolongedPeriods,
240
+ .bleedingDuringPregnancy, .bleedingAfterPregnancy:
241
+ return nil
242
+ @unknown default: return nil
243
+ }
244
+ }
245
+
246
+ static func menstrualEvent(_ event: TerraSDK.MenstrualEvent) -> MenstrualEvent? {
247
+ eventKind(event.kind).map {
248
+ MenstrualEvent(
249
+ id: event.id.uuidString,
250
+ kind: $0,
251
+ time: BridgeInstant.format(event.instant),
252
+ value: event.value,
253
+ storeIdentifier: event.storeIdentifier,
254
+ source: sampleSource(event.source),
255
+ userEntered: event.userEntered,
256
+ timeZone: event.timeZone?.identifier)
257
+ }
258
+ }
259
+ }