Deniz Cengiz пре 1 година
родитељ
комит
db22921030
56 измењених фајлова са 380 додато и 135 уклоњено
  1. 4 0
      FreeAPS.xcodeproj/project.pbxproj
  2. 17 0
      FreeAPS.xcodeproj/project.pbxproj.rej
  3. 1 1
      FreeAPS/Resources/json/defaults/freeaps/freeaps_settings.json
  4. 4 4
      FreeAPS/Resources/json/defaults/settings/bg_targets.json
  5. 3 3
      FreeAPS/Resources/json/defaults/settings/insulin_sensitivities.json
  6. 1 1
      FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift
  7. 4 2
      FreeAPS/Sources/APS/Storage/DeterminationStorage.swift
  8. 9 0
      FreeAPS/Sources/Helpers/Rounding.swift
  9. 5 5
      FreeAPS/Sources/Models/BloodGlucose.swift
  10. 6 0
      FreeAPS/Sources/Modules/AlgorithmAdvancedSettings/AlgorithmAdvancedSettingsStateModel.swift
  11. 11 0
      FreeAPS/Sources/Modules/AlgorithmAdvancedSettings/View/AlgorithmAdvancedSettingsRootView.swift
  12. 6 0
      FreeAPS/Sources/Modules/AutosensSettings/AutosensSettingsStateModel.swift
  13. 3 0
      FreeAPS/Sources/Modules/AutosensSettings/View/AutosensSettingsRootView.swift
  14. 6 0
      FreeAPS/Sources/Modules/AutotuneConfig/AutotuneConfigStateModel.swift
  15. 2 0
      FreeAPS/Sources/Modules/AutotuneConfig/View/AutotuneConfigRootView.swift
  16. 9 0
      FreeAPS/Sources/Modules/BolusCalculatorConfig/BolusCalculatorStateModel.swift
  17. 4 0
      FreeAPS/Sources/Modules/BolusCalculatorConfig/View/BolusCalculatorConfigRootView.swift
  18. 9 1
      FreeAPS/Sources/Modules/CGM/CGMStateModel.swift
  19. 1 0
      FreeAPS/Sources/Modules/CGM/View/CGMRootView.swift
  20. 6 0
      FreeAPS/Sources/Modules/CalendarEventSettings/CalendarEventSettingsStateModel.swift
  21. 3 0
      FreeAPS/Sources/Modules/CalendarEventSettings/View/CalendarEventSettingsRootView.swift
  22. 10 4
      FreeAPS/Sources/Modules/DynamicSettings/DynamicSettingsStateModel.swift
  23. 9 1
      FreeAPS/Sources/Modules/DynamicSettings/View/DynamicSettingsRootView.swift
  24. 1 71
      FreeAPS/Sources/Modules/GeneralSettings/UnitsLimitsSettingsProvider.swift
  25. 6 2
      FreeAPS/Sources/Modules/GeneralSettings/UnitsLimitsSettingsStateModel.swift
  26. 2 0
      FreeAPS/Sources/Modules/GeneralSettings/View/UnitsLimitsSettingsRootView.swift
  27. 6 0
      FreeAPS/Sources/Modules/GlucoseNotificationSettings/GlucoseNotificationSettingsStateModel.swift
  28. 4 0
      FreeAPS/Sources/Modules/GlucoseNotificationSettings/View/GlucoseNotificationSettingsRootView.swift
  29. 9 0
      FreeAPS/Sources/Modules/HealthKit/HealthKitStateModel.swift
  30. 1 0
      FreeAPS/Sources/Modules/HealthKit/View/AppleHealthKitRootView.swift
  31. 10 1
      FreeAPS/Sources/Modules/ISFEditor/ISFEditorStateModel.swift
  32. 9 0
      FreeAPS/Sources/Modules/LiveActivitySettings/LiveActivitySettingsStateModel.swift
  33. 1 0
      FreeAPS/Sources/Modules/LiveActivitySettings/View/LiveActivitySettingsRootView.swift
  34. 9 0
      FreeAPS/Sources/Modules/MealSettings/MealSettingsStateModel.swift
  35. 5 0
      FreeAPS/Sources/Modules/MealSettings/View/MealSettingsRootView.swift
  36. 6 0
      FreeAPS/Sources/Modules/NightscoutConfig/NightscoutConfigStateModel.swift
  37. 2 0
      FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutFetchView.swift
  38. 2 0
      FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutUploadView.swift
  39. 10 0
      FreeAPS/Sources/Modules/PumpSettingsEditor/PumpSettingsEditorStateModel.swift
  40. 1 0
      FreeAPS/Sources/Modules/PumpSettingsEditor/View/PumpSettingsEditorRootView.swift
  41. 7 1
      FreeAPS/Sources/Modules/SMBSettings/SMBSettingsStateModel.swift
  42. 12 0
      FreeAPS/Sources/Modules/SMBSettings/View/SMBSettingsRootView.swift
  43. 3 0
      FreeAPS/Sources/Modules/Settings/SettingsStateModel.swift
  44. 1 0
      FreeAPS/Sources/Modules/Settings/View/SettingsRootView.swift
  45. 9 0
      FreeAPS/Sources/Modules/ShortcutsConfig/ShortcutsConfigStateModel.swift
  46. 1 0
      FreeAPS/Sources/Modules/ShortcutsConfig/View/ShortcutsConfigView.swift
  47. 7 1
      FreeAPS/Sources/Modules/TargetBehavoir/TargetBehavoirStateModel.swift
  48. 5 0
      FreeAPS/Sources/Modules/TargetBehavoir/View/TargetBehavoirRootView.swift
  49. 1 1
      FreeAPS/Sources/Modules/TargetsEditor/TargetsEditorProvider.swift
  50. 3 1
      FreeAPS/Sources/Modules/TargetsEditor/TargetsEditorStateModel.swift
  51. 9 14
      FreeAPS/Sources/Modules/UserInterfaceSettings/UserInterfaceSettingsStateModel.swift
  52. 11 4
      FreeAPS/Sources/Modules/UserInterfaceSettings/View/UserInterfaceSettingsRootView.swift
  53. 2 0
      FreeAPS/Sources/Modules/WatchConfig/View/WatchConfigAppleWatchView.swift
  54. 10 0
      FreeAPS/Sources/Modules/WatchConfig/WatchConfigStateModel.swift
  55. 26 15
      FreeAPS/Sources/Views/SettingInputSection.swift
  56. 56 2
      Trio.xcworkspace/xcshareddata/swiftpm/Package.resolved

+ 4 - 0
FreeAPS.xcodeproj/project.pbxproj

@@ -420,6 +420,7 @@
 		DD1745552C55CA6C00211FAC /* UnitsLimitsSettingsRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1745542C55CA6C00211FAC /* UnitsLimitsSettingsRootView.swift */; };
 		DD1DB7CC2BECCA1F0048B367 /* BuildDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1DB7CB2BECCA1F0048B367 /* BuildDetails.swift */; };
 		DD21FCB52C6952AD00AF2C25 /* DecimalPickerSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD21FCB42C6952AD00AF2C25 /* DecimalPickerSettings.swift */; };
+		DD21FCB72C6AA2F000AF2C25 /* Rounding.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD21FCB62C6AA2F000AF2C25 /* Rounding.swift */; };
 		DD399FB31EACB9343C944C4C /* PreferencesEditorStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA3E609094E064C99A4752C /* PreferencesEditorStateModel.swift */; };
 		DD57C4B22C4C7103001A5B28 /* LoopStatRecord+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4902C4C7103001A5B28 /* LoopStatRecord+CoreDataClass.swift */; };
 		DD57C4B32C4C7103001A5B28 /* LoopStatRecord+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57C4912C4C7103001A5B28 /* LoopStatRecord+CoreDataProperties.swift */; };
@@ -1062,6 +1063,7 @@
 		DD1745542C55CA6C00211FAC /* UnitsLimitsSettingsRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitsLimitsSettingsRootView.swift; sourceTree = "<group>"; };
 		DD1DB7CB2BECCA1F0048B367 /* BuildDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildDetails.swift; sourceTree = "<group>"; };
 		DD21FCB42C6952AD00AF2C25 /* DecimalPickerSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecimalPickerSettings.swift; sourceTree = "<group>"; };
+		DD21FCB62C6AA2F000AF2C25 /* Rounding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Rounding.swift; sourceTree = "<group>"; };
 		DD57C4902C4C7103001A5B28 /* LoopStatRecord+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LoopStatRecord+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
 		DD57C4912C4C7103001A5B28 /* LoopStatRecord+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LoopStatRecord+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		DD57C4922C4C7103001A5B28 /* MealPresetStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MealPresetStored+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
@@ -1923,6 +1925,7 @@
 				BD1661302B82ADAB00256551 /* CustomProgressView.swift */,
 				581516A32BCED84A00BF67D7 /* DebuggingIdentifiers.swift */,
 				DD1DB7CB2BECCA1F0048B367 /* BuildDetails.swift */,
+				DD21FCB62C6AA2F000AF2C25 /* Rounding.swift */,
 			);
 			path = Helpers;
 			sourceTree = "<group>";
@@ -3386,6 +3389,7 @@
 				BD4064D12C4ED26900582F43 /* CoreDataObserver.swift in Sources */,
 				448B6FCB252BD4796E2960C0 /* PumpSettingsEditorDataFlow.swift in Sources */,
 				38E44536274E411700EC9A94 /* Disk.swift in Sources */,
+				DD21FCB72C6AA2F000AF2C25 /* Rounding.swift in Sources */,
 				2BE9A6FA20875F6F4F9CD461 /* PumpSettingsEditorProvider.swift in Sources */,
 				6B9625766B697D1C98E455A2 /* PumpSettingsEditorStateModel.swift in Sources */,
 				19A910362A24D6D700C8951B /* DateFilter.swift in Sources */,

+ 17 - 0
FreeAPS.xcodeproj/project.pbxproj.rej

@@ -0,0 +1,17 @@
+diff a/FreeAPS.xcodeproj/project.pbxproj b/FreeAPS.xcodeproj/project.pbxproj	(rejected hunks)
+@@ -320,6 +320,7 @@
+ 		BDF530D82B40F8AC002CAF43 /* LockScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDF530D72B40F8AC002CAF43 /* LockScreenView.swift */; };
+ 		BDFD165A2AE40438007F0DDA /* BolusRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDFD16592AE40438007F0DDA /* BolusRootView.swift */; };
+ 		BF1667ADE69E4B5B111CECAE /* ManualTempBasalProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 680C4420C9A345D46D90D06C /* ManualTempBasalProvider.swift */; };
++		C20BC6CC2C66B348002BC1C6 /* Rounding.swift in Sources */ = {isa = PBXBuildFile; fileRef = C20BC6CB2C66B348002BC1C6 /* Rounding.swift */; };
+ 		C967DACD3B1E638F8B43BE06 /* ManualTempBasalStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFCFE0781F9074C2917890E8 /* ManualTempBasalStateModel.swift */; };
+ 		CA370FC152BC98B3D1832968 /* BasalProfileEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8BCB0C37DEB5EC377B9612 /* BasalProfileEditorRootView.swift */; };
+ 		CC41E29A2B1E1F460070974F /* HistoryLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC41E2992B1E1F460070974F /* HistoryLayout.swift */; };
+@@ -921,6 +922,7 @@
+ 		BDFD16592AE40438007F0DDA /* BolusRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BolusRootView.swift; sourceTree = "<group>"; };
+ 		BF8BCB0C37DEB5EC377B9612 /* BasalProfileEditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = BasalProfileEditorRootView.swift; sourceTree = "<group>"; };
+ 		C19984D62EFC0035A9E9644D /* BolusProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = BolusProvider.swift; sourceTree = "<group>"; };
++		C20BC6CB2C66B348002BC1C6 /* Rounding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Rounding.swift; sourceTree = "<group>"; };
+ 		C377490C77661D75E8C50649 /* ManualTempBasalRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ManualTempBasalRootView.swift; sourceTree = "<group>"; };
+ 		C8D1A7CA8C10C4403D4BBFA7 /* BolusDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = BolusDataFlow.swift; sourceTree = "<group>"; };
+ 		CC41E2992B1E1F460070974F /* HistoryLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryLayout.swift; sourceTree = "<group>"; };

+ 1 - 1
FreeAPS/Resources/json/defaults/freeaps/freeaps_settings.json

@@ -1,5 +1,5 @@
 {
-  "units" : "mmol/L",
+  "units" : "mg/dL",
   "closedLoop" : false,
   "allowAnnouncements" : false,
   "useAutotune" : false,

+ 4 - 4
FreeAPS/Resources/json/defaults/settings/bg_targets.json

@@ -1,10 +1,10 @@
 {
-    "units": "mmol/L",
-    "user_preferred_units": "mmol/L",
+    "units": "mg/dL",
+    "user_preferred_units": "mg/dL",
     "targets": [
         {
-            "low": 5.5,
-            "high": 5.5,
+            "low": 100,
+            "high": 100,
             "start": "00:00:00",
             "offset": 0
         }

+ 3 - 3
FreeAPS/Resources/json/defaults/settings/insulin_sensitivities.json

@@ -1,9 +1,9 @@
 {
-    "units": "mmol/L",
-    "user_preferred_units": "mmol/L",
+    "units": "mg/dL",
+    "user_preferred_units": "mg/dL",
     "sensitivities": [
         {
-            "sensitivity": 3.0,
+            "sensitivity": 54,
             "offset": 0,
             "start": "00:00:00"
         }

+ 1 - 1
FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift

@@ -208,7 +208,7 @@ final class OpenAPS {
         debug(.openAPS, "Start determineBasal")
 
         // clock
-        self.storage.save(clock, as: Monitor.clock)
+        storage.save(clock, as: Monitor.clock)
 
         // temp_basal
         let tempBasal = currentTemp.rawJSON

+ 4 - 2
FreeAPS/Sources/APS/Storage/DeterminationStorage.swift

@@ -152,8 +152,10 @@ final class BaseDeterminationStorage: DeterminationStorage, Injectable {
                     )
                 }
             } catch {
-                debugPrint("\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to fetch managed object with error: \(error.localizedDescription)")
-             }
+                debugPrint(
+                    "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to fetch managed object with error: \(error.localizedDescription)"
+                )
+            }
 
             return result
         }

+ 9 - 0
FreeAPS/Sources/Helpers/Rounding.swift

@@ -0,0 +1,9 @@
+
+import Foundation
+
+func rounded(_ value: Decimal, scale: Int, roundingMode: NSDecimalNumber.RoundingMode) -> Decimal {
+    var result = Decimal()
+    var toRound = value
+    NSDecimalRound(&result, &toRound, scale, roundingMode)
+    return result
+}

+ 5 - 5
FreeAPS/Sources/Models/BloodGlucose.swift

@@ -107,27 +107,27 @@ enum GlucoseUnits: String, JSON, Equatable {
 
 extension Int {
     var asMmolL: Decimal {
-        Decimal(self) * GlucoseUnits.exchangeRate
+        FreeAPS.rounded(Decimal(self) * GlucoseUnits.exchangeRate, scale: 1, roundingMode: .plain)
     }
 }
 
 extension Decimal {
     var asMmolL: Decimal {
-        self * GlucoseUnits.exchangeRate
+        FreeAPS.rounded(self * GlucoseUnits.exchangeRate, scale: 1, roundingMode: .plain)
     }
 
     var asMgdL: Decimal {
-        self / GlucoseUnits.exchangeRate
+        FreeAPS.rounded(self / GlucoseUnits.exchangeRate, scale: 0, roundingMode: .plain)
     }
 }
 
 extension Double {
     var asMmolL: Decimal {
-        Decimal(self) * GlucoseUnits.exchangeRate
+        FreeAPS.rounded(Decimal(self) * GlucoseUnits.exchangeRate, scale: 1, roundingMode: .plain)
     }
 
     var asMgdL: Decimal {
-        Decimal(self) / GlucoseUnits.exchangeRate
+        FreeAPS.rounded(Decimal(self) / GlucoseUnits.exchangeRate, scale: 0, roundingMode: .plain)
     }
 }
 

+ 6 - 0
FreeAPS/Sources/Modules/AlgorithmAdvancedSettings/AlgorithmAdvancedSettingsStateModel.swift

@@ -79,3 +79,9 @@ extension AlgorithmAdvancedSettings {
         }
     }
 }
+
+extension AlgorithmAdvancedSettings.StateModel: SettingsObserver {
+    func settingsDidChange(_: FreeAPSSettings) {
+        units = settingsManager.settings.units
+    }
+}

+ 11 - 0
FreeAPS/Sources/Modules/AlgorithmAdvancedSettings/View/AlgorithmAdvancedSettingsRootView.swift

@@ -57,6 +57,7 @@ extension AlgorithmAdvancedSettings {
                             hintLabel = NSLocalizedString("Max Daily Safety Multiplier", comment: "Max Daily Safety Multiplier")
                         }
                     ),
+                    units: state.units,
                     type: .decimal("maxDailySafetyMultiplier"),
                     label: NSLocalizedString("Max Daily Safety Multiplier", comment: "Max Daily Safety Multiplier"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -80,6 +81,7 @@ extension AlgorithmAdvancedSettings {
                             )
                         }
                     ),
+                    units: state.units,
                     type: .decimal("currentBasalSafetyMultiplier"),
                     label: NSLocalizedString("Current Basal Safety Multiplier", comment: "Current Basal Safety Multiplier"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -100,6 +102,7 @@ extension AlgorithmAdvancedSettings {
                             hintLabel = NSLocalizedString("Use Custom Peak Time", comment: "Use Custom Peak Time")
                         }
                     ),
+                    units: state.units,
                     type: .conditionalDecimal("insulinPeakTime"),
                     label: NSLocalizedString("Use Custom Peak Time", comment: "Use Custom Peak Time"),
                     conditionalLabel: NSLocalizedString("Insulin Peak Time", comment: "Insulin Peak Time"),
@@ -123,6 +126,7 @@ extension AlgorithmAdvancedSettings {
                             hintLabel = NSLocalizedString("Skip Neutral Temps", comment: "Skip Neutral Temps")
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: NSLocalizedString("Skip Neutral Temps", comment: "Skip Neutral Temps"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -143,6 +147,7 @@ extension AlgorithmAdvancedSettings {
                             hintLabel = NSLocalizedString("Unsuspend If No Temp", comment: "Unsuspend If No Temp")
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: NSLocalizedString("Unsuspend If No Temp", comment: "Unsuspend If No Temp"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -163,6 +168,7 @@ extension AlgorithmAdvancedSettings {
                             hintLabel = NSLocalizedString("Suspend Zeros IOB", comment: "Suspend Zeros IOB")
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: NSLocalizedString("Suspend Zeros IOB", comment: "Suspend Zeros IOB"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -186,6 +192,7 @@ extension AlgorithmAdvancedSettings {
                             )
                         }
                     ),
+                    units: state.units,
                     type: .decimal("autotuneISFAdjustmentFraction"),
                     label: NSLocalizedString("Autotune ISF Adjustment Fraction", comment: "Autotune ISF Adjustment Fraction"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -206,6 +213,7 @@ extension AlgorithmAdvancedSettings {
                             hintLabel = NSLocalizedString("Min 5m Carbimpact", comment: "Min 5m Carbimpact")
                         }
                     ),
+                    units: state.units,
                     type: .decimal("min5mCarbimpact"),
                     label: NSLocalizedString("Min 5m Carbimpact", comment: "Min 5m Carbimpact"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -226,6 +234,7 @@ extension AlgorithmAdvancedSettings {
                             hintLabel = NSLocalizedString("Remaining Carbs Fraction", comment: "Remaining Carbs Fraction")
                         }
                     ),
+                    units: state.units,
                     type: .decimal("remainingCarbsFraction"),
                     label: NSLocalizedString("Remaining Carbs Fraction", comment: "Remaining Carbs Fraction"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -246,6 +255,7 @@ extension AlgorithmAdvancedSettings {
                             hintLabel = NSLocalizedString("Remaining Carbs Cap", comment: "Remaining Carbs Cap")
                         }
                     ),
+                    units: state.units,
                     type: .decimal("remainingCarbsCap"),
                     label: NSLocalizedString("Remaining Carbs Cap", comment: "Remaining Carbs Cap"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -266,6 +276,7 @@ extension AlgorithmAdvancedSettings {
                             hintLabel = NSLocalizedString("Noisy CGM Target Multiplier", comment: "Noisy CGM Target Multiplier")
                         }
                     ),
+                    units: state.units,
                     type: .decimal("noisyCGMTargetMultiplier"),
                     label: NSLocalizedString("Noisy CGM Target Multiplier", comment: "Noisy CGM Target Multiplier"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",

+ 6 - 0
FreeAPS/Sources/Modules/AutosensSettings/AutosensSettingsStateModel.swift

@@ -43,3 +43,9 @@ extension AutosensSettings {
         }
     }
 }
+
+extension AutosensSettings.StateModel: SettingsObserver {
+    func settingsDidChange(_: FreeAPSSettings) {
+        units = settingsManager.settings.units
+    }
+}

+ 3 - 0
FreeAPS/Sources/Modules/AutosensSettings/View/AutosensSettingsRootView.swift

@@ -45,6 +45,7 @@ extension AutosensSettings {
                             hintLabel = NSLocalizedString("Autosens Max", comment: "Autosens Max")
                         }
                     ),
+                    units: state.units,
                     type: .decimal("autosensMax"),
                     label: NSLocalizedString("Autosens Max", comment: "Autosens Max"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -66,6 +67,7 @@ extension AutosensSettings {
                             hintLabel = NSLocalizedString("Autosens Min", comment: "Autosens Min")
                         }
                     ),
+                    units: state.units,
                     type: .decimal("autosensMin"),
                     label: NSLocalizedString("Autosens Min", comment: "Autosens Min"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -86,6 +88,7 @@ extension AutosensSettings {
                             hintLabel = NSLocalizedString("Rewind Resets Autosens", comment: "Rewind Resets Autosens")
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: NSLocalizedString("Rewind Resets Autosens", comment: "Rewind Resets Autosens"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",

+ 6 - 0
FreeAPS/Sources/Modules/AutotuneConfig/AutotuneConfigStateModel.swift

@@ -105,3 +105,9 @@ extension AutotuneConfig {
         }
     }
 }
+
+extension AutotuneConfig.StateModel: SettingsObserver {
+    func settingsDidChange(_: FreeAPSSettings) {
+        units = settingsManager.settings.units
+    }
+}

+ 2 - 0
FreeAPS/Sources/Modules/AutotuneConfig/View/AutotuneConfigRootView.swift

@@ -67,6 +67,7 @@ extension AutotuneConfig {
                             hintLabel = "Use Autotune"
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: "Use Autotune",
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -86,6 +87,7 @@ extension AutotuneConfig {
                                 hintLabel = "Only Autotune Basal Insulin"
                             }
                         ),
+                        units: state.units,
                         type: .boolean,
                         label: "Only Autotune Basal Insulin",
                         miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",

+ 9 - 0
FreeAPS/Sources/Modules/BolusCalculatorConfig/BolusCalculatorStateModel.swift

@@ -2,6 +2,7 @@ import SwiftUI
 
 extension BolusCalculatorConfig {
     final class StateModel: BaseStateModel<Provider> {
+        @Published var units: GlucoseUnits = .mgdL
         @Published var overrideFactor: Decimal = 0
         @Published var fattyMeals: Bool = false
         @Published var fattyMealFactor: Decimal = 0
@@ -10,6 +11,8 @@ extension BolusCalculatorConfig {
         @Published var displayPresets: Bool = true
 
         override func subscribe() {
+            units = settingsManager.settings.units
+
             subscribeSetting(\.overrideFactor, on: $overrideFactor, initial: {
                 let value = max(min($0, 1.2), 0.1)
                 overrideFactor = value
@@ -34,3 +37,9 @@ extension BolusCalculatorConfig {
         }
     }
 }
+
+extension BolusCalculatorConfig.StateModel: SettingsObserver {
+    func settingsDidChange(_: FreeAPSSettings) {
+        units = settingsManager.settings.units
+    }
+}

+ 4 - 0
FreeAPS/Sources/Modules/BolusCalculatorConfig/View/BolusCalculatorConfigRootView.swift

@@ -59,6 +59,7 @@ extension BolusCalculatorConfig {
                             hintLabel = "Display Meal Presets"
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: "Display Meal Presets",
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -76,6 +77,7 @@ extension BolusCalculatorConfig {
                             hintLabel = "Recommended Bolus Percentage"
                         }
                     ),
+                    units: state.units,
                     type: .decimal("overrideFactor"),
                     label: "Recommended Bolus Percentage",
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -94,6 +96,7 @@ extension BolusCalculatorConfig {
                             hintLabel = "Fatty Meal Factor"
                         }
                     ),
+                    units: state.units,
                     type: .conditionalDecimal("fattyMealFactor"),
                     label: "Enable Fatty Meal Factor",
                     conditionalLabel: "Fatty Meal Factor",
@@ -112,6 +115,7 @@ extension BolusCalculatorConfig {
                             hintLabel = "Super Bolus & Sweet Meal Factor"
                         }
                     ),
+                    units: state.units,
                     type: .conditionalDecimal("sweetMealFactor"),
                     label: "Enable Super Bolus",
                     conditionalLabel: "Super Bolus Factor",

+ 9 - 1
FreeAPS/Sources/Modules/CGM/CGMStateModel.swift

@@ -25,15 +25,17 @@ extension CGM {
         @Injected() private var broadcaster: Broadcaster!
         @Injected() var nightscoutManager: NightscoutManager!
 
+        @Published var units: GlucoseUnits = .mgdL
         @Published var setupCGM: Bool = false
         @Published var cgmCurrent = cgmDefaultName
         @Published var smoothGlucose = false
-
         @Published var cgmTransmitterDeviceAddress: String? = nil
         @Published var listOfCGM: [cgmName] = []
         @Published var url: URL?
 
         override func subscribe() {
+            units = settingsManager.settings.units
+
             // collect the list of CGM available with plugins and CGMType defined manually
             listOfCGM = (
                 CGMType.allCases.filter { $0 != CGMType.plugin }.map {
@@ -178,3 +180,9 @@ extension CGM.StateModel: CGMManagerOnboardingDelegate {
         // nothing to do ?
     }
 }
+
+extension CGM.StateModel {
+    func settingsDidChange(_: FreeAPSSettings) {
+        units = settingsManager.settings.units
+    }
+}

+ 1 - 0
FreeAPS/Sources/Modules/CGM/View/CGMRootView.swift

@@ -203,6 +203,7 @@ extension CGM {
                                 hintLabel = "Smooth Glucose Value"
                             }
                         ),
+                        units: state.units,
                         type: .boolean,
                         label: "Smooth Glucose Value",
                         miniHint: "Smooth CGM readings using Savitzky–Golay filtering.",

+ 6 - 0
FreeAPS/Sources/Modules/CalendarEventSettings/CalendarEventSettingsStateModel.swift

@@ -57,3 +57,9 @@ extension CalendarEventSettings {
         }
     }
 }
+
+extension CalendarEventSettings.StateModel: SettingsObserver {
+    func settingsDidChange(_: FreeAPSSettings) {
+        units = settingsManager.settings.units
+    }
+}

+ 3 - 0
FreeAPS/Sources/Modules/CalendarEventSettings/View/CalendarEventSettingsRootView.swift

@@ -45,6 +45,7 @@ extension CalendarEventSettings {
                             hintLabel = "Create Events in Calendar"
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: "Create Events in Calendar",
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -74,6 +75,7 @@ extension CalendarEventSettings {
                                 hintLabel = "Display Emojis as Labels"
                             }
                         ),
+                        units: state.units,
                         type: .boolean,
                         label: "Display Emojis as Labels",
                         miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -91,6 +93,7 @@ extension CalendarEventSettings {
                                 hintLabel = "Display IOB and COB"
                             }
                         ),
+                        units: state.units,
                         type: .boolean,
                         label: "Display IOB and COB",
                         miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",

+ 10 - 4
FreeAPS/Sources/Modules/DynamicSettings/DynamicSettingsStateModel.swift

@@ -13,14 +13,14 @@ extension DynamicSettings {
         @Published var weightPercentage: Decimal = 0.65
         @Published var tddAdjBasal: Bool = false
         @Published var threshold_setting: Decimal = 65
-        @Published var unit: GlucoseUnits = .mgdL
+        @Published var units: GlucoseUnits = .mgdL
 
         var preferences: Preferences {
             settingsManager.preferences
         }
 
         override func subscribe() {
-            unit = settingsManager.settings.units
+            units = settingsManager.settings.units
             useNewFormula = settings.preferences.useNewFormula
             enableDynamicCR = settings.preferences.enableDynamicCR
             sigmoid = settings.preferences.sigmoid
@@ -29,7 +29,7 @@ extension DynamicSettings {
             weightPercentage = settings.preferences.weightPercentage
             tddAdjBasal = settings.preferences.tddAdjBasal
 
-            if unit == .mmolL {
+            if units == .mmolL {
                 threshold_setting = settings.preferences.threshold_setting.asMmolL
             } else {
                 threshold_setting = settings.preferences.threshold_setting
@@ -48,7 +48,7 @@ extension DynamicSettings {
         }
 
         func convertBack(_ glucose: Decimal) -> Decimal {
-            if unit == .mmolL {
+            if units == .mmolL {
                 return glucose.asMgdL
             }
             return glucose
@@ -71,3 +71,9 @@ extension DynamicSettings {
         }
     }
 }
+
+extension DynamicSettings.StateModel: SettingsObserver {
+    func settingsDidChange(_: FreeAPSSettings) {
+        units = settingsManager.settings.units
+    }
+}

+ 9 - 1
FreeAPS/Sources/Modules/DynamicSettings/View/DynamicSettingsRootView.swift

@@ -47,7 +47,7 @@ extension DynamicSettings {
         private var glucoseFormatter: NumberFormatter {
             let formatter = NumberFormatter()
             formatter.numberStyle = .decimal
-            if state.unit == .mmolL {
+            if state.units == .mmolL {
                 formatter.maximumFractionDigits = 1
             } else { formatter.maximumFractionDigits = 0 }
             formatter.roundingMode = .halfUp
@@ -67,6 +67,7 @@ extension DynamicSettings {
                             hintLabel = "Activate Dynamic Sensitivity (ISF)"
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: "Activate Dynamic Sensitivity (ISF)",
                     miniHint: "Trio calculates insulin sensitivity (ISF) each loop cycle based on current blood sugar, daily insulin use, and an adjustment factor, within set limits.",
@@ -86,6 +87,7 @@ extension DynamicSettings {
                                 hintLabel = "Activate Dynamic Carb Ratio (CR)"
                             }
                         ),
+                        units: state.units,
                         type: .boolean,
                         label: "Activate Dynamic Carb Ratio (CR)",
                         miniHint: "Similar to Dynamic Sensitivity, Trio calculates a dynamic carb ratio every loop cycle.",
@@ -103,6 +105,7 @@ extension DynamicSettings {
                                 hintLabel = "Use Sigmoid Formula"
                             }
                         ),
+                        units: state.units,
                         type: .boolean,
                         label: "Use Sigmoid Formula",
                         miniHint: "Alternative formula for dynamic ISF, that alters ISF based on distance from target BG",
@@ -121,6 +124,7 @@ extension DynamicSettings {
                                     hintLabel = "Adjustment Factor"
                                 }
                             ),
+                            units: state.units,
                             type: .decimal("adjustmentFactor"),
                             label: "Adjustment Factor",
                             miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -138,6 +142,7 @@ extension DynamicSettings {
                                     hintLabel = "Sigmoid Adjustment Factor"
                                 }
                             ),
+                            units: state.units,
                             type: .decimal("adjustmentFactorSigmoid"),
                             label: "Sigmoid Adjustment Factor",
                             miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -156,6 +161,7 @@ extension DynamicSettings {
                                 hintLabel = "Weighted Average of TDD"
                             }
                         ),
+                        units: state.units,
                         type: .decimal("weightPercentage"),
                         label: "Weighted Average of TDD",
                         miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -173,6 +179,7 @@ extension DynamicSettings {
                                 hintLabel = "Adjust Basal"
                             }
                         ),
+                        units: state.units,
                         type: .boolean,
                         label: "Adjust Basal",
                         miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -190,6 +197,7 @@ extension DynamicSettings {
                                 hintLabel = "Threshold Setting"
                             }
                         ),
+                        units: state.units,
                         type: .decimal("threshold_setting"),
                         label: "Threshold Setting",
                         miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",

+ 1 - 71
FreeAPS/Sources/Modules/GeneralSettings/UnitsLimitsSettingsProvider.swift

@@ -1,75 +1,5 @@
 import Foundation
 
 extension UnitsLimitsSettings {
-    final class Provider: BaseProvider, UnitsLimitsSettingsProvider {
-        @Injected() private var settingsManager: SettingsManager!
-
-        func migrateUnits() {
-            migrateTargets()
-            migrateISF()
-        }
-
-        private func migrateTargets() {
-            let profile = storage.retrieve(OpenAPS.Settings.bgTargets, as: BGTargets.self)
-                ?? BGTargets(from: OpenAPS.defaults(for: OpenAPS.Settings.bgTargets))
-                ?? BGTargets(units: .mmolL, userPrefferedUnits: .mmolL, targets: [])
-
-            let units = settingsManager.settings.units
-            guard units != profile.units else { return }
-
-            let targets = profile.targets.map { target -> BGTargetEntry in
-                if units == .mmolL {
-                    return BGTargetEntry(
-                        low: Decimal(round(Double(target.low.asMmolL) * 10) / 10),
-                        high: Decimal(round(Double(target.high.asMmolL) * 10) / 10),
-                        start: target.start,
-                        offset: target.offset
-                    )
-                } else {
-                    return BGTargetEntry(
-                        low: Decimal(round(Double(target.low.asMgdL))),
-                        high: Decimal(round(Double(target.high.asMgdL))),
-                        start: target.start,
-                        offset: target.offset
-                    )
-                }
-            }
-
-            let newProfile = BGTargets(units: units, userPrefferedUnits: units, targets: targets)
-            storage.save(newProfile, as: OpenAPS.Settings.bgTargets)
-        }
-
-        private func migrateISF() {
-            let profile = storage.retrieve(OpenAPS.Settings.insulinSensitivities, as: InsulinSensitivities.self)
-                ?? InsulinSensitivities(from: OpenAPS.defaults(for: OpenAPS.Settings.insulinSensitivities))
-                ?? InsulinSensitivities(
-                    units: .mmolL,
-                    userPrefferedUnits: .mmolL,
-                    sensitivities: []
-                )
-            let units = settingsManager.settings.units
-            guard units != profile.units else { return }
-
-            let sensitivities = profile.sensitivities.map { item -> InsulinSensitivityEntry in
-
-                if units == .mmolL {
-                    return InsulinSensitivityEntry(
-                        sensitivity: Decimal(round(Double(item.sensitivity.asMmolL) * 10) / 10),
-                        offset: item.offset,
-                        start: item.start
-                    )
-                } else {
-                    return InsulinSensitivityEntry(
-                        sensitivity: Decimal(round(Double(item.sensitivity.asMgdL))),
-                        offset: item.offset,
-                        start: item.start
-                    )
-                }
-            }
-
-            let newProfile = InsulinSensitivities(units: units, userPrefferedUnits: units, sensitivities: sensitivities)
-
-            storage.save(newProfile, as: OpenAPS.Settings.insulinSensitivities)
-        }
-    }
+    final class Provider: BaseProvider, UnitsLimitsSettingsProvider {}
 }

+ 6 - 2
FreeAPS/Sources/Modules/GeneralSettings/UnitsLimitsSettingsStateModel.swift

@@ -19,8 +19,6 @@ extension UnitsLimitsSettings {
             units = settingsManager.settings.units
             subscribeSetting(\.units, on: $unitsIndex.map { $0 == 0 ? GlucoseUnits.mgdL : .mmolL }) {
                 unitsIndex = $0 == .mgdL ? 0 : 1
-            } didSet: { [weak self] _ in
-                self?.provider.migrateUnits()
             }
 
             maxIOB = settings.preferences.maxIOB
@@ -45,3 +43,9 @@ extension UnitsLimitsSettings {
         }
     }
 }
+
+extension UnitsLimitsSettings.StateModel: SettingsObserver {
+    func settingsDidChange(_: FreeAPSSettings) {
+        units = settingsManager.settings.units
+    }
+}

+ 2 - 0
FreeAPS/Sources/Modules/GeneralSettings/View/UnitsLimitsSettingsRootView.swift

@@ -55,6 +55,7 @@ extension UnitsLimitsSettings {
                             hintLabel = NSLocalizedString("Max IOB", comment: "Max IOB")
                         }
                     ),
+                    units: state.units,
                     type: .decimal("maxIOB"),
                     label: NSLocalizedString("Max IOB", comment: "Max IOB"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -75,6 +76,7 @@ extension UnitsLimitsSettings {
                             hintLabel = NSLocalizedString("Max COB", comment: "Max COB")
                         }
                     ),
+                    units: state.units,
                     type: .decimal("maxCOB"),
                     label: NSLocalizedString("Max COB", comment: "Max COB"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",

+ 6 - 0
FreeAPS/Sources/Modules/GlucoseNotificationSettings/GlucoseNotificationSettingsStateModel.swift

@@ -38,3 +38,9 @@ extension GlucoseNotificationSettings {
         }
     }
 }
+
+extension GlucoseNotificationSettings.StateModel: SettingsObserver {
+    func settingsDidChange(_: FreeAPSSettings) {
+        units = settingsManager.settings.units
+    }
+}

+ 4 - 0
FreeAPS/Sources/Modules/GlucoseNotificationSettings/View/GlucoseNotificationSettingsRootView.swift

@@ -65,6 +65,7 @@ extension GlucoseNotificationSettings {
                             hintLabel = "Show Glucose App Badge"
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: "Show Glucose App Badge",
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -83,6 +84,7 @@ extension GlucoseNotificationSettings {
                             hintLabel = "Always Notify Glucose"
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: "Always Notify Glucose",
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -100,6 +102,7 @@ extension GlucoseNotificationSettings {
                             hintLabel = "Play Alarm Sound"
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: "Play Alarm Sound",
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -117,6 +120,7 @@ extension GlucoseNotificationSettings {
                             hintLabel = "Add Glucose Source to Alarm"
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: "Add Glucose Source to Alarm",
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",

+ 9 - 0
FreeAPS/Sources/Modules/HealthKit/HealthKitStateModel.swift

@@ -5,10 +5,13 @@ extension AppleHealthKit {
     final class StateModel: BaseStateModel<Provider> {
         @Injected() var healthKitManager: HealthKitManager!
 
+        @Published var units: GlucoseUnits = .mgdL
         @Published var useAppleHealth = false
         @Published var needShowInformationTextForSetPermissions = false
 
         override func subscribe() {
+            units = settingsManager.settings.units
+
             useAppleHealth = settingsManager.settings.useAppleHealth
 
             needShowInformationTextForSetPermissions = healthKitManager.areAllowAllPermissions
@@ -42,3 +45,9 @@ extension AppleHealthKit {
         }
     }
 }
+
+extension AppleHealthKit.StateModel: SettingsObserver {
+    func settingsDidChange(_: FreeAPSSettings) {
+        units = settingsManager.settings.units
+    }
+}

+ 1 - 0
FreeAPS/Sources/Modules/HealthKit/View/AppleHealthKitRootView.swift

@@ -44,6 +44,7 @@ extension AppleHealthKit {
                             hintLabel = "Connect to Apple Health"
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: "Connect to Apple Health",
                     miniHint: "Allows Trio to read from and write to Apple Health.",

+ 10 - 1
FreeAPS/Sources/Modules/ISFEditor/ISFEditorStateModel.swift

@@ -32,13 +32,16 @@ extension ISFEditor {
         private(set) var units: GlucoseUnits = .mgdL
 
         override func subscribe() {
+            units = settingsManager.settings.units
+
             let profile = provider.profile
-            units = profile.units
+
             items = profile.sensitivities.map { value in
                 let timeIndex = timeValues.firstIndex(of: Double(value.offset * 60)) ?? 0
                 let rateIndex = rateValues.firstIndex(of: value.sensitivity) ?? 0
                 return Item(rateIndex: rateIndex, timeIndex: timeIndex)
             }
+
             autotune = provider.autotune
 
             if let newISF = provider.autosense.newisf {
@@ -122,3 +125,9 @@ extension ISFEditor {
         }
     }
 }
+
+extension ISFEditor.StateModel: SettingsObserver {
+    func settingsDidChange(_: FreeAPSSettings) {
+        units = settingsManager.settings.units
+    }
+}

+ 9 - 0
FreeAPS/Sources/Modules/LiveActivitySettings/LiveActivitySettingsStateModel.swift

@@ -6,12 +6,21 @@ extension LiveActivitySettings {
         @Injected() var settings: SettingsManager!
         @Injected() var storage: FileStorage!
 
+        @Published var units: GlucoseUnits = .mgdL
         @Published var useLiveActivity = false
         @Published var lockScreenView: LockScreenView = .simple
 
         override func subscribe() {
+            units = settingsManager.settings.units
+
             subscribeSetting(\.useLiveActivity, on: $useLiveActivity) { useLiveActivity = $0 }
             subscribeSetting(\.lockScreenView, on: $lockScreenView) { lockScreenView = $0 }
         }
     }
 }
+
+extension LiveActivitySettings.StateModel: SettingsObserver {
+    func settingsDidChange(_: FreeAPSSettings) {
+        units = settingsManager.settings.units
+    }
+}

+ 1 - 0
FreeAPS/Sources/Modules/LiveActivitySettings/View/LiveActivitySettingsRootView.swift

@@ -72,6 +72,7 @@ extension LiveActivitySettings {
                                 hintLabel = "Enable Live Activity"
                             }
                         ),
+                        units: state.units,
                         type: .boolean,
                         label: "Enable Live Activity",
                         miniHint: "Live Activities display Trio's glucose readings, and other current data on the iPhone Lock Screen and in the Dynamic Island",

+ 9 - 0
FreeAPS/Sources/Modules/MealSettings/MealSettingsStateModel.swift

@@ -2,6 +2,7 @@ import SwiftUI
 
 extension MealSettings {
     final class StateModel: BaseStateModel<Provider> {
+        @Published var units: GlucoseUnits = .mgdL
         @Published var useFPUconversion: Bool = true
         @Published var maxCarbs: Decimal = 250
         @Published var maxFat: Decimal = 250
@@ -12,6 +13,8 @@ extension MealSettings {
         @Published var delay: Decimal = 0
 
         override func subscribe() {
+            units = settingsManager.settings.units
+
             subscribeSetting(\.useFPUconversion, on: $useFPUconversion) { useFPUconversion = $0 }
             subscribeSetting(\.maxCarbs, on: $maxCarbs) { maxCarbs = $0 }
             subscribeSetting(\.maxFat, on: $maxFat) { maxFat = $0 }
@@ -46,3 +49,9 @@ extension MealSettings {
         }
     }
 }
+
+extension MealSettings.StateModel: SettingsObserver {
+    func settingsDidChange(_: FreeAPSSettings) {
+        units = settingsManager.settings.units
+    }
+}

+ 5 - 0
FreeAPS/Sources/Modules/MealSettings/View/MealSettingsRootView.swift

@@ -198,6 +198,7 @@ extension MealSettings {
                             hintLabel = "Display and Allow Fat and Protein Entries"
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: "Display and Allow Fat and Protein Entries",
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -217,6 +218,7 @@ extension MealSettings {
                                 hintLabel = "Fat and Protein Delay"
                             }
                         ),
+                        units: state.units,
                         type: .decimal("delay"),
                         label: "Fat and Protein Delay",
                         miniHint: "Delay is time from now until the first future carb entry.",
@@ -234,6 +236,7 @@ extension MealSettings {
                                 hintLabel = "Maximum Duration (hours)"
                             }
                         ),
+                        units: state.units,
                         type: .decimal("timeCap"),
                         label: "Maximum Duration (hours)",
                         miniHint: "Carb spread over a maximum number of hours (5-12).",
@@ -251,6 +254,7 @@ extension MealSettings {
                                 hintLabel = "Spread Interval (minutes)"
                             }
                         ),
+                        units: state.units,
                         type: .decimal("minuteInterval"),
                         label: "Spread Interval (minutes)",
                         miniHint: "Interval in minutes is how many minutes are between entries.",
@@ -268,6 +272,7 @@ extension MealSettings {
                                 hintLabel = "Fat and Protein Factor"
                             }
                         ),
+                        units: state.units,
                         type: .decimal("individualAdjustmentFactor"),
                         label: "Fat and Protein Factor",
                         miniHint: "Influences how many carb equivalents are recorded for fat and protein.",

+ 6 - 0
FreeAPS/Sources/Modules/NightscoutConfig/NightscoutConfigStateModel.swift

@@ -373,3 +373,9 @@ extension NightscoutConfig {
         }
     }
 }
+
+extension NightscoutConfig.StateModel: SettingsObserver {
+    func settingsDidChange(_: FreeAPSSettings) {
+        units = settingsManager.settings.units
+    }
+}

+ 2 - 0
FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutFetchView.swift

@@ -42,6 +42,7 @@ struct NightscoutFetchView: View {
                         hintLabel = "Allow Fetching from Nightscout"
                     }
                 ),
+                units: state.units,
                 type: .boolean,
                 label: "Allow Fetching from Nightscout",
                 miniHint: "Enable fetching of selected data sets from Nightscout. See hint for more details.",
@@ -61,6 +62,7 @@ struct NightscoutFetchView: View {
                             hintLabel = "Allow Remote Control of Trio"
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: "Allow Remote Control of Trio",
                     miniHint: "Enables selected remote control capabilities via Nightscout. See hint for more details.",

+ 2 - 0
FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutUploadView.swift

@@ -42,6 +42,7 @@ struct NightscoutUploadView: View {
                         shouldDisplayHint = true
                     }
                 ),
+                units: state.units,
                 type: .boolean,
                 label: "Allow Uploading to Nightscout",
                 miniHint: "Enables upload of selected data sets to Nightscout. See hint for more details.",
@@ -61,6 +62,7 @@ struct NightscoutUploadView: View {
                             shouldDisplayHint = true
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: "Upload Glucose",
                     miniHint: "Enables uploading of CGM readings to Nightscout.",

+ 10 - 0
FreeAPS/Sources/Modules/PumpSettingsEditor/PumpSettingsEditorStateModel.swift

@@ -3,6 +3,8 @@ import SwiftUI
 
 extension PumpSettingsEditor {
     final class StateModel: BaseStateModel<Provider> {
+        @Published var units: GlucoseUnits = .mgdL
+
         @Published var maxBasal: Decimal = 0.0 {
             didSet {
                 checkForChanges()
@@ -29,6 +31,8 @@ extension PumpSettingsEditor {
         private var initialDia: Decimal = 0.0
 
         override func subscribe() {
+            units = settingsManager.settings.units
+
             let settings = provider.settings()
             maxBasal = settings.maxBasal
             maxBolus = settings.maxBolus
@@ -77,3 +81,9 @@ extension PumpSettingsEditor {
         }
     }
 }
+
+extension PumpSettingsEditor.StateModel: SettingsObserver {
+    func settingsDidChange(_: FreeAPSSettings) {
+        units = settingsManager.settings.units
+    }
+}

+ 1 - 0
FreeAPS/Sources/Modules/PumpSettingsEditor/View/PumpSettingsEditorRootView.swift

@@ -91,6 +91,7 @@ extension PumpSettingsEditor {
                             hintLabel = "Duration of Insulin Action"
                         }
                     ),
+                    units: state.units,
                     type: .decimal("dia"),
                     label: "Duration of Insulin Action",
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",

+ 7 - 1
FreeAPS/Sources/Modules/SMBSettings/SMBSettingsStateModel.swift

@@ -35,7 +35,7 @@ extension SMBSettings {
             enableSMBAfterCarbs = settings.preferences.enableSMBAfterCarbs
             allowSMBWithHighTemptarget = settings.preferences.allowSMBWithHighTemptarget
             enableSMB_high_bg = settings.preferences.enableSMB_high_bg
-            enableSMB_high_bg_target = units == .mmolL ? settings.preferences.enableSMB_high_bg_target.asMmolL : settings
+            enableSMB_high_bg_target = settings
                 .preferences.enableSMB_high_bg_target
             maxSMBBasalMinutes = settings.preferences.maxSMBBasalMinutes
             smbDeliveryRatio = settings.preferences.smbDeliveryRatio
@@ -85,3 +85,9 @@ extension SMBSettings {
         }
     }
 }
+
+extension SMBSettings.StateModel: SettingsObserver {
+    func settingsDidChange(_: FreeAPSSettings) {
+        units = settingsManager.settings.units
+    }
+}

+ 12 - 0
FreeAPS/Sources/Modules/SMBSettings/View/SMBSettingsRootView.swift

@@ -45,6 +45,7 @@ extension SMBSettings {
                             hintLabel = NSLocalizedString("Enable SMB Always", comment: "Enable SMB Always")
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: NSLocalizedString("Enable SMB Always", comment: "Enable SMB Always"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -67,6 +68,7 @@ extension SMBSettings {
                                 hintLabel = NSLocalizedString("Enable SMB With COB", comment: "Enable SMB With COB")
                             }
                         ),
+                        units: state.units,
                         type: .boolean,
                         label: NSLocalizedString("Enable SMB With COB", comment: "Enable SMB With COB"),
                         miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -87,6 +89,7 @@ extension SMBSettings {
                                 hintLabel = NSLocalizedString("Enable SMB With Temptarget", comment: "Enable SMB With Temptarget")
                             }
                         ),
+                        units: state.units,
                         type: .boolean,
                         label: NSLocalizedString("Enable SMB With Temptarget", comment: "Enable SMB With Temptarget"),
                         miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -107,6 +110,7 @@ extension SMBSettings {
                                 hintLabel = NSLocalizedString("Enable SMB After Carbs", comment: "Enable SMB After Carbs")
                             }
                         ),
+                        units: state.units,
                         type: .boolean,
                         label: NSLocalizedString("Enable SMB After Carbs", comment: "Enable SMB After Carbs"),
                         miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -130,6 +134,7 @@ extension SMBSettings {
                                 )
                             }
                         ),
+                        units: state.units,
                         type: .boolean,
                         label: NSLocalizedString(
                             "Allow SMB With High Temptarget",
@@ -153,6 +158,7 @@ extension SMBSettings {
                                 hintLabel = NSLocalizedString("Enable SMB With High BG", comment: "Enable SMB With High BG")
                             }
                         ),
+                        units: state.units,
                         type: .conditionalDecimal("enableSMB_high_bg_target"),
                         label: NSLocalizedString("Enable SMB With High BG", comment: "Enable SMB With High BG"),
                         conditionalLabel: "High BG Target",
@@ -174,6 +180,7 @@ extension SMBSettings {
                                 hintLabel = NSLocalizedString("Enable UAM", comment: "Enable UAM")
                             }
                         ),
+                        units: state.units,
                         type: .boolean,
                         label: NSLocalizedString("Enable UAM", comment: "Enable UAM"),
                         miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -195,6 +202,7 @@ extension SMBSettings {
                             hintLabel = NSLocalizedString("Max SMB Basal Minutes", comment: "Max SMB Basal Minutes")
                         }
                     ),
+                    units: state.units,
                     type: .decimal("maxSMBBasalMinutes"),
                     label: NSLocalizedString("Max SMB Basal Minutes", comment: "Max SMB Basal Minutes"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -215,6 +223,7 @@ extension SMBSettings {
                             hintLabel = NSLocalizedString("Max UAM SMB Basal Minutes", comment: "Max UAM SMB Basal Minutes")
                         }
                     ),
+                    units: state.units,
                     type: .decimal("maxUAMSMBBasalMinutes"),
                     label: NSLocalizedString("Max UAM SMB Basal Minutes", comment: "Max UAM SMB Basal Minutes"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -235,6 +244,7 @@ extension SMBSettings {
                             hintLabel = NSLocalizedString("Max Delta-BG Threshold SMB", comment: "Max Delta-BG Threshold")
                         }
                     ),
+                    units: state.units,
                     type: .decimal("maxDeltaBGthreshold"),
                     label: NSLocalizedString("Max Delta-BG Threshold SMB", comment: "Max Delta-BG Threshold"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -255,6 +265,7 @@ extension SMBSettings {
                             hintLabel = NSLocalizedString("SMB DeliveryRatio", comment: "SMB DeliveryRatio")
                         }
                     ),
+                    units: state.units,
                     type: .decimal("smbDeliveryRatio"),
                     label: NSLocalizedString("SMB DeliveryRatio", comment: "SMB DeliveryRatio"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -275,6 +286,7 @@ extension SMBSettings {
                             hintLabel = NSLocalizedString("SMB Interval", comment: "SMB Interval")
                         }
                     ),
+                    units: state.units,
                     type: .decimal("smbInterval"),
                     label: NSLocalizedString("SMB Interval", comment: "SMB Interval"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",

+ 3 - 0
FreeAPS/Sources/Modules/Settings/SettingsStateModel.swift

@@ -10,6 +10,7 @@ extension Settings {
         @Injected() var pluginManager: PluginManager!
         @Injected() var fetchCgmManager: FetchGlucoseManager!
 
+        @Published var units: GlucoseUnits = .mgdL
         @Published var closedLoop = false
         @Published var debugOptions = false
         @Published var serviceUIType: ServiceUI.Type?
@@ -21,6 +22,8 @@ extension Settings {
         private(set) var copyrightNotice = ""
 
         override func subscribe() {
+            units = settingsManager.settings.units
+
             subscribeSetting(\.debugOptions, on: $debugOptions) { debugOptions = $0 }
             subscribeSetting(\.closedLoop, on: $closedLoop) { closedLoop = $0 }
 

+ 1 - 0
FreeAPS/Sources/Modules/Settings/View/SettingsRootView.swift

@@ -97,6 +97,7 @@ extension Settings {
                                 hintLabel = "Closed Loop"
                             }
                         ),
+                        units: state.units,
                         type: .boolean,
                         label: "Closed Loop",
                         miniHint: "Enables automated insulin delivery. Requires active CGM sensor session and connected pump.",

+ 9 - 0
FreeAPS/Sources/Modules/ShortcutsConfig/ShortcutsConfigStateModel.swift

@@ -8,10 +8,13 @@ import SwiftUI
 
 extension ShortcutsConfig {
     final class StateModel: BaseStateModel<Provider> {
+        @Published var units: GlucoseUnits = .mgdL
         @Published var allowBolusByShortcuts: Bool = false
         @Published var maxBolusByShortcuts: BolusShortcutLimit = .notAllowed
 
         override func subscribe() {
+            units = settingsManager.settings.units
+
             subscribeSetting(\.bolusShortcut, on: $maxBolusByShortcuts) {
                 maxBolusByShortcuts = ($0 == .notAllowed) ? .limitBolusMax : $0
                 allowBolusByShortcuts = ($0 != .notAllowed)
@@ -34,3 +37,9 @@ extension ShortcutsConfig {
         }
     }
 }
+
+extension ShortcutsConfig.StateModel: SettingsObserver {
+    func settingsDidChange(_: FreeAPSSettings) {
+        units = settingsManager.settings.units
+    }
+}

+ 1 - 0
FreeAPS/Sources/Modules/ShortcutsConfig/View/ShortcutsConfigView.swift

@@ -67,6 +67,7 @@ extension ShortcutsConfig {
                             hintLabel = "Allow Bolusing with Shortcuts"
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: "Allow Bolusing with Shortcuts",
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",

+ 7 - 1
FreeAPS/Sources/Modules/TargetBehavoir/TargetBehavoirStateModel.swift

@@ -26,7 +26,7 @@ extension TargetBehavoir {
             resistanceLowersTarget = settings.preferences.resistanceLowersTarget
             halfBasalExerciseTarget = settings.preferences.halfBasalExerciseTarget
 
-            halfBasalExerciseTarget = units == .mmolL ? settings.preferences.halfBasalExerciseTarget.asMmolL : settings
+            halfBasalExerciseTarget = settings
                 .preferences.halfBasalExerciseTarget
         }
 
@@ -54,3 +54,9 @@ extension TargetBehavoir {
         }
     }
 }
+
+extension TargetBehavoir.StateModel: SettingsObserver {
+    func settingsDidChange(_: FreeAPSSettings) {
+        units = settingsManager.settings.units
+    }
+}

+ 5 - 0
FreeAPS/Sources/Modules/TargetBehavoir/View/TargetBehavoirRootView.swift

@@ -48,6 +48,7 @@ extension TargetBehavoir {
                             )
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: NSLocalizedString(
                         "High Temptarget Raises Sensitivity",
@@ -75,6 +76,7 @@ extension TargetBehavoir {
                             )
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: NSLocalizedString(
                         "Low Temptarget Lowers Sensitivity",
@@ -98,6 +100,7 @@ extension TargetBehavoir {
                             hintLabel = NSLocalizedString("Sensitivity Raises Target", comment: "Sensitivity Raises Target")
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: NSLocalizedString("Sensitivity Raises Target", comment: "Sensitivity Raises Target"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -118,6 +121,7 @@ extension TargetBehavoir {
                             hintLabel = NSLocalizedString("Resistance Lowers Target", comment: "Resistance Lowers Target")
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: NSLocalizedString("Resistance Lowers Target", comment: "Resistance Lowers Target"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -138,6 +142,7 @@ extension TargetBehavoir {
                             hintLabel = NSLocalizedString("Half Basal Exercise Target", comment: "Half Basal Exercise Target")
                         }
                     ),
+                    units: state.units,
                     type: .decimal("halfBasalExerciseTarget"),
                     label: NSLocalizedString("Half Basal Exercise Target", comment: "Half Basal Exercise Target"),
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",

+ 1 - 1
FreeAPS/Sources/Modules/TargetsEditor/TargetsEditorProvider.swift

@@ -3,7 +3,7 @@ extension TargetsEditor {
         var profile: BGTargets {
             storage.retrieve(OpenAPS.Settings.bgTargets, as: BGTargets.self)
                 ?? BGTargets(from: OpenAPS.defaults(for: OpenAPS.Settings.bgTargets))
-                ?? BGTargets(units: .mmolL, userPrefferedUnits: .mmolL, targets: [])
+                ?? BGTargets(units: .mgdL, userPrefferedUnits: .mgdL, targets: [])
         }
 
         func saveProfile(_ profile: BGTargets) {

+ 3 - 1
FreeAPS/Sources/Modules/TargetsEditor/TargetsEditorStateModel.swift

@@ -23,8 +23,10 @@ extension TargetsEditor {
         private(set) var units: GlucoseUnits = .mgdL
 
         override func subscribe() {
+            units = settingsManager.settings.units
+
             let profile = provider.profile
-            units = profile.units
+
             items = profile.targets.map { value in
                 let timeIndex = timeValues.firstIndex(of: Double(value.offset * 60)) ?? 0
                 let lowIndex = rateValues.firstIndex(of: value.low) ?? 0

+ 9 - 14
FreeAPS/Sources/Modules/UserInterfaceSettings/UserInterfaceSettingsStateModel.swift

@@ -28,20 +28,9 @@ extension UserInterfaceSettings {
 
             subscribeSetting(\.totalInsulinDisplayType, on: $totalInsulinDisplayType) { totalInsulinDisplayType = $0 }
 
-            subscribeSetting(\.low, on: $low, initial: {
-                let value = max(min($0, 90), 40)
-                low = units == .mmolL ? value.asMmolL : value
-            }, map: {
-                guard units == .mmolL else { return $0 }
-                return $0.asMgdL
-            })
-            subscribeSetting(\.high, on: $high, initial: {
-                let value = max(min($0, 270), 110)
-                high = units == .mmolL ? value.asMmolL : value
-            }, map: {
-                guard units == .mmolL else { return $0 }
-                return $0.asMgdL
-            })
+            subscribeSetting(\.low, on: $low) { low = $0 }
+
+            subscribeSetting(\.high, on: $high) { high = $0 }
 
             subscribeSetting(\.showCarbsRequiredBadge, on: $showCarbsRequiredBadge) { showCarbsRequiredBadge = $0 }
 
@@ -53,6 +42,12 @@ extension UserInterfaceSettings {
     }
 }
 
+extension UserInterfaceSettings.StateModel: SettingsObserver {
+    func settingsDidChange(_: FreeAPSSettings) {
+        units = settingsManager.settings.units
+    }
+}
+
 enum TotalInsulinDisplayType: String, JSON, CaseIterable, Identifiable, Codable, Hashable {
     var id: String { rawValue }
     case totalDailyDose

+ 11 - 4
FreeAPS/Sources/Modules/UserInterfaceSettings/View/UserInterfaceSettingsRootView.swift

@@ -96,6 +96,7 @@ extension UserInterfaceSettings {
                             hintLabel = "Show Low and High Thresholds"
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: "Show Low and High Thresholds",
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -111,7 +112,7 @@ extension UserInterfaceSettings {
                                 Spacer()
 
                                 Group {
-                                    Text(state.low.description)
+                                    Text(state.units == .mgdL ? state.low.description : state.low.asMmolL.description)
                                         .foregroundColor(!displayPickerLowThreshold ? .primary : .accentColor)
 
                                     Text(state.units == .mgdL ? " mg/dL" : " mmol/L").foregroundColor(.secondary)
@@ -131,7 +132,8 @@ extension UserInterfaceSettings {
                                     PickerSettingsProvider.shared.generatePickerValues(from: setting),
                                     id: \.self
                                 ) { value in
-                                    Text("\(value.description)").tag(value)
+                                    let displayValue = state.units == .mgdL ? value : value.asMmolL
+                                    Text("\(displayValue.description)").tag(value)
                                 }
                             }
                             .pickerStyle(WheelPickerStyle())
@@ -145,7 +147,7 @@ extension UserInterfaceSettings {
                                 Spacer()
 
                                 Group {
-                                    Text(state.high.description)
+                                    Text(state.units == .mgdL ? state.high.description : state.high.asMmolL.description)
                                         .foregroundColor(!displayPickerHighThreshold ? .primary : .accentColor)
 
                                     Text(state.units == .mgdL ? " mg/dL" : " mmol/L").foregroundColor(.secondary)
@@ -164,7 +166,8 @@ extension UserInterfaceSettings {
                                     PickerSettingsProvider.shared.generatePickerValues(from: setting),
                                     id: \.self
                                 ) { value in
-                                    Text("\(value.description)").tag(value)
+                                    let displayValue = state.units == .mgdL ? value : value.asMmolL
+                                    Text("\(displayValue.description)").tag(value)
                                 }
                             }
                             .pickerStyle(WheelPickerStyle())
@@ -207,6 +210,7 @@ extension UserInterfaceSettings {
                             hintLabel = "X-Axis Interval Step"
                         }
                     ),
+                    units: state.units,
                     type: .decimal("hours"),
                     label: "X-Axis Interval Step",
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -260,6 +264,7 @@ extension UserInterfaceSettings {
                             hintLabel = "Override HbA1c Unit"
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: "Override HbA1c Unit",
                     miniHint: "Display HbA1c in mmol/L or %. Default is percent.",
@@ -279,6 +284,7 @@ extension UserInterfaceSettings {
                             hintLabel = "Standing / Laying TIR Chart"
                         }
                     ),
+                    units: state.units,
                     type: .boolean,
                     label: "Standing / Laying TIR Chart",
                     miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -296,6 +302,7 @@ extension UserInterfaceSettings {
                             hintLabel = "Show Carbs Required Badge"
                         }
                     ),
+                    units: state.units,
                     type: .conditionalDecimal("carbsRequiredThreshold"),
                     label: "Show Carbs Required Badge",
                     conditionalLabel: "Carbs Required Threshold",

+ 2 - 0
FreeAPS/Sources/Modules/WatchConfig/View/WatchConfigAppleWatchView.swift

@@ -84,6 +84,7 @@ struct WatchConfigAppleWatchView: View {
                         hintLabel = "Show Protein and Fat"
                     }
                 ),
+                units: state.units,
                 type: .boolean,
                 label: "Show Protein and Fat",
                 miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
@@ -101,6 +102,7 @@ struct WatchConfigAppleWatchView: View {
                         hintLabel = "Confirm Bolus Faster"
                     }
                 ),
+                units: state.units,
                 type: .boolean,
                 label: "Confirm Bolus Faster",
                 miniHint: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",

+ 10 - 0
FreeAPS/Sources/Modules/WatchConfig/WatchConfigStateModel.swift

@@ -28,6 +28,8 @@ enum AwConfig: String, JSON, CaseIterable, Identifiable, Codable {
 extension WatchConfig {
     final class StateModel: BaseStateModel<Provider> {
         @Injected() private var garmin: GarminManager!
+
+        @Published var units: GlucoseUnits = .mgdL
         @Published var devices: [IQDevice] = []
         @Published var selectedAwConfig: AwConfig = .HR
         @Published var displayFatAndProteinOnWatch = false
@@ -38,6 +40,8 @@ extension WatchConfig {
         override func subscribe() {
             preferences = provider.preferences
 
+            units = settingsManager.settings.units
+
             subscribeSetting(\.displayFatAndProteinOnWatch, on: $displayFatAndProteinOnWatch) { displayFatAndProteinOnWatch = $0 }
             subscribeSetting(\.confirmBolusFaster, on: $confirmBolusFaster) { confirmBolusFaster = $0 }
             subscribeSetting(\.displayOnWatch, on: $selectedAwConfig) { selectedAwConfig = $0 }
@@ -66,3 +70,9 @@ extension WatchConfig {
         }
     }
 }
+
+extension WatchConfig.StateModel: SettingsObserver {
+    func settingsDidChange(_: FreeAPSSettings) {
+        units = settingsManager.settings.units
+    }
+}

+ 26 - 15
FreeAPS/Sources/Views/SettingInputSection.swift

@@ -25,6 +25,7 @@ struct SettingInputSection: View {
     @Binding var shouldDisplayHint: Bool
     @Binding var selectedVerboseHint: String?
 
+    var units: GlucoseUnits
     var type: SettingInputSectionType
     var label: String
     var conditionalLabel: String?
@@ -36,13 +37,7 @@ struct SettingInputSection: View {
     // Access the shared PickerSettingsProvider instance
     @ObservedObject private var pickerSettingsProvider = PickerSettingsProvider.shared
     @State private var displayPicker: Bool = false
-    @State private var units: GlucoseUnits = .mgdL
-
-    @Injected() var settingsManager: SettingsManager!
-
-    mutating func subscribe() {
-        units = settingsManager.settings.units
-    }
+    @State private var displayConditionalPicker: Bool = false
 
     var body: some View {
         Section(
@@ -58,8 +53,11 @@ struct SettingInputSection: View {
                                     Spacer()
 
                                     Group {
-                                        Text(decimalValue.description)
-                                            .foregroundColor(!displayPicker ? .primary : .accentColor)
+                                        Text(
+                                            units == .mmolL ? decimalValue.asMmolL.description : decimalValue
+                                                .description
+                                        )
+                                        .foregroundColor(!displayPicker ? .primary : .accentColor)
 
                                         if setting.type == PickerSetting.PickerSettingType.glucose {
                                             Text(units == .mgdL ? " mg/dL" : " mmol/L").foregroundColor(.secondary)
@@ -76,7 +74,12 @@ struct SettingInputSection: View {
                                 if displayPicker {
                                     Picker(selection: $decimalValue, label: Text("")) {
                                         ForEach(pickerSettingsProvider.generatePickerValues(from: setting), id: \.self) { value in
-                                            Text("\(value.description)").tag(value)
+                                            if setting.type == PickerSetting.PickerSettingType.glucose {
+                                                let displayValue = units == .mgdL ? value : value.asMmolL
+                                                Text("\(displayValue.description)").tag(value)
+                                            } else {
+                                                Text("\(value.description)").tag(value)
+                                            }
                                         }
                                     }
                                     .pickerStyle(WheelPickerStyle())
@@ -108,8 +111,11 @@ struct SettingInputSection: View {
                                         Spacer()
 
                                         Group {
-                                            Text(decimalValue.description)
-                                                .foregroundColor(!displayPicker ? .primary : .accentColor)
+                                            Text(
+                                                units == .mmolL ? decimalValue.asMmolL
+                                                    .description : decimalValue.description
+                                            )
+                                            .foregroundColor(!displayPicker ? .primary : .accentColor)
 
                                             if setting.type == PickerSetting.PickerSettingType.glucose {
                                                 Text(units == .mgdL ? " mg/dL" : " mmol/L").foregroundColor(.secondary)
@@ -119,17 +125,22 @@ struct SettingInputSection: View {
                                                 Text(" g").foregroundColor(.secondary)
                                             }
                                         }.onTapGesture {
-                                            displayPicker.toggle()
+                                            displayConditionalPicker.toggle()
                                         }
                                     }.padding(.top)
 
-                                    if displayPicker {
+                                    if displayConditionalPicker {
                                         Picker(selection: $decimalValue, label: Text("")) {
                                             ForEach(
                                                 pickerSettingsProvider.generatePickerValues(from: setting),
                                                 id: \.self
                                             ) { value in
-                                                Text("\(value.description)").tag(value)
+                                                if setting.type == PickerSetting.PickerSettingType.glucose {
+                                                    let displayValue = units == .mgdL ? value : value.asMmolL
+                                                    Text("\(displayValue.description)").tag(value)
+                                                } else {
+                                                    Text("\(value.description)").tag(value)
+                                                }
                                             }
                                         }
                                         .pickerStyle(WheelPickerStyle())

+ 56 - 2
Trio.xcworkspace/xcshareddata/swiftpm/Package.resolved

@@ -1,5 +1,5 @@
 {
-  "originHash" : "cef813f4bbb01679d4ac9bf4a9f82c1a0a61e44dc839643e81aa92e4d00642bc",
+  "originHash" : "f5c836c216c4ca7d356e3777e58d6d4f9502b03f3974891349eb775f4c4cf750",
   "pins" : [
     {
       "identity" : "cryptoswift",
@@ -11,6 +11,15 @@
       }
     },
     {
+      "identity" : "mkringprogressview",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/maxkonovalov/MKRingProgressView.git",
+      "state" : {
+        "branch" : "master",
+        "revision" : "660888aab1d2ab0ed7eb9eb53caec12af4955fa7"
+      }
+    },
+    {
       "identity" : "slidebutton",
       "kind" : "remoteSourceControl",
       "location" : "https://github.com/no-comment/SlideButton",
@@ -20,15 +29,60 @@
       }
     },
     {
+      "identity" : "swift-algorithms",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/apple/swift-algorithms",
+      "state" : {
+        "revision" : "2327673b0e9c7e90e6b1826376526ec3627210e4",
+        "version" : "0.2.1"
+      }
+    },
+    {
+      "identity" : "swift-numerics",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/apple/swift-numerics",
+      "state" : {
+        "revision" : "6583ac70c326c3ee080c1d42d9ca3361dca816cd",
+        "version" : "0.1.0"
+      }
+    },
+    {
       "identity" : "swiftcharts",
       "kind" : "remoteSourceControl",
-      "location" : "https://github.com/ivanschuetz/SwiftCharts.git",
+      "location" : "https://github.com/ivanschuetz/SwiftCharts",
       "state" : {
         "branch" : "master",
         "revision" : "c354c1945bb35a1f01b665b22474f6db28cba4a2"
       }
     },
     {
+      "identity" : "swiftdate",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/malcommac/SwiftDate",
+      "state" : {
+        "revision" : "6190d0cefff3013e77ed567e6b074f324e5c5bf5",
+        "version" : "6.3.1"
+      }
+    },
+    {
+      "identity" : "swiftmessages",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/SwiftKickMobile/SwiftMessages",
+      "state" : {
+        "revision" : "62e12e138fc3eedf88c7553dd5d98712aa119f40",
+        "version" : "9.0.9"
+      }
+    },
+    {
+      "identity" : "swinject",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/Swinject/Swinject",
+      "state" : {
+        "revision" : "be9dbcc7b86811bc131539a20c6f9c2d3e56919f",
+        "version" : "2.9.1"
+      }
+    },
+    {
       "identity" : "tidepoolkit",
       "kind" : "remoteSourceControl",
       "location" : "https://github.com/tidepool-org/TidepoolKit",