Sfoglia il codice sorgente

Introduce the new Observable Framework, fix Race Condition in Home State Model

polscm32 1 anno fa
parent
commit
f32004148c
43 ha cambiato i file con 549 aggiunte e 535 eliminazioni
  1. 10 10
      FreeAPS.xcodeproj/project.pbxproj
  2. 22 21
      FreeAPS/Sources/Modules/AlgorithmAdvancedSettings/AlgorithmAdvancedSettingsStateModel.swift
  3. 1 1
      FreeAPS/Sources/Modules/AlgorithmAdvancedSettings/View/AlgorithmAdvancedSettingsRootView.swift
  4. 8 7
      FreeAPS/Sources/Modules/AutosensSettings/AutosensSettingsStateModel.swift
  5. 1 1
      FreeAPS/Sources/Modules/AutosensSettings/View/AutosensSettingsRootView.swift
  6. 9 8
      FreeAPS/Sources/Modules/BasalProfileEditor/BasalProfileEditorStateModel.swift
  7. 1 1
      FreeAPS/Sources/Modules/BasalProfileEditor/View/BasalProfileEditorRootView.swift
  8. 101 102
      FreeAPS/Sources/Modules/Bolus/BolusStateModel.swift
  9. 1 0
      FreeAPS/Sources/Modules/Bolus/View/AddMealPresetView.swift
  10. 2 2
      FreeAPS/Sources/Modules/Bolus/View/BolusRootView.swift
  11. 13 14
      FreeAPS/Sources/Modules/Bolus/View/ForeCastChart.swift
  12. 1 1
      FreeAPS/Sources/Modules/Bolus/View/MealPresetView.swift
  13. 1 1
      FreeAPS/Sources/Modules/Bolus/View/PopupView.swift
  14. 10 10
      FreeAPS/Sources/Modules/Calibrations/CalibrationsStateModel.swift
  15. 1 1
      FreeAPS/Sources/Modules/Calibrations/View/CalibrationsChart.swift
  16. 2 2
      FreeAPS/Sources/Modules/Calibrations/View/CalibrationsRootView.swift
  17. 19 18
      FreeAPS/Sources/Modules/DataTable/DataTableStateModel.swift
  18. 3 1
      FreeAPS/Sources/Modules/DataTable/View/DataTableRootView.swift
  19. 13 12
      FreeAPS/Sources/Modules/DynamicSettings/DynamicSettingsStateModel.swift
  20. 1 1
      FreeAPS/Sources/Modules/DynamicSettings/View/DynamicSettingsRootView.swift
  21. 3 5
      FreeAPS/Sources/Modules/Home/HomeStateModel+Setup/ChartAxisSetup.swift
  22. 8 3
      FreeAPS/Sources/Modules/Home/HomeStateModel+Setup/ForecastSetup.swift
  23. 84 83
      FreeAPS/Sources/Modules/Home/HomeStateModel.swift
  24. 11 11
      FreeAPS/Sources/Modules/Home/View/Chart/MainChartView.swift
  25. 8 8
      FreeAPS/Sources/Modules/Home/View/Header/CurrentGlucoseView.swift
  26. 6 6
      FreeAPS/Sources/Modules/Home/View/Header/LoopView.swift
  27. 7 7
      FreeAPS/Sources/Modules/Home/View/Header/PumpView.swift
  28. 28 30
      FreeAPS/Sources/Modules/Home/View/HomeRootView.swift
  29. 9 8
      FreeAPS/Sources/Modules/ISFEditor/ISFEditorStateModel.swift
  30. 1 1
      FreeAPS/Sources/Modules/ISFEditor/View/ISFEditorRootView.swift
  31. 5 4
      FreeAPS/Sources/Modules/ManualTempBasal/ManualTempBasalStateModel.swift
  32. 1 1
      FreeAPS/Sources/Modules/ManualTempBasal/View/ManualTempBasalRootView.swift
  33. 45 44
      FreeAPS/Sources/Modules/OverrideConfig/OverrideStateModel.swift
  34. 2 2
      FreeAPS/Sources/Modules/OverrideConfig/View/EditOverrideForm.swift
  35. 1 1
      FreeAPS/Sources/Modules/OverrideConfig/View/OverrideRootView.swift
  36. 19 18
      FreeAPS/Sources/Modules/SMBSettings/SMBSettingsStateModel.swift
  37. 1 1
      FreeAPS/Sources/Modules/SMBSettings/View/SMBSettingsRootView.swift
  38. 5 4
      FreeAPS/Sources/Modules/Snooze/SnoozeStateModel.swift
  39. 1 1
      FreeAPS/Sources/Modules/Snooze/View/SnoozeRootView.swift
  40. 10 9
      FreeAPS/Sources/Modules/Stat/StatStateModel.swift
  41. 16 16
      FreeAPS/Sources/Modules/Stat/View/ChartsView.swift
  42. 46 46
      FreeAPS/Sources/Modules/Stat/View/StatRootView.swift
  43. 12 12
      FreeAPS/Sources/Modules/Stat/View/StatsView.swift

+ 10 - 10
FreeAPS.xcodeproj/project.pbxproj

@@ -3906,7 +3906,7 @@
 				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
 				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
 				ENABLE_PREVIEWS = YES;
 				ENABLE_PREVIEWS = YES;
 				INFOPLIST_FILE = FreeAPS/Resources/Info.plist;
 				INFOPLIST_FILE = FreeAPS/Resources/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 16.2;
+				IPHONEOS_DEPLOYMENT_TARGET = 17.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(inherited)",
 					"@executable_path/Frameworks",
 					"@executable_path/Frameworks",
@@ -3947,7 +3947,7 @@
 				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
 				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
 				ENABLE_PREVIEWS = YES;
 				ENABLE_PREVIEWS = YES;
 				INFOPLIST_FILE = FreeAPS/Resources/Info.plist;
 				INFOPLIST_FILE = FreeAPS/Resources/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 16.2;
+				IPHONEOS_DEPLOYMENT_TARGET = 17.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(inherited)",
 					"@executable_path/Frameworks",
 					"@executable_path/Frameworks",
@@ -4004,7 +4004,7 @@
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				SWIFT_VERSION = 5.0;
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = 4;
 				TARGETED_DEVICE_FAMILY = 4;
-				WATCHOS_DEPLOYMENT_TARGET = 8.0;
+				WATCHOS_DEPLOYMENT_TARGET = 10;
 			};
 			};
 			name = Debug;
 			name = Debug;
 		};
 		};
@@ -4041,7 +4041,7 @@
 				SWIFT_OBJC_BRIDGING_HEADER = "Model/Classes+Properties/FreeAPSWatch-Bridging-Header.h";
 				SWIFT_OBJC_BRIDGING_HEADER = "Model/Classes+Properties/FreeAPSWatch-Bridging-Header.h";
 				SWIFT_VERSION = 5.0;
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = 4;
 				TARGETED_DEVICE_FAMILY = 4;
-				WATCHOS_DEPLOYMENT_TARGET = 8.0;
+				WATCHOS_DEPLOYMENT_TARGET = 10;
 			};
 			};
 			name = Release;
 			name = Release;
 		};
 		};
@@ -4081,7 +4081,7 @@
 				SWIFT_EMIT_LOC_STRINGS = YES;
 				SWIFT_EMIT_LOC_STRINGS = YES;
 				SWIFT_VERSION = 5.0;
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = 4;
 				TARGETED_DEVICE_FAMILY = 4;
-				WATCHOS_DEPLOYMENT_TARGET = 8.0;
+				WATCHOS_DEPLOYMENT_TARGET = 10;
 			};
 			};
 			name = Debug;
 			name = Debug;
 		};
 		};
@@ -4121,7 +4121,7 @@
 				SWIFT_EMIT_LOC_STRINGS = YES;
 				SWIFT_EMIT_LOC_STRINGS = YES;
 				SWIFT_VERSION = 5.0;
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = 4;
 				TARGETED_DEVICE_FAMILY = 4;
-				WATCHOS_DEPLOYMENT_TARGET = 8.0;
+				WATCHOS_DEPLOYMENT_TARGET = 10;
 			};
 			};
 			name = Release;
 			name = Release;
 		};
 		};
@@ -4132,7 +4132,7 @@
 				CODE_SIGN_STYLE = Automatic;
 				CODE_SIGN_STYLE = Automatic;
 				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
 				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
 				INFOPLIST_FILE = FreeAPSTests/Info.plist;
 				INFOPLIST_FILE = FreeAPSTests/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 16.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 17;
 				LD_RUNPATH_SEARCH_PATHS = (
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(inherited)",
 					"@executable_path/Frameworks",
 					"@executable_path/Frameworks",
@@ -4153,7 +4153,7 @@
 				CODE_SIGN_STYLE = Automatic;
 				CODE_SIGN_STYLE = Automatic;
 				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
 				DEVELOPMENT_TEAM = "$(DEVELOPER_TEAM)";
 				INFOPLIST_FILE = FreeAPSTests/Info.plist;
 				INFOPLIST_FILE = FreeAPSTests/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 16.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 17;
 				LD_RUNPATH_SEARCH_PATHS = (
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(inherited)",
 					"@executable_path/Frameworks",
 					"@executable_path/Frameworks",
@@ -4183,7 +4183,7 @@
 				INFOPLIST_FILE = LiveActivity/Info.plist;
 				INFOPLIST_FILE = LiveActivity/Info.plist;
 				INFOPLIST_KEY_CFBundleDisplayName = LiveActivity;
 				INFOPLIST_KEY_CFBundleDisplayName = LiveActivity;
 				INFOPLIST_KEY_NSHumanReadableCopyright = "";
 				INFOPLIST_KEY_NSHumanReadableCopyright = "";
-				IPHONEOS_DEPLOYMENT_TARGET = 16.2;
+				IPHONEOS_DEPLOYMENT_TARGET = 17;
 				LD_RUNPATH_SEARCH_PATHS = (
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(inherited)",
 					"@executable_path/Frameworks",
 					"@executable_path/Frameworks",
@@ -4217,7 +4217,7 @@
 				INFOPLIST_FILE = LiveActivity/Info.plist;
 				INFOPLIST_FILE = LiveActivity/Info.plist;
 				INFOPLIST_KEY_CFBundleDisplayName = LiveActivity;
 				INFOPLIST_KEY_CFBundleDisplayName = LiveActivity;
 				INFOPLIST_KEY_NSHumanReadableCopyright = "";
 				INFOPLIST_KEY_NSHumanReadableCopyright = "";
-				IPHONEOS_DEPLOYMENT_TARGET = 16.2;
+				IPHONEOS_DEPLOYMENT_TARGET = 17;
 				LD_RUNPATH_SEARCH_PATHS = (
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(inherited)",
 					"@executable_path/Frameworks",
 					"@executable_path/Frameworks",

+ 22 - 21
FreeAPS/Sources/Modules/AlgorithmAdvancedSettings/AlgorithmAdvancedSettingsStateModel.swift

@@ -1,28 +1,29 @@
 import Combine
 import Combine
+import Observation
 import SwiftUI
 import SwiftUI
 
 
 extension AlgorithmAdvancedSettings {
 extension AlgorithmAdvancedSettings {
-    final class StateModel: BaseStateModel<Provider> {
-        @Injected() var settings: SettingsManager!
-        @Injected() var storage: FileStorage!
-        @Injected() var nightscout: NightscoutManager!
-
-        @Published var units: GlucoseUnits = .mgdL
-
-        @Published var maxDailySafetyMultiplier: Decimal = 3
-        @Published var currentBasalSafetyMultiplier: Decimal = 4
-        @Published var useCustomPeakTime: Bool = false
-        @Published var insulinPeakTime: Decimal = 75
-        @Published var skipNeutralTemps: Bool = false
-        @Published var unsuspendIfNoTemp: Bool = false
-        @Published var suspendZerosIOB: Bool = false
-        @Published var min5mCarbimpact: Decimal = 8
-        @Published var autotuneISFAdjustmentFraction: Decimal = 1.0
-        @Published var remainingCarbsFraction: Decimal = 1.0
-        @Published var remainingCarbsCap: Decimal = 90
-        @Published var noisyCGMTargetMultiplier: Decimal = 1.3
-
-        @Published var insulinActionCurve: Decimal = 6
+    @Observable final class StateModel: BaseStateModel<Provider> {
+        @ObservationIgnored @Injected() var settings: SettingsManager!
+        @ObservationIgnored @Injected() var storage: FileStorage!
+        @ObservationIgnored @Injected() var nightscout: NightscoutManager!
+
+        var units: GlucoseUnits = .mgdL
+
+        var maxDailySafetyMultiplier: Decimal = 3
+        var currentBasalSafetyMultiplier: Decimal = 4
+        var useCustomPeakTime: Bool = false
+        var insulinPeakTime: Decimal = 75
+        var skipNeutralTemps: Bool = false
+        var unsuspendIfNoTemp: Bool = false
+        var suspendZerosIOB: Bool = false
+        var min5mCarbimpact: Decimal = 8
+        var autotuneISFAdjustmentFraction: Decimal = 1.0
+        var remainingCarbsFraction: Decimal = 1.0
+        var remainingCarbsCap: Decimal = 90
+        var noisyCGMTargetMultiplier: Decimal = 1.3
+
+        var insulinActionCurve: Decimal = 6
 
 
         var preferences: Preferences {
         var preferences: Preferences {
             settingsManager.preferences
             settingsManager.preferences

+ 1 - 1
FreeAPS/Sources/Modules/AlgorithmAdvancedSettings/View/AlgorithmAdvancedSettingsRootView.swift

@@ -4,7 +4,7 @@ import Swinject
 extension AlgorithmAdvancedSettings {
 extension AlgorithmAdvancedSettings {
     struct RootView: BaseView {
     struct RootView: BaseView {
         let resolver: Resolver
         let resolver: Resolver
-        @StateObject var state = StateModel()
+        @State var state = StateModel()
         @State private var shouldDisplayHint: Bool = false
         @State private var shouldDisplayHint: Bool = false
         @State var hintDetent = PresentationDetent.large
         @State var hintDetent = PresentationDetent.large
         @State var selectedVerboseHint: String?
         @State var selectedVerboseHint: String?

+ 8 - 7
FreeAPS/Sources/Modules/AutosensSettings/AutosensSettingsStateModel.swift

@@ -1,15 +1,16 @@
+import Observation
 import SwiftUI
 import SwiftUI
 
 
 extension AutosensSettings {
 extension AutosensSettings {
-    final class StateModel: BaseStateModel<Provider> {
-        @Injected() var settings: SettingsManager!
-        @Injected() var storage: FileStorage!
+    @Observable final class StateModel: BaseStateModel<Provider> {
+        @ObservationIgnored @Injected() var settings: SettingsManager!
+        @ObservationIgnored @Injected() var storage: FileStorage!
 
 
-        @Published var units: GlucoseUnits = .mgdL
+        var units: GlucoseUnits = .mgdL
 
 
-        @Published var autosensMax: Decimal = 1.2
-        @Published var autosensMin: Decimal = 0.7
-        @Published var rewindResetsAutosens: Bool = true
+        var autosensMax: Decimal = 1.2
+        var autosensMin: Decimal = 0.7
+        var rewindResetsAutosens: Bool = true
 
 
         var preferences: Preferences {
         var preferences: Preferences {
             settingsManager.preferences
             settingsManager.preferences

+ 1 - 1
FreeAPS/Sources/Modules/AutosensSettings/View/AutosensSettingsRootView.swift

@@ -4,7 +4,7 @@ import Swinject
 extension AutosensSettings {
 extension AutosensSettings {
     struct RootView: BaseView {
     struct RootView: BaseView {
         let resolver: Resolver
         let resolver: Resolver
-        @StateObject var state = StateModel()
+        @State var state = StateModel()
         @State private var shouldDisplayHint: Bool = false
         @State private var shouldDisplayHint: Bool = false
         @State var hintDetent = PresentationDetent.large
         @State var hintDetent = PresentationDetent.large
         @State var selectedVerboseHint: String?
         @State var selectedVerboseHint: String?

+ 9 - 8
FreeAPS/Sources/Modules/BasalProfileEditor/BasalProfileEditorStateModel.swift

@@ -1,14 +1,15 @@
+import Observation
 import SwiftUI
 import SwiftUI
 
 
 extension BasalProfileEditor {
 extension BasalProfileEditor {
-    final class StateModel: BaseStateModel<Provider> {
-        @Injected() private var nightscout: NightscoutManager!
-
-        @Published var syncInProgress: Bool = false
-        @Published var initialItems: [Item] = []
-        @Published var items: [Item] = []
-        @Published var total: Decimal = 0.0
-        @Published var showAlert: Bool = false
+    @Observable final class StateModel: BaseStateModel<Provider> {
+        @ObservationIgnored @Injected() private var nightscout: NightscoutManager!
+
+        var syncInProgress: Bool = false
+        var initialItems: [Item] = []
+        var items: [Item] = []
+        var total: Decimal = 0.0
+        var showAlert: Bool = false
 
 
         let timeValues = stride(from: 0.0, to: 1.days.timeInterval, by: 30.minutes.timeInterval).map { $0 }
         let timeValues = stride(from: 0.0, to: 1.days.timeInterval, by: 30.minutes.timeInterval).map { $0 }
 
 

+ 1 - 1
FreeAPS/Sources/Modules/BasalProfileEditor/View/BasalProfileEditorRootView.swift

@@ -4,7 +4,7 @@ import Swinject
 extension BasalProfileEditor {
 extension BasalProfileEditor {
     struct RootView: BaseView {
     struct RootView: BaseView {
         let resolver: Resolver
         let resolver: Resolver
-        @StateObject var state = StateModel()
+        @State var state = StateModel()
         @State private var editMode = EditMode.inactive
         @State private var editMode = EditMode.inactive
 
 
         @Environment(\.colorScheme) var colorScheme
         @Environment(\.colorScheme) var colorScheme

+ 101 - 102
FreeAPS/Sources/Modules/Bolus/BolusStateModel.swift

@@ -2,116 +2,115 @@ import Combine
 import CoreData
 import CoreData
 import Foundation
 import Foundation
 import LoopKit
 import LoopKit
+import Observation
 import SwiftUI
 import SwiftUI
 import Swinject
 import Swinject
 
 
 extension Bolus {
 extension Bolus {
-    final class StateModel: BaseStateModel<Provider> {
-        @Injected() var unlockmanager: UnlockManager!
-        @Injected() var apsManager: APSManager!
-        @Injected() var broadcaster: Broadcaster!
-        @Injected() var pumpHistoryStorage: PumpHistoryStorage!
-        // added for bolus calculator
-        @Injected() var settings: SettingsManager!
-        @Injected() var nsManager: NightscoutManager!
-        @Injected() var carbsStorage: CarbsStorage!
-        @Injected() var glucoseStorage: GlucoseStorage!
-        @Injected() var determinationStorage: DeterminationStorage!
-
-        @Published var lowGlucose: Decimal = 70
-        @Published var highGlucose: Decimal = 180
-
-        @Published var predictions: Predictions?
-        @Published var amount: Decimal = 0
-        @Published var insulinRecommended: Decimal = 0
-        @Published var insulinRequired: Decimal = 0
-        @Published var units: GlucoseUnits = .mgdL
-        @Published var threshold: Decimal = 0
-        @Published var maxBolus: Decimal = 0
+    @Observable final class StateModel: BaseStateModel<Provider> {
+        @ObservationIgnored @Injected() var unlockmanager: UnlockManager!
+        @ObservationIgnored @Injected() var apsManager: APSManager!
+        @ObservationIgnored @Injected() var broadcaster: Broadcaster!
+        @ObservationIgnored @Injected() var pumpHistoryStorage: PumpHistoryStorage!
+        @ObservationIgnored @Injected() var settings: SettingsManager!
+        @ObservationIgnored @Injected() var nsManager: NightscoutManager!
+        @ObservationIgnored @Injected() var carbsStorage: CarbsStorage!
+        @ObservationIgnored @Injected() var glucoseStorage: GlucoseStorage!
+        @ObservationIgnored @Injected() var determinationStorage: DeterminationStorage!
+
+        var lowGlucose: Decimal = 70
+        var highGlucose: Decimal = 180
+
+        var predictions: Predictions?
+        var amount: Decimal = 0
+        var insulinRecommended: Decimal = 0
+        var insulinRequired: Decimal = 0
+        var units: GlucoseUnits = .mgdL
+        var threshold: Decimal = 0
+        var maxBolus: Decimal = 0
         var maxExternal: Decimal { maxBolus * 3 }
         var maxExternal: Decimal { maxBolus * 3 }
-        @Published var errorString: Decimal = 0
-        @Published var evBG: Decimal = 0
-        @Published var insulin: Decimal = 0
-        @Published var isf: Decimal = 0
-        @Published var error: Bool = false
-        @Published var minGuardBG: Decimal = 0
-        @Published var minDelta: Decimal = 0
-        @Published var expectedDelta: Decimal = 0
-        @Published var minPredBG: Decimal = 0
-        @Published var waitForSuggestion: Bool = false
-        @Published var carbRatio: Decimal = 0
-
-        @Published var addButtonPressed: Bool = false
+        var errorString: Decimal = 0
+        var evBG: Decimal = 0
+        var insulin: Decimal = 0
+        var isf: Decimal = 0
+        var error: Bool = false
+        var minGuardBG: Decimal = 0
+        var minDelta: Decimal = 0
+        var expectedDelta: Decimal = 0
+        var minPredBG: Decimal = 0
+        var waitForSuggestion: Bool = false
+        var carbRatio: Decimal = 0
+
+        var addButtonPressed: Bool = false
 
 
         var waitForSuggestionInitial: Bool = false
         var waitForSuggestionInitial: Bool = false
 
 
-        // added for bolus calculator
-        @Published var target: Decimal = 0
-        @Published var cob: Int16 = 0
-        @Published var iob: Decimal = 0
-
-        @Published var currentBG: Decimal = 0
-        @Published var fifteenMinInsulin: Decimal = 0
-        @Published var deltaBG: Decimal = 0
-        @Published var targetDifferenceInsulin: Decimal = 0
-        @Published var targetDifference: Decimal = 0
-        @Published var wholeCob: Decimal = 0
-        @Published var wholeCobInsulin: Decimal = 0
-        @Published var iobInsulinReduction: Decimal = 0
-        @Published var wholeCalc: Decimal = 0
-        @Published var insulinCalculated: Decimal = 0
-        @Published var fraction: Decimal = 0
-        @Published var basal: Decimal = 0
-        @Published var fattyMeals: Bool = false
-        @Published var fattyMealFactor: Decimal = 0
-        @Published var useFattyMealCorrectionFactor: Bool = false
-        @Published var displayPresets: Bool = true
-
-        @Published var currentBasal: Decimal = 0
-        @Published var currentCarbRatio: Decimal = 0
-        @Published var currentBGTarget: Decimal = 0
-        @Published var currentISF: Decimal = 0
-
-        @Published var sweetMeals: Bool = false
-        @Published var sweetMealFactor: Decimal = 0
-        @Published var useSuperBolus: Bool = false
-        @Published var superBolusInsulin: Decimal = 0
-
-        @Published var meal: [CarbsEntry]?
-        @Published var carbs: Decimal = 0
-        @Published var fat: Decimal = 0
-        @Published var protein: Decimal = 0
-        @Published var note: String = ""
-
-        @Published var date = Date()
-
-        @Published var carbsRequired: Decimal?
-        @Published var useFPUconversion: Bool = false
-        @Published var dish: String = ""
-        @Published var selection: MealPresetStored?
-        @Published var summation: [String] = []
-        @Published var maxCarbs: Decimal = 0
-        @Published var maxFat: Decimal = 0
-        @Published var maxProtein: Decimal = 0
-
-        @Published var id_: String = ""
-        @Published var summary: String = ""
-
-        @Published var externalInsulin: Bool = false
-        @Published var showInfo: Bool = false
-        @Published var glucoseFromPersistence: [GlucoseStored] = []
-        @Published var determination: [OrefDetermination] = []
-        @Published var preprocessedData: [(id: UUID, forecast: Forecast, forecastValue: ForecastValue)] = []
-        @Published var predictionsForChart: Predictions?
-        @Published var simulatedDetermination: Determination?
-        @Published var determinationObjectIDs: [NSManagedObjectID] = []
-
-        @Published var minForecast: [Int] = []
-        @Published var maxForecast: [Int] = []
-        @Published var minCount: Int = 12 // count of Forecasts drawn in 5 min distances, i.e. 12 means a min of 1 hour
-        @Published var forecastDisplayType: ForecastDisplayType = .cone
-        @Published var isSmoothingEnabled: Bool = false
-        @Published var stops: [Gradient.Stop] = []
+        var target: Decimal = 0
+        var cob: Int16 = 0
+        var iob: Decimal = 0
+
+        var currentBG: Decimal = 0
+        var fifteenMinInsulin: Decimal = 0
+        var deltaBG: Decimal = 0
+        var targetDifferenceInsulin: Decimal = 0
+        var targetDifference: Decimal = 0
+        var wholeCob: Decimal = 0
+        var wholeCobInsulin: Decimal = 0
+        var iobInsulinReduction: Decimal = 0
+        var wholeCalc: Decimal = 0
+        var insulinCalculated: Decimal = 0
+        var fraction: Decimal = 0
+        var basal: Decimal = 0
+        var fattyMeals: Bool = false
+        var fattyMealFactor: Decimal = 0
+        var useFattyMealCorrectionFactor: Bool = false
+        var displayPresets: Bool = true
+
+        var currentBasal: Decimal = 0
+        var currentCarbRatio: Decimal = 0
+        var currentBGTarget: Decimal = 0
+        var currentISF: Decimal = 0
+
+        var sweetMeals: Bool = false
+        var sweetMealFactor: Decimal = 0
+        var useSuperBolus: Bool = false
+        var superBolusInsulin: Decimal = 0
+
+        var meal: [CarbsEntry]?
+        var carbs: Decimal = 0
+        var fat: Decimal = 0
+        var protein: Decimal = 0
+        var note: String = ""
+
+        var date = Date()
+
+        var carbsRequired: Decimal?
+        var useFPUconversion: Bool = false
+        var dish: String = ""
+        var selection: MealPresetStored?
+        var summation: [String] = []
+        var maxCarbs: Decimal = 0
+        var maxFat: Decimal = 0
+        var maxProtein: Decimal = 0
+
+        var id_: String = ""
+        var summary: String = ""
+
+        var externalInsulin: Bool = false
+        var showInfo: Bool = false
+        var glucoseFromPersistence: [GlucoseStored] = []
+        var determination: [OrefDetermination] = []
+        var preprocessedData: [(id: UUID, forecast: Forecast, forecastValue: ForecastValue)] = []
+        var predictionsForChart: Predictions?
+        var simulatedDetermination: Determination?
+        var determinationObjectIDs: [NSManagedObjectID] = []
+
+        var minForecast: [Int] = []
+        var maxForecast: [Int] = []
+        var minCount: Int = 12 // count of Forecasts drawn in 5 min distances, i.e. 12 means a min of 1 hour
+        var forecastDisplayType: ForecastDisplayType = .cone
+        var isSmoothingEnabled: Bool = false
+        var stops: [Gradient.Stop] = []
 
 
         let now = Date.now
         let now = Date.now
 
 

+ 1 - 0
FreeAPS/Sources/Modules/Bolus/View/AddMealPresetView.swift

@@ -1,5 +1,6 @@
 import CoreData
 import CoreData
 import Foundation
 import Foundation
+import Observation
 import SwiftUI
 import SwiftUI
 
 
 struct AddMealPresetView: View {
 struct AddMealPresetView: View {

+ 2 - 2
FreeAPS/Sources/Modules/Bolus/View/BolusRootView.swift

@@ -17,7 +17,7 @@ extension Bolus {
 
 
         let resolver: Resolver
         let resolver: Resolver
 
 
-        @StateObject var state = StateModel()
+        @State var state = StateModel()
 
 
         @State private var showPresetSheet = false
         @State private var showPresetSheet = false
         @State private var autofocus: Bool = true
         @State private var autofocus: Bool = true
@@ -171,7 +171,7 @@ extension Bolus {
                 VStack {
                 VStack {
                     Form {
                     Form {
                         Section {
                         Section {
-                            ForeCastChart(state: state, units: $state.units)
+                            ForeCastChart(state: state)
                                 .padding(.vertical)
                                 .padding(.vertical)
                         }.listRowBackground(Color.chart)
                         }.listRowBackground(Color.chart)
 
 

+ 13 - 14
FreeAPS/Sources/Modules/Bolus/View/ForeCastChart.swift

@@ -4,9 +4,8 @@ import Foundation
 import SwiftUI
 import SwiftUI
 
 
 struct ForeCastChart: View {
 struct ForeCastChart: View {
-    @StateObject var state: Bolus.StateModel
+    var state: Bolus.StateModel
     @Environment(\.colorScheme) var colorScheme
     @Environment(\.colorScheme) var colorScheme
-    @Binding var units: GlucoseUnits
 
 
     @State private var startMarker = Date(timeIntervalSinceNow: -4 * 60 * 60)
     @State private var startMarker = Date(timeIntervalSinceNow: -4 * 60 * 60)
 
 
@@ -23,7 +22,7 @@ struct ForeCastChart: View {
         let formatter = NumberFormatter()
         let formatter = NumberFormatter()
         formatter.numberStyle = .decimal
         formatter.numberStyle = .decimal
 
 
-        if units == .mmolL {
+        if state.units == .mmolL {
             formatter.maximumFractionDigits = 1
             formatter.maximumFractionDigits = 1
             formatter.minimumFractionDigits = 1
             formatter.minimumFractionDigits = 1
             formatter.roundingMode = .halfUp
             formatter.roundingMode = .halfUp
@@ -81,11 +80,11 @@ struct ForeCastChart: View {
                 if let eventualBG = state.simulatedDetermination?.eventualBG {
                 if let eventualBG = state.simulatedDetermination?.eventualBG {
                     HStack {
                     HStack {
                         Text(
                         Text(
-                            units == .mgdL ? Decimal(eventualBG).description : eventualBG.formattedAsMmolL
+                            state.units == .mgdL ? Decimal(eventualBG).description : eventualBG.formattedAsMmolL
                         )
                         )
                         .font(.footnote)
                         .font(.footnote)
                         .foregroundStyle(.primary)
                         .foregroundStyle(.primary)
-                        Text("\(units.rawValue)")
+                        Text("\(state.units.rawValue)")
                             .font(.footnote)
                             .font(.footnote)
                             .foregroundStyle(.secondary)
                             .foregroundStyle(.secondary)
                     }
                     }
@@ -93,7 +92,7 @@ struct ForeCastChart: View {
                     Text("---")
                     Text("---")
                         .font(.footnote)
                         .font(.footnote)
                         .foregroundStyle(.primary)
                         .foregroundStyle(.primary)
-                    Text("\(units.rawValue)")
+                    Text("\(state.units.rawValue)")
                         .font(.footnote)
                         .font(.footnote)
                         .foregroundStyle(.secondary)
                         .foregroundStyle(.secondary)
                 }
                 }
@@ -115,7 +114,7 @@ struct ForeCastChart: View {
         .chartXAxis { forecastChartXAxis }
         .chartXAxis { forecastChartXAxis }
         .chartXScale(domain: startMarker ... endMarker)
         .chartXScale(domain: startMarker ... endMarker)
         .chartYAxis { forecastChartYAxis }
         .chartYAxis { forecastChartYAxis }
-        .chartYScale(domain: units == .mgdL ? 0 ... 300 : 0.asMmolL ... 300.asMmolL)
+        .chartYScale(domain: state.units == .mgdL ? 0 ... 300 : 0.asMmolL ... 300.asMmolL)
         .backport.chartForegroundStyleScale(state: state)
         .backport.chartForegroundStyleScale(state: state)
     }
     }
 
 
@@ -163,17 +162,17 @@ struct ForeCastChart: View {
 
 
                 // if distance between respective min and max is 0, provide a default range
                 // if distance between respective min and max is 0, provide a default range
                 if yMinMaxDelta == 0 {
                 if yMinMaxDelta == 0 {
-                    let yMinValue = units == .mgdL ? Decimal(state.minForecast[index] - 1) :
+                    let yMinValue = state.units == .mgdL ? Decimal(state.minForecast[index] - 1) :
                         Decimal(state.minForecast[index] - 1)
                         Decimal(state.minForecast[index] - 1)
                         .asMmolL
                         .asMmolL
-                    let yMaxValue = units == .mgdL ? Decimal(state.minForecast[index] + 1) :
+                    let yMaxValue = state.units == .mgdL ? Decimal(state.minForecast[index] + 1) :
                         Decimal(state.minForecast[index] + 1)
                         Decimal(state.minForecast[index] + 1)
                         .asMmolL
                         .asMmolL
 
 
                     AreaMark(
                     AreaMark(
                         x: .value("Time", xValue <= endMarker ? xValue : endMarker),
                         x: .value("Time", xValue <= endMarker ? xValue : endMarker),
-                        yStart: .value("Min Value", units == .mgdL ? yMinValue : yMinValue.asMmolL),
-                        yEnd: .value("Max Value", units == .mgdL ? yMaxValue : yMaxValue.asMmolL)
+                        yStart: .value("Min Value", state.units == .mgdL ? yMinValue : yMinValue.asMmolL),
+                        yEnd: .value("Max Value", state.units == .mgdL ? yMaxValue : yMaxValue.asMmolL)
                     )
                     )
                     .foregroundStyle(Color.blue.opacity(0.5))
                     .foregroundStyle(Color.blue.opacity(0.5))
                     .interpolationMethod(.catmullRom)
                     .interpolationMethod(.catmullRom)
@@ -184,8 +183,8 @@ struct ForeCastChart: View {
 
 
                     AreaMark(
                     AreaMark(
                         x: .value("Time", timeForIndex(Int32(index)) <= endMarker ? timeForIndex(Int32(index)) : endMarker),
                         x: .value("Time", timeForIndex(Int32(index)) <= endMarker ? timeForIndex(Int32(index)) : endMarker),
-                        yStart: .value("Min Value", units == .mgdL ? yMinValue : yMinValue.asMmolL),
-                        yEnd: .value("Max Value", units == .mgdL ? yMaxValue : yMaxValue.asMmolL)
+                        yStart: .value("Min Value", state.units == .mgdL ? yMinValue : yMinValue.asMmolL),
+                        yEnd: .value("Max Value", state.units == .mgdL ? yMaxValue : yMaxValue.asMmolL)
                     )
                     )
                     .foregroundStyle(Color.blue.opacity(0.5))
                     .foregroundStyle(Color.blue.opacity(0.5))
                     .interpolationMethod(.catmullRom)
                     .interpolationMethod(.catmullRom)
@@ -210,7 +209,7 @@ struct ForeCastChart: View {
                 ForEach(values.indices, id: \.self) { index in
                 ForEach(values.indices, id: \.self) { index in
                     LineMark(
                     LineMark(
                         x: .value("Time", timeForIndex(Int32(index))),
                         x: .value("Time", timeForIndex(Int32(index))),
-                        y: .value("Value", units == .mgdL ? Decimal(values[index]) : Decimal(values[index]).asMmolL)
+                        y: .value("Value", state.units == .mgdL ? Decimal(values[index]) : Decimal(values[index]).asMmolL)
                     )
                     )
                     .foregroundStyle(by: .value("Prediction Type", name))
                     .foregroundStyle(by: .value("Prediction Type", name))
                 }
                 }

+ 1 - 1
FreeAPS/Sources/Modules/Bolus/View/MealPresetView.swift

@@ -3,7 +3,7 @@ import Foundation
 import SwiftUI
 import SwiftUI
 
 
 struct MealPresetView: View {
 struct MealPresetView: View {
-    @StateObject var state: Bolus.StateModel
+    @Bindable var state: Bolus.StateModel
     @Environment(\.colorScheme) var colorScheme
     @Environment(\.colorScheme) var colorScheme
     @Environment(\.dismiss) var dismiss
     @Environment(\.dismiss) var dismiss
     @Environment(\.managedObjectContext) var moc
     @Environment(\.managedObjectContext) var moc

+ 1 - 1
FreeAPS/Sources/Modules/Bolus/View/PopupView.swift

@@ -1,7 +1,7 @@
 import SwiftUI
 import SwiftUI
 
 
 struct PopupView: View {
 struct PopupView: View {
-    @StateObject var state: Bolus.StateModel
+    var state: Bolus.StateModel
     @Environment(\.colorScheme) var colorScheme
     @Environment(\.colorScheme) var colorScheme
 
 
     private var fractionDigits: Int {
     private var fractionDigits: Int {

+ 10 - 10
FreeAPS/Sources/Modules/Calibrations/CalibrationsStateModel.swift

@@ -1,21 +1,21 @@
+import Observation
 import SwiftDate
 import SwiftDate
 import SwiftUI
 import SwiftUI
 
 
 extension Calibrations {
 extension Calibrations {
-    final class StateModel: BaseStateModel<Provider> {
-        @Injected() var glucoseStorage: GlucoseStorage!
-        @Injected() var calibrationService: CalibrationService!
+    @Observable final class StateModel: BaseStateModel<Provider> {
+        @ObservationIgnored @Injected() var glucoseStorage: GlucoseStorage!
+        @ObservationIgnored @Injected() var calibrationService: CalibrationService!
 
 
-        @Published var slope: Double = 1
-        @Published var intercept: Double = 1
-        @Published var newCalibration: Decimal = 0
-        @Published var calibrations: [Calibration] = []
-        @Published var calibrate: (Int) -> Double = { Double($0) }
-        @Published var items: [Item] = []
+        var slope: Double = 1
+        var intercept: Double = 1
+        var newCalibration: Decimal = 0
+        var calibrations: [Calibration] = []
+        var calibrate: (Int) -> Double = { Double($0) }
+        var items: [Item] = []
 
 
         var units: GlucoseUnits = .mgdL
         var units: GlucoseUnits = .mgdL
 
 
-        // TODO: - test if we need to use the viewContext here
         private let context = CoreDataStack.shared.newTaskContext()
         private let context = CoreDataStack.shared.newTaskContext()
 
 
         override func subscribe() {
         override func subscribe() {

+ 1 - 1
FreeAPS/Sources/Modules/Calibrations/View/CalibrationsChart.swift

@@ -1,7 +1,7 @@
 import SwiftUI
 import SwiftUI
 
 
 struct CalibrationsChart: View {
 struct CalibrationsChart: View {
-    @EnvironmentObject var state: Calibrations.StateModel
+    var state: Calibrations.StateModel
 
 
     private var dateFormatter: DateFormatter {
     private var dateFormatter: DateFormatter {
         let formatter = DateFormatter()
         let formatter = DateFormatter()

+ 2 - 2
FreeAPS/Sources/Modules/Calibrations/View/CalibrationsRootView.swift

@@ -4,7 +4,7 @@ import Swinject
 extension Calibrations {
 extension Calibrations {
     struct RootView: BaseView {
     struct RootView: BaseView {
         let resolver: Resolver
         let resolver: Resolver
-        @StateObject var state = StateModel()
+        @State var state = StateModel()
 
 
         @Environment(\.colorScheme) var colorScheme
         @Environment(\.colorScheme) var colorScheme
         var color: LinearGradient {
         var color: LinearGradient {
@@ -101,7 +101,7 @@ extension Calibrations {
 
 
                     if state.calibrations.isNotEmpty {
                     if state.calibrations.isNotEmpty {
                         Section(header: Text("Chart")) {
                         Section(header: Text("Chart")) {
-                            CalibrationsChart().environmentObject(state)
+                            CalibrationsChart(state: state)
                                 .frame(minHeight: geo.size.width)
                                 .frame(minHeight: geo.size.width)
                         }.listRowBackground(Color.chart)
                         }.listRowBackground(Color.chart)
                     }
                     }

+ 19 - 18
FreeAPS/Sources/Modules/DataTable/DataTableStateModel.swift

@@ -1,29 +1,30 @@
 import CoreData
 import CoreData
+import Observation
 import SwiftUI
 import SwiftUI
 
 
 extension DataTable {
 extension DataTable {
-    final class StateModel: BaseStateModel<Provider> {
-        @Injected() var broadcaster: Broadcaster!
-        @Injected() var apsManager: APSManager!
-        @Injected() var unlockmanager: UnlockManager!
-        @Injected() private var storage: FileStorage!
-        @Injected() var pumpHistoryStorage: PumpHistoryStorage!
-        @Injected() var glucoseStorage: GlucoseStorage!
-        @Injected() var healthKitManager: HealthKitManager!
-        @Injected() var carbsStorage: CarbsStorage!
+    @Observable final class StateModel: BaseStateModel<Provider> {
+        @ObservationIgnored @Injected() var broadcaster: Broadcaster!
+        @ObservationIgnored @Injected() var apsManager: APSManager!
+        @ObservationIgnored @Injected() var unlockmanager: UnlockManager!
+        @ObservationIgnored @Injected() private var storage: FileStorage!
+        @ObservationIgnored @Injected() var pumpHistoryStorage: PumpHistoryStorage!
+        @ObservationIgnored @Injected() var glucoseStorage: GlucoseStorage!
+        @ObservationIgnored @Injected() var healthKitManager: HealthKitManager!
+        @ObservationIgnored @Injected() var carbsStorage: CarbsStorage!
 
 
         let coredataContext = CoreDataStack.shared.newTaskContext()
         let coredataContext = CoreDataStack.shared.newTaskContext()
 
 
-        @Published var mode: Mode = .treatments
-        @Published var treatments: [Treatment] = []
-        @Published var glucose: [Glucose] = []
-        @Published var meals: [Treatment] = []
-        @Published var manualGlucose: Decimal = 0
-        @Published var maxBolus: Decimal = 0
-        @Published var waitForSuggestion: Bool = false
+        var mode: Mode = .treatments
+        var treatments: [Treatment] = []
+        var glucose: [Glucose] = []
+        var meals: [Treatment] = []
+        var manualGlucose: Decimal = 0
+        var maxBolus: Decimal = 0
+        var waitForSuggestion: Bool = false
 
 
-        @Published var insulinEntryDeleted: Bool = false
-        @Published var carbEntryDeleted: Bool = false
+        var insulinEntryDeleted: Bool = false
+        var carbEntryDeleted: Bool = false
 
 
         var units: GlucoseUnits = .mgdL
         var units: GlucoseUnits = .mgdL
 
 

+ 3 - 1
FreeAPS/Sources/Modules/DataTable/View/DataTableRootView.swift

@@ -5,7 +5,9 @@ import Swinject
 extension DataTable {
 extension DataTable {
     struct RootView: BaseView {
     struct RootView: BaseView {
         let resolver: Resolver
         let resolver: Resolver
-        @StateObject var state = StateModel()
+
+        @State var state = StateModel()
+
         @State private var isRemoveHistoryItemAlertPresented: Bool = false
         @State private var isRemoveHistoryItemAlertPresented: Bool = false
         @State private var alertTitle: String = ""
         @State private var alertTitle: String = ""
         @State private var alertMessage: String = ""
         @State private var alertMessage: String = ""

+ 13 - 12
FreeAPS/Sources/Modules/DynamicSettings/DynamicSettingsStateModel.swift

@@ -1,19 +1,20 @@
+import Observation
 import SwiftUI
 import SwiftUI
 
 
 extension DynamicSettings {
 extension DynamicSettings {
-    final class StateModel: BaseStateModel<Provider> {
-        @Injected() var settings: SettingsManager!
-        @Injected() var storage: FileStorage!
+    @Observable final class StateModel: BaseStateModel<Provider> {
+        @ObservationIgnored @Injected() var settings: SettingsManager!
+        @ObservationIgnored @Injected() var storage: FileStorage!
 
 
-        @Published var useNewFormula: Bool = false
-        @Published var enableDynamicCR: Bool = false
-        @Published var sigmoid: Bool = false
-        @Published var adjustmentFactor: Decimal = 0.8
-        @Published var adjustmentFactorSigmoid: Decimal = 0.5
-        @Published var weightPercentage: Decimal = 0.65
-        @Published var tddAdjBasal: Bool = false
-        @Published var threshold_setting: Decimal = 60
-        @Published var units: GlucoseUnits = .mgdL
+        var useNewFormula: Bool = false
+        var enableDynamicCR: Bool = false
+        var sigmoid: Bool = false
+        var adjustmentFactor: Decimal = 0.8
+        var adjustmentFactorSigmoid: Decimal = 0.5
+        var weightPercentage: Decimal = 0.65
+        var tddAdjBasal: Bool = false
+        var threshold_setting: Decimal = 60
+        var units: GlucoseUnits = .mgdL
 
 
         var preferences: Preferences {
         var preferences: Preferences {
             settingsManager.preferences
             settingsManager.preferences

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

@@ -4,7 +4,7 @@ import Swinject
 extension DynamicSettings {
 extension DynamicSettings {
     struct RootView: BaseView {
     struct RootView: BaseView {
         let resolver: Resolver
         let resolver: Resolver
-        @StateObject var state = StateModel()
+        @State var state = StateModel()
         @State private var shouldDisplayHint: Bool = false
         @State private var shouldDisplayHint: Bool = false
         @State var hintDetent = PresentationDetent.large
         @State var hintDetent = PresentationDetent.large
         @State var selectedVerboseHint: String?
         @State var selectedVerboseHint: String?

+ 3 - 5
FreeAPS/Sources/Modules/Home/HomeStateModel+Setup/ChartAxisSetup.swift

@@ -17,9 +17,7 @@ extension Home.StateModel {
                 let maxForecast = forecastValues.max()
                 let maxForecast = forecastValues.max()
 
 
                 // Ensure all values exist, otherwise set default values
                 // Ensure all values exist, otherwise set default values
-                guard let minGlucose = minGlucose, let maxGlucose = maxGlucose,
-                      let minForecast = minForecast, let maxForecast = maxForecast
-                else {
+                guard let minGlucose = minGlucose, let maxGlucose = maxGlucose else {
                     Task {
                     Task {
                         await self.updateChartBounds(minValue: 39, maxValue: 300)
                         await self.updateChartBounds(minValue: 39, maxValue: 300)
                     }
                     }
@@ -27,8 +25,8 @@ extension Home.StateModel {
                 }
                 }
 
 
                 // Adjust max forecast to be no more than 100 over max glucose
                 // Adjust max forecast to be no more than 100 over max glucose
-                let adjustedMaxForecast = min(maxForecast, maxGlucose + 100)
-                let minOverall = min(minGlucose, minForecast)
+                let adjustedMaxForecast = min(maxForecast ?? maxGlucose + 100, maxGlucose + 100)
+                let minOverall = min(minGlucose, minForecast ?? minGlucose)
                 let maxOverall = max(maxGlucose, adjustedMaxForecast)
                 let maxOverall = max(maxGlucose, adjustedMaxForecast)
 
 
                 // Update the chart bounds on the main thread
                 // Update the chart bounds on the main thread

+ 8 - 3
FreeAPS/Sources/Modules/Home/HomeStateModel+Setup/ForecastSetup.swift

@@ -80,19 +80,24 @@ extension Home.StateModel {
             return
             return
         }
         }
 
 
+        // Update minCount on the Main Thread
         minCount = max(12, allForecastValues.map(\.count).min() ?? 0)
         minCount = max(12, allForecastValues.map(\.count).min() ?? 0)
-        guard minCount > 0 else { return }
+
+        // Safely read minCount for use inside the detached task
+        let localMinCount = minCount
+
+        guard localMinCount > 0 else { return }
 
 
         // Copy allForecastValues to a local constant for thread safety
         // Copy allForecastValues to a local constant for thread safety
         let localAllForecastValues = allForecastValues
         let localAllForecastValues = allForecastValues
 
 
         // Calculate min and max forecast values in a background task
         // Calculate min and max forecast values in a background task
         let (minResult, maxResult) = await Task.detached {
         let (minResult, maxResult) = await Task.detached {
-            let minForecast = (0 ..< self.minCount).map { index in
+            let minForecast = (0 ..< localMinCount).map { index in
                 localAllForecastValues.compactMap { $0.indices.contains(index) ? $0[index] : nil }.min() ?? 0
                 localAllForecastValues.compactMap { $0.indices.contains(index) ? $0[index] : nil }.min() ?? 0
             }
             }
 
 
-            let maxForecast = (0 ..< self.minCount).map { index in
+            let maxForecast = (0 ..< localMinCount).map { index in
                 localAllForecastValues.compactMap { $0.indices.contains(index) ? $0[index] : nil }.max() ?? 0
                 localAllForecastValues.compactMap { $0.indices.contains(index) ? $0[index] : nil }.max() ?? 0
             }
             }
 
 

+ 84 - 83
FreeAPS/Sources/Modules/Home/HomeStateModel.swift

@@ -2,100 +2,101 @@ import Combine
 import CoreData
 import CoreData
 import Foundation
 import Foundation
 import LoopKitUI
 import LoopKitUI
+import Observation
 import SwiftDate
 import SwiftDate
 import SwiftUI
 import SwiftUI
 
 
 extension Home {
 extension Home {
-    final class StateModel: BaseStateModel<Provider> {
-        @Injected() var broadcaster: Broadcaster!
-        @Injected() var apsManager: APSManager!
-        @Injected() var fetchGlucoseManager: FetchGlucoseManager!
-        @Injected() var nightscoutManager: NightscoutManager!
-        @Injected() var determinationStorage: DeterminationStorage!
-        @Injected() var glucoseStorage: GlucoseStorage!
-        @Injected() var carbsStorage: CarbsStorage!
+    @Observable final class StateModel: BaseStateModel<Provider> {
+        @ObservationIgnored @Injected() var broadcaster: Broadcaster!
+        @ObservationIgnored @Injected() var apsManager: APSManager!
+        @ObservationIgnored @Injected() var fetchGlucoseManager: FetchGlucoseManager!
+        @ObservationIgnored @Injected() var nightscoutManager: NightscoutManager!
+        @ObservationIgnored @Injected() var determinationStorage: DeterminationStorage!
+        @ObservationIgnored @Injected() var glucoseStorage: GlucoseStorage!
+        @ObservationIgnored @Injected() var carbsStorage: CarbsStorage!
         private let timer = DispatchTimer(timeInterval: 5)
         private let timer = DispatchTimer(timeInterval: 5)
         private(set) var filteredHours = 24
         private(set) var filteredHours = 24
-        @Published var manualGlucose: [BloodGlucose] = []
-        @Published var announcement: [Announcement] = []
-        @Published var uploadStats = false
-        @Published var recentGlucose: BloodGlucose?
-        @Published var maxBasal: Decimal = 2
-        @Published var autotunedBasalProfile: [BasalProfileEntry] = []
-        @Published var basalProfile: [BasalProfileEntry] = []
-        @Published var tempTargets: [TempTarget] = []
-        @Published var timerDate = Date()
-        @Published var closedLoop = false
-        @Published var pumpSuspended = false
-        @Published var isLooping = false
-        @Published var statusTitle = ""
-        @Published var lastLoopDate: Date = .distantPast
-        @Published var battery: Battery?
-        @Published var reservoir: Decimal?
-        @Published var pumpName = ""
-        @Published var pumpExpiresAtDate: Date?
-        @Published var tempTarget: TempTarget?
-        @Published var setupPump = false
-        @Published var errorMessage: String? = nil
-        @Published var errorDate: Date? = nil
-        @Published var bolusProgress: Decimal?
-        @Published var eventualBG: Int?
-        @Published var allowManualTemp = false
-        @Published var units: GlucoseUnits = .mgdL
-        @Published var pumpDisplayState: PumpDisplayState?
-        @Published var alarm: GlucoseAlarm?
-        @Published var manualTempBasal = false
-        @Published var isSmoothingEnabled = false
-        @Published var maxValue: Decimal = 1.2
-        @Published var lowGlucose: Decimal = 70
-        @Published var highGlucose: Decimal = 180
-        @Published var overrideUnit: Bool = false
-        @Published var displayXgridLines: Bool = false
-        @Published var displayYgridLines: Bool = false
-        @Published var thresholdLines: Bool = false
-        @Published var timeZone: TimeZone?
-        @Published var hours: Int16 = 6
-        @Published var totalBolus: Decimal = 0
-        @Published var isStatusPopupPresented: Bool = false
-        @Published var isLegendPresented: Bool = false
-        @Published var legendSheetDetent = PresentationDetent.large
-        @Published var totalInsulinDisplayType: TotalInsulinDisplayType = .totalDailyDose
-        @Published var roundedTotalBolus: String = ""
-        @Published var selectedTab: Int = 0
-        @Published var waitForSuggestion: Bool = false
-        @Published var glucoseFromPersistence: [GlucoseStored] = []
-        @Published var latestTwoGlucoseValues: [GlucoseStored] = []
-        @Published var carbsFromPersistence: [CarbEntryStored] = []
-        @Published var fpusFromPersistence: [CarbEntryStored] = []
-        @Published var determinationsFromPersistence: [OrefDetermination] = []
-        @Published var enactedAndNonEnactedDeterminations: [OrefDetermination] = []
-        @Published var insulinFromPersistence: [PumpEventStored] = []
-        @Published var tempBasals: [PumpEventStored] = []
-        @Published var suspensions: [PumpEventStored] = []
-        @Published var batteryFromPersistence: [OpenAPS_Battery] = []
-        @Published var lastPumpBolus: PumpEventStored?
-        @Published var overrides: [OverrideStored] = []
-        @Published var overrideRunStored: [OverrideRunStored] = []
-        @Published var isOverrideCancelled: Bool = false
-        @Published var preprocessedData: [(id: UUID, forecast: Forecast, forecastValue: ForecastValue)] = []
-        @Published var pumpStatusHighlightMessage: String? = nil
-        @Published var cgmAvailable: Bool = false
-        @Published var showCarbsRequiredBadge: Bool = true
+        var manualGlucose: [BloodGlucose] = []
+        var announcement: [Announcement] = []
+        var uploadStats = false
+        var recentGlucose: BloodGlucose?
+        var maxBasal: Decimal = 2
+        var autotunedBasalProfile: [BasalProfileEntry] = []
+        var basalProfile: [BasalProfileEntry] = []
+        var tempTargets: [TempTarget] = []
+        var timerDate = Date()
+        var closedLoop = false
+        var pumpSuspended = false
+        var isLooping = false
+        var statusTitle = ""
+        var lastLoopDate: Date = .distantPast
+        var battery: Battery?
+        var reservoir: Decimal?
+        var pumpName = ""
+        var pumpExpiresAtDate: Date?
+        var tempTarget: TempTarget?
+        var setupPump = false
+        var errorMessage: String?
+        var errorDate: Date?
+        var bolusProgress: Decimal?
+        var eventualBG: Int?
+        var allowManualTemp = false
+        var units: GlucoseUnits = .mgdL
+        var pumpDisplayState: PumpDisplayState?
+        var alarm: GlucoseAlarm?
+        var manualTempBasal = false
+        var isSmoothingEnabled = false
+        var maxValue: Decimal = 1.2
+        var lowGlucose: Decimal = 70
+        var highGlucose: Decimal = 180
+        var overrideUnit: Bool = false
+        var displayXgridLines: Bool = false
+        var displayYgridLines: Bool = false
+        var thresholdLines: Bool = false
+        var timeZone: TimeZone?
+        var hours: Int16 = 6
+        var totalBolus: Decimal = 0
+        var isStatusPopupPresented: Bool = false
+        var isLegendPresented: Bool = false
+        var legendSheetDetent = PresentationDetent.large
+        var totalInsulinDisplayType: TotalInsulinDisplayType = .totalDailyDose
+        var roundedTotalBolus: String = ""
+        var selectedTab: Int = 0
+        var waitForSuggestion: Bool = false
+        var glucoseFromPersistence: [GlucoseStored] = []
+        var latestTwoGlucoseValues: [GlucoseStored] = []
+        var carbsFromPersistence: [CarbEntryStored] = []
+        var fpusFromPersistence: [CarbEntryStored] = []
+        var determinationsFromPersistence: [OrefDetermination] = []
+        var enactedAndNonEnactedDeterminations: [OrefDetermination] = []
+        var insulinFromPersistence: [PumpEventStored] = []
+        var tempBasals: [PumpEventStored] = []
+        var suspensions: [PumpEventStored] = []
+        var batteryFromPersistence: [OpenAPS_Battery] = []
+        var lastPumpBolus: PumpEventStored?
+        var overrides: [OverrideStored] = []
+        var overrideRunStored: [OverrideRunStored] = []
+        var isOverrideCancelled: Bool = false
+        var preprocessedData: [(id: UUID, forecast: Forecast, forecastValue: ForecastValue)] = []
+        var pumpStatusHighlightMessage: String?
+        var cgmAvailable: Bool = false
+        var showCarbsRequiredBadge: Bool = true
         private(set) var setupPumpType: PumpConfig.PumpType = .minimed
         private(set) var setupPumpType: PumpConfig.PumpType = .minimed
 
 
-        @Published var minForecast: [Int] = []
-        @Published var maxForecast: [Int] = []
-        @Published var minCount: Int = 12 // count of Forecasts drawn in 5 min distances, i.e. 12 means a min of 1 hour
-        @Published var forecastDisplayType: ForecastDisplayType = .cone
+        var minForecast: [Int] = []
+        var maxForecast: [Int] = []
+        var minCount: Int = 12 // count of Forecasts drawn in 5 min distances, i.e. 12 means a min of 1 hour
+        var forecastDisplayType: ForecastDisplayType = .cone
 
 
-        @Published var minYAxisValue: Decimal = 39
-        @Published var maxYAxisValue: Decimal = 300
+        var minYAxisValue: Decimal = 39
+        var maxYAxisValue: Decimal = 300
 
 
-        @Published var minValueCobChart: Decimal = 0
-        @Published var maxValueCobChart: Decimal = 20
+        var minValueCobChart: Decimal = 0
+        var maxValueCobChart: Decimal = 20
 
 
-        @Published var minValueIobChart: Decimal = 0
-        @Published var maxValueIobChart: Decimal = 5
+        var minValueIobChart: Decimal = 0
+        var maxValueIobChart: Decimal = 5
 
 
         let taskContext = CoreDataStack.shared.newTaskContext()
         let taskContext = CoreDataStack.shared.newTaskContext()
         let glucoseFetchContext = CoreDataStack.shared.newTaskContext()
         let glucoseFetchContext = CoreDataStack.shared.newTaskContext()

+ 11 - 11
FreeAPS/Sources/Modules/Home/View/Chart/MainChartView.swift

@@ -7,17 +7,17 @@ let calendar = Calendar.current
 
 
 struct MainChartView: View {
 struct MainChartView: View {
     var geo: GeometryProxy
     var geo: GeometryProxy
-    @Binding var units: GlucoseUnits
-    @Binding var hours: Int
-    @Binding var tempTargets: [TempTarget]
-    @Binding var highGlucose: Decimal
-    @Binding var lowGlucose: Decimal
-    @Binding var screenHours: Int16
-    @Binding var displayXgridLines: Bool
-    @Binding var displayYgridLines: Bool
-    @Binding var thresholdLines: Bool
-
-    @StateObject var state: Home.StateModel
+    var units: GlucoseUnits
+    var hours: Int
+    var tempTargets: [TempTarget]
+    var highGlucose: Decimal
+    var lowGlucose: Decimal
+    var screenHours: Int16
+    var displayXgridLines: Bool
+    var displayYgridLines: Bool
+    var thresholdLines: Bool
+
+    var state: Home.StateModel
 
 
     @State var basalProfiles: [BasalProfile] = []
     @State var basalProfiles: [BasalProfile] = []
     @State var preparedTempBasals: [(start: Date, end: Date, rate: Double)] = []
     @State var preparedTempBasals: [(start: Date, end: Date, rate: Double)] = []

+ 8 - 8
FreeAPS/Sources/Modules/Home/View/Header/CurrentGlucoseView.swift

@@ -2,14 +2,14 @@ import CoreData
 import SwiftUI
 import SwiftUI
 
 
 struct CurrentGlucoseView: View {
 struct CurrentGlucoseView: View {
-    @Binding var timerDate: Date
-    @Binding var units: GlucoseUnits
-    @Binding var alarm: GlucoseAlarm?
-    @Binding var lowGlucose: Decimal
-    @Binding var highGlucose: Decimal
-    @Binding var cgmAvailable: Bool
-
-    var glucose: [GlucoseStored] // This contains the last two glucose values, no matter if its manual or a cgm reading
+    let timerDate: Date
+    let units: GlucoseUnits
+    let alarm: GlucoseAlarm?
+    let lowGlucose: Decimal
+    let highGlucose: Decimal
+    let cgmAvailable: Bool
+
+    let glucose: [GlucoseStored] // This contains the last two glucose values, no matter if its manual or a cgm reading
 
 
     @State private var rotationDegrees: Double = 0.0
     @State private var rotationDegrees: Double = 0.0
     @State private var angularGradient = AngularGradient(colors: [
     @State private var angularGradient = AngularGradient(colors: [

+ 6 - 6
FreeAPS/Sources/Modules/Home/View/Header/LoopView.swift

@@ -8,13 +8,13 @@ struct LoopView: View {
         static let lag: TimeInterval = 30
         static let lag: TimeInterval = 30
     }
     }
 
 
-    @Binding var closedLoop: Bool
-    @Binding var timerDate: Date
-    @Binding var isLooping: Bool
-    @Binding var lastLoopDate: Date
-    @Binding var manualTempBasal: Bool
+    let closedLoop: Bool
+    let timerDate: Date
+    let isLooping: Bool
+    let lastLoopDate: Date
+    let manualTempBasal: Bool
 
 
-    var determination: [OrefDetermination]
+    let determination: [OrefDetermination]
 
 
     private var dateFormatter: DateFormatter {
     private var dateFormatter: DateFormatter {
         let formatter = DateFormatter()
         let formatter = DateFormatter()

+ 7 - 7
FreeAPS/Sources/Modules/Home/View/Header/PumpView.swift

@@ -2,13 +2,13 @@ import CoreData
 import SwiftUI
 import SwiftUI
 
 
 struct PumpView: View {
 struct PumpView: View {
-    @Binding var reservoir: Decimal?
-    @Binding var name: String
-    @Binding var expiresAtDate: Date?
-    @Binding var timerDate: Date
-    @Binding var timeZone: TimeZone?
-    @Binding var pumpStatusHighlightMessage: String?
-    @Binding var battery: [OpenAPS_Battery]
+    let reservoir: Decimal?
+    let name: String
+    let expiresAtDate: Date?
+    let timerDate: Date
+    let timeZone: TimeZone?
+    let pumpStatusHighlightMessage: String?
+    let battery: [OpenAPS_Battery]
 
 
     @Environment(\.colorScheme) var colorScheme
     @Environment(\.colorScheme) var colorScheme
 
 

+ 28 - 30
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -8,7 +8,7 @@ extension Home {
     struct RootView: BaseView {
     struct RootView: BaseView {
         let resolver: Resolver
         let resolver: Resolver
 
 
-        @StateObject var state = StateModel()
+        @State var state = StateModel()
         @State var isStatusPopupPresented = false
         @State var isStatusPopupPresented = false
         @State var showCancelAlert = false
         @State var showCancelAlert = false
         @State var isMenuPresented = false
         @State var isMenuPresented = false
@@ -54,8 +54,6 @@ extension Home {
             sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)]
             sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)]
         ) var enactedSliderTT: FetchedResults<TempTargetsSlider>
         ) var enactedSliderTT: FetchedResults<TempTargetsSlider>
 
 
-        // TODO: end todo
-
         var bolusProgressFormatter: NumberFormatter {
         var bolusProgressFormatter: NumberFormatter {
             let formatter = NumberFormatter()
             let formatter = NumberFormatter()
             formatter.numberStyle = .decimal
             formatter.numberStyle = .decimal
@@ -130,12 +128,12 @@ extension Home {
 
 
         var glucoseView: some View {
         var glucoseView: some View {
             CurrentGlucoseView(
             CurrentGlucoseView(
-                timerDate: $state.timerDate,
-                units: $state.units,
-                alarm: $state.alarm,
-                lowGlucose: $state.lowGlucose,
-                highGlucose: $state.highGlucose,
-                cgmAvailable: $state.cgmAvailable,
+                timerDate: state.timerDate,
+                units: state.units,
+                alarm: state.alarm,
+                lowGlucose: state.lowGlucose,
+                highGlucose: state.highGlucose,
+                cgmAvailable: state.cgmAvailable,
                 glucose: state.latestTwoGlucoseValues
                 glucose: state.latestTwoGlucoseValues
             ).scaleEffect(0.9)
             ).scaleEffect(0.9)
                 .onTapGesture {
                 .onTapGesture {
@@ -150,13 +148,13 @@ extension Home {
 
 
         var pumpView: some View {
         var pumpView: some View {
             PumpView(
             PumpView(
-                reservoir: $state.reservoir,
-                name: $state.pumpName,
-                expiresAtDate: $state.pumpExpiresAtDate,
-                timerDate: $state.timerDate,
-                timeZone: $state.timeZone,
-                pumpStatusHighlightMessage: $state.pumpStatusHighlightMessage,
-                battery: $state.batteryFromPersistence
+                reservoir: state.reservoir,
+                name: state.pumpName,
+                expiresAtDate: state.pumpExpiresAtDate,
+                timerDate: state.timerDate,
+                timeZone: state.timeZone,
+                pumpStatusHighlightMessage: state.pumpStatusHighlightMessage,
+                battery: state.batteryFromPersistence
             ).onTapGesture {
             ).onTapGesture {
                 if state.pumpDisplayState == nil {
                 if state.pumpDisplayState == nil {
                     // shows user confirmation dialog with pump model choices, then proceeds to setup
                     // shows user confirmation dialog with pump model choices, then proceeds to setup
@@ -336,15 +334,15 @@ extension Home {
             ZStack {
             ZStack {
                 MainChartView(
                 MainChartView(
                     geo: geo,
                     geo: geo,
-                    units: $state.units,
-                    hours: .constant(state.filteredHours),
-                    tempTargets: $state.tempTargets,
-                    highGlucose: $state.highGlucose,
-                    lowGlucose: $state.lowGlucose,
-                    screenHours: $state.hours,
-                    displayXgridLines: $state.displayXgridLines,
-                    displayYgridLines: $state.displayYgridLines,
-                    thresholdLines: $state.thresholdLines,
+                    units: state.units,
+                    hours: state.filteredHours,
+                    tempTargets: state.tempTargets,
+                    highGlucose: state.highGlucose,
+                    lowGlucose: state.lowGlucose,
+                    screenHours: state.hours,
+                    displayXgridLines: state.displayXgridLines,
+                    displayYgridLines: state.displayYgridLines,
+                    thresholdLines: state.thresholdLines,
                     state: state
                     state: state
                 )
                 )
             }
             }
@@ -361,11 +359,11 @@ extension Home {
             VStack(alignment: .leading, spacing: 20) {
             VStack(alignment: .leading, spacing: 20) {
                 /// Loop view at bottomLeading
                 /// Loop view at bottomLeading
                 LoopView(
                 LoopView(
-                    closedLoop: $state.closedLoop,
-                    timerDate: $state.timerDate,
-                    isLooping: $state.isLooping,
-                    lastLoopDate: $state.lastLoopDate,
-                    manualTempBasal: $state.manualTempBasal,
+                    closedLoop: state.closedLoop,
+                    timerDate: state.timerDate,
+                    isLooping: state.isLooping,
+                    lastLoopDate: state.lastLoopDate,
+                    manualTempBasal: state.manualTempBasal,
                     determination: state.determinationsFromPersistence
                     determination: state.determinationsFromPersistence
                 ).onTapGesture {
                 ).onTapGesture {
                     state.isStatusPopupPresented = true
                     state.isStatusPopupPresented = true

+ 9 - 8
FreeAPS/Sources/Modules/ISFEditor/ISFEditorStateModel.swift

@@ -1,18 +1,19 @@
 import CoreData
 import CoreData
+import Observation
 import SwiftUI
 import SwiftUI
 
 
 extension ISFEditor {
 extension ISFEditor {
-    final class StateModel: BaseStateModel<Provider> {
-        @Injected() var determinationStorage: DeterminationStorage!
-        @Injected() private var nightscout: NightscoutManager!
+    @Observable final class StateModel: BaseStateModel<Provider> {
+        @ObservationIgnored @Injected() var determinationStorage: DeterminationStorage!
+        @ObservationIgnored @Injected() private var nightscout: NightscoutManager!
 
 
-        @Published var items: [Item] = []
-        @Published var initialItems: [Item] = []
-        @Published var shouldDisplaySaving: Bool = false
+        var items: [Item] = []
+        var initialItems: [Item] = []
+        var shouldDisplaySaving: Bool = false
         private(set) var autosensISF: Decimal?
         private(set) var autosensISF: Decimal?
         private(set) var autosensRatio: Decimal = 0
         private(set) var autosensRatio: Decimal = 0
-        @Published var autotune: Autotune?
-        @Published var determinationsFromPersistence: [OrefDetermination] = []
+        var autotune: Autotune?
+        var determinationsFromPersistence: [OrefDetermination] = []
 
 
         let context = CoreDataStack.shared.newTaskContext()
         let context = CoreDataStack.shared.newTaskContext()
         let viewContext = CoreDataStack.shared.persistentContainer.viewContext
         let viewContext = CoreDataStack.shared.persistentContainer.viewContext

+ 1 - 1
FreeAPS/Sources/Modules/ISFEditor/View/ISFEditorRootView.swift

@@ -4,7 +4,7 @@ import Swinject
 extension ISFEditor {
 extension ISFEditor {
     struct RootView: BaseView {
     struct RootView: BaseView {
         let resolver: Resolver
         let resolver: Resolver
-        @StateObject var state = StateModel()
+        @State var state = StateModel()
         @State private var editMode = EditMode.inactive
         @State private var editMode = EditMode.inactive
 
 
         @Environment(\.colorScheme) var colorScheme
         @Environment(\.colorScheme) var colorScheme

+ 5 - 4
FreeAPS/Sources/Modules/ManualTempBasal/ManualTempBasalStateModel.swift

@@ -1,10 +1,11 @@
+import Observation
 import SwiftUI
 import SwiftUI
 
 
 extension ManualTempBasal {
 extension ManualTempBasal {
-    final class StateModel: BaseStateModel<Provider> {
-        @Injected() var apsManager: APSManager!
-        @Published var rate: Decimal = 0
-        @Published var durationIndex = 0
+    @Observable final class StateModel: BaseStateModel<Provider> {
+        @ObservationIgnored @Injected() var apsManager: APSManager!
+        var rate: Decimal = 0
+        var durationIndex = 0
 
 
         let durationValues = stride(from: 30.0, to: 720.1, by: 30.0).map { $0 }
         let durationValues = stride(from: 30.0, to: 720.1, by: 30.0).map { $0 }
 
 

+ 1 - 1
FreeAPS/Sources/Modules/ManualTempBasal/View/ManualTempBasalRootView.swift

@@ -4,7 +4,7 @@ import Swinject
 extension ManualTempBasal {
 extension ManualTempBasal {
     struct RootView: BaseView {
     struct RootView: BaseView {
         let resolver: Resolver
         let resolver: Resolver
-        @StateObject var state = StateModel()
+        @State var state = StateModel()
 
 
         @Environment(\.colorScheme) var colorScheme
         @Environment(\.colorScheme) var colorScheme
         var color: LinearGradient {
         var color: LinearGradient {

+ 45 - 44
FreeAPS/Sources/Modules/OverrideConfig/OverrideStateModel.swift

@@ -1,55 +1,56 @@
 import CoreData
 import CoreData
+import Observation
 import SwiftUI
 import SwiftUI
 
 
 extension OverrideConfig {
 extension OverrideConfig {
-    final class StateModel: BaseStateModel<Provider> {
-        @Injected() var broadcaster: Broadcaster!
-        @Injected() var storage: TempTargetsStorage!
-        @Injected() var apsManager: APSManager!
-        @Injected() var overrideStorage: OverrideStorage!
-
-        @Published var overrideSliderPercentage: Double = 100
-        @Published var isEnabled = false
-        @Published var indefinite = true
-        @Published var overrideDuration: Decimal = 0
-        @Published var target: Decimal = 0
-        @Published var shouldOverrideTarget: Bool = false
-        @Published var smbIsOff: Bool = false
-        @Published var id = ""
-        @Published var overrideName: String = ""
-        @Published var isPreset: Bool = false
-        @Published var overridePresets: [OverrideStored] = []
-        @Published var advancedSettings: Bool = false
-        @Published var isfAndCr: Bool = true
-        @Published var isf: Bool = true
-        @Published var cr: Bool = true
-        @Published var smbIsAlwaysOff: Bool = false
-        @Published var start: Decimal = 0
-        @Published var end: Decimal = 23
-        @Published var smbMinutes: Decimal = 0
-        @Published var uamMinutes: Decimal = 0
-        @Published var defaultSmbMinutes: Decimal = 0
-        @Published var defaultUamMinutes: Decimal = 0
-        @Published var selectedTab: Tab = .overrides
-        @Published var activeOverrideName: String = ""
-        @Published var currentActiveOverride: OverrideStored?
-        @Published var showOverrideEditSheet = false
-        @Published var showInvalidTargetAlert = false
+    @Observable final class StateModel: BaseStateModel<Provider> {
+        @ObservationIgnored @Injected() var broadcaster: Broadcaster!
+        @ObservationIgnored @Injected() var storage: TempTargetsStorage!
+        @ObservationIgnored @Injected() var apsManager: APSManager!
+        @ObservationIgnored @Injected() var overrideStorage: OverrideStorage!
+
+        var overrideSliderPercentage: Double = 100
+        var isEnabled = false
+        var indefinite = true
+        var overrideDuration: Decimal = 0
+        var target: Decimal = 0
+        var shouldOverrideTarget: Bool = false
+        var smbIsOff: Bool = false
+        var id = ""
+        var overrideName: String = ""
+        var isPreset: Bool = false
+        var overridePresets: [OverrideStored] = []
+        var advancedSettings: Bool = false
+        var isfAndCr: Bool = true
+        var isf: Bool = true
+        var cr: Bool = true
+        var smbIsAlwaysOff: Bool = false
+        var start: Decimal = 0
+        var end: Decimal = 23
+        var smbMinutes: Decimal = 0
+        var uamMinutes: Decimal = 0
+        var defaultSmbMinutes: Decimal = 0
+        var defaultUamMinutes: Decimal = 0
+        var selectedTab: Tab = .overrides
+        var activeOverrideName: String = ""
+        var currentActiveOverride: OverrideStored?
+        var showOverrideEditSheet = false
+        var showInvalidTargetAlert = false
 
 
         var units: GlucoseUnits = .mgdL
         var units: GlucoseUnits = .mgdL
 
 
         // temp target stuff
         // temp target stuff
-        @Published var low: Decimal = 0
-        @Published var high: Decimal = 0
-        @Published var durationTT: Decimal = 0
-        @Published var date = Date()
-        @Published var newPresetName = ""
-        @Published var presetsTT: [TempTarget] = []
-        @Published var percentageTT = 100.0
-        @Published var maxValue: Decimal = 1.2
-        @Published var viewPercantage = false
-        @Published var hbt: Double = 160
-        @Published var didSaveSettings: Bool = false
+        var low: Decimal = 0
+        var high: Decimal = 0
+        var durationTT: Decimal = 0
+        var date = Date()
+        var newPresetName = ""
+        var presetsTT: [TempTarget] = []
+        var percentageTT = 100.0
+        var maxValue: Decimal = 1.2
+        var viewPercantage = false
+        var hbt: Double = 160
+        var didSaveSettings: Bool = false
 
 
         var alertMessage: String {
         var alertMessage: String {
             let target: String = units == .mgdL ? "70-270 mg/dl" : "4-15 mmol/l"
             let target: String = units == .mgdL ? "70-270 mg/dl" : "4-15 mmol/l"

+ 2 - 2
FreeAPS/Sources/Modules/OverrideConfig/View/EditOverrideForm.swift

@@ -5,7 +5,7 @@ struct EditOverrideForm: View {
     @ObservedObject var override: OverrideStored
     @ObservedObject var override: OverrideStored
     @Environment(\.presentationMode) var presentationMode
     @Environment(\.presentationMode) var presentationMode
     @Environment(\.colorScheme) var colorScheme
     @Environment(\.colorScheme) var colorScheme
-    @StateObject var state: OverrideConfig.StateModel
+    @Bindable var state: OverrideConfig.StateModel
 
 
     @State private var name: String
     @State private var name: String
     @State private var percentage: Double
     @State private var percentage: Double
@@ -30,7 +30,7 @@ struct EditOverrideForm: View {
 
 
     init(overrideToEdit: OverrideStored, state: OverrideConfig.StateModel) {
     init(overrideToEdit: OverrideStored, state: OverrideConfig.StateModel) {
         override = overrideToEdit
         override = overrideToEdit
-        _state = StateObject(wrappedValue: state)
+        _state = Bindable(wrappedValue: state)
         _name = State(initialValue: overrideToEdit.name ?? "")
         _name = State(initialValue: overrideToEdit.name ?? "")
         _percentage = State(initialValue: overrideToEdit.percentage)
         _percentage = State(initialValue: overrideToEdit.percentage)
         _indefinite = State(initialValue: overrideToEdit.indefinite)
         _indefinite = State(initialValue: overrideToEdit.indefinite)

+ 1 - 1
FreeAPS/Sources/Modules/OverrideConfig/View/OverrideRootView.swift

@@ -6,7 +6,7 @@ extension OverrideConfig {
     struct RootView: BaseView {
     struct RootView: BaseView {
         let resolver: Resolver
         let resolver: Resolver
 
 
-        @StateObject var state = StateModel()
+        @State var state = StateModel()
 
 
         @State private var isEditing = false
         @State private var isEditing = false
         @State private var showOverrideCreationSheet = false
         @State private var showOverrideCreationSheet = false

+ 19 - 18
FreeAPS/Sources/Modules/SMBSettings/SMBSettingsStateModel.swift

@@ -1,26 +1,27 @@
+import Observation
 import SwiftUI
 import SwiftUI
 
 
 extension SMBSettings {
 extension SMBSettings {
-    final class StateModel: BaseStateModel<Provider> {
-        @Injected() var settings: SettingsManager!
-        @Injected() var storage: FileStorage!
+    @Observable final class StateModel: BaseStateModel<Provider> {
+        @ObservationIgnored @Injected() var settings: SettingsManager!
+        @ObservationIgnored @Injected() var storage: FileStorage!
 
 
-        @Published var units: GlucoseUnits = .mgdL
+        var units: GlucoseUnits = .mgdL
 
 
-        @Published var enableSMBAlways: Bool = false
-        @Published var maxDeltaBGthreshold: Decimal = 0.2
-        @Published var enableSMBWithCOB: Bool = false
-        @Published var enableSMBWithTemptarget: Bool = false
-        @Published var enableSMBAfterCarbs: Bool = false
-        @Published var allowSMBWithHighTemptarget: Bool = false
-        @Published var enableSMB_high_bg: Bool = false
-        @Published var enableSMB_high_bg_target: Decimal = 100
-        @Published var maxSMBBasalMinutes: Decimal = 30
-        @Published var smbDeliveryRatio: Decimal = 0.5
-        @Published var smbInterval: Decimal = 3
-        @Published var bolusIncrement: Decimal = 0.1 // get this from pump, dafuq?: Bool = false
-        @Published var enableUAM: Bool = false
-        @Published var maxUAMSMBBasalMinutes: Decimal = 30
+        var enableSMBAlways: Bool = false
+        var maxDeltaBGthreshold: Decimal = 0.2
+        var enableSMBWithCOB: Bool = false
+        var enableSMBWithTemptarget: Bool = false
+        var enableSMBAfterCarbs: Bool = false
+        var allowSMBWithHighTemptarget: Bool = false
+        var enableSMB_high_bg: Bool = false
+        var enableSMB_high_bg_target: Decimal = 100
+        var maxSMBBasalMinutes: Decimal = 30
+        var smbDeliveryRatio: Decimal = 0.5
+        var smbInterval: Decimal = 3
+        var bolusIncrement: Decimal = 0.1 // get this from pump, dafuq?: Bool = false
+        var enableUAM: Bool = false
+        var maxUAMSMBBasalMinutes: Decimal = 30
 
 
         var preferences: Preferences {
         var preferences: Preferences {
             settingsManager.preferences
             settingsManager.preferences

+ 1 - 1
FreeAPS/Sources/Modules/SMBSettings/View/SMBSettingsRootView.swift

@@ -4,7 +4,7 @@ import Swinject
 extension SMBSettings {
 extension SMBSettings {
     struct RootView: BaseView {
     struct RootView: BaseView {
         let resolver: Resolver
         let resolver: Resolver
-        @StateObject var state = StateModel()
+        @State var state = StateModel()
         @State private var shouldDisplayHint: Bool = false
         @State private var shouldDisplayHint: Bool = false
         @State var hintDetent = PresentationDetent.large
         @State var hintDetent = PresentationDetent.large
         @State var selectedVerboseHint: String?
         @State var selectedVerboseHint: String?

+ 5 - 4
FreeAPS/Sources/Modules/Snooze/SnoozeStateModel.swift

@@ -1,11 +1,12 @@
+import Observation
 import SwiftUI
 import SwiftUI
 
 
 extension Snooze {
 extension Snooze {
-    final class StateModel: BaseStateModel<Provider> {
-        @Persisted(key: "UserNotificationsManager.snoozeUntilDate") var snoozeUntilDate: Date = .distantPast
-        @Injected() var glucoseStogare: GlucoseStorage!
+    @Observable final class StateModel: BaseStateModel<Provider> {
+        @ObservationIgnored @Persisted(key: "UserNotificationsManager.snoozeUntilDate") var snoozeUntilDate: Date = .distantPast
+        @ObservationIgnored @Injected() var glucoseStogare: GlucoseStorage!
 
 
-        @Published var alarm: GlucoseAlarm?
+        var alarm: GlucoseAlarm?
 
 
         override func subscribe() {
         override func subscribe() {
             alarm = glucoseStogare.alarm
             alarm = glucoseStogare.alarm

+ 1 - 1
FreeAPS/Sources/Modules/Snooze/View/SnoozeRootView.swift

@@ -5,7 +5,7 @@ import Swinject
 extension Snooze {
 extension Snooze {
     struct RootView: BaseView {
     struct RootView: BaseView {
         let resolver: Resolver
         let resolver: Resolver
-        @StateObject var state = StateModel()
+        @State var state = StateModel()
 
 
         @Environment(\.colorScheme) var colorScheme
         @Environment(\.colorScheme) var colorScheme
         var color: LinearGradient {
         var color: LinearGradient {

+ 10 - 9
FreeAPS/Sources/Modules/Stat/StatStateModel.swift

@@ -1,19 +1,20 @@
 import CoreData
 import CoreData
 import Foundation
 import Foundation
+import Observation
 import SwiftUI
 import SwiftUI
 import Swinject
 import Swinject
 
 
 extension Stat {
 extension Stat {
-    final class StateModel: BaseStateModel<Provider> {
-        @Injected() var settings: SettingsManager!
-        @Published var highLimit: Decimal = 10 / 0.0555
-        @Published var lowLimit: Decimal = 4 / 0.0555
-        @Published var overrideUnit: Bool = false
-        @Published var layingChart: Bool = false
-        @Published var units: GlucoseUnits = .mgdL
-        @Published var glucoseFromPersistence: [GlucoseStored] = []
+    @Observable final class StateModel: BaseStateModel<Provider> {
+        @ObservationIgnored @Injected() var settings: SettingsManager!
+        var highLimit: Decimal = 10 / 0.0555
+        var lowLimit: Decimal = 4 / 0.0555
+        var overrideUnit: Bool = false
+        var layingChart: Bool = false
+        var units: GlucoseUnits = .mgdL
+        var glucoseFromPersistence: [GlucoseStored] = []
 
 
-        @Published var selectedDuration: Duration = .Today
+        var selectedDuration: Duration = .Today
 
 
         private let context = CoreDataStack.shared.newTaskContext()
         private let context = CoreDataStack.shared.newTaskContext()
         private let viewContext = CoreDataStack.shared.persistentContainer.viewContext
         private let viewContext = CoreDataStack.shared.persistentContainer.viewContext

+ 16 - 16
FreeAPS/Sources/Modules/Stat/View/ChartsView.swift

@@ -4,13 +4,13 @@ import SwiftDate
 import SwiftUI
 import SwiftUI
 
 
 struct ChartsView: View {
 struct ChartsView: View {
-    @Binding var highLimit: Decimal
-    @Binding var lowLimit: Decimal
-    @Binding var units: GlucoseUnits
-    @Binding var overrideUnit: Bool
-    @Binding var standing: Bool
+    let highLimit: Decimal
+    let lowLimit: Decimal
+    let units: GlucoseUnits
+    let overrideUnit: Bool
+    let standing: Bool
 
 
-    var glucose: [GlucoseStored]
+    let glucose: [GlucoseStored]
 
 
     @State var headline: Color = .secondary
     @State var headline: Color = .secondary
 
 
@@ -36,18 +36,18 @@ struct ChartsView: View {
     }
     }
 
 
     init(
     init(
-        _ highLimit: Binding<Decimal>,
-        _ lowLimit: Binding<Decimal>,
-        _ units: Binding<GlucoseUnits>,
-        _ overrideUnit: Binding<Bool>,
-        _ standing: Binding<Bool>,
+        highLimit: Decimal,
+        lowLimit: Decimal,
+        units: GlucoseUnits,
+        overrideUnit: Bool,
+        standing: Bool,
         glucose: [GlucoseStored]
         glucose: [GlucoseStored]
     ) {
     ) {
-        _highLimit = highLimit
-        _lowLimit = lowLimit
-        _units = units
-        _overrideUnit = overrideUnit
-        _standing = standing
+        self.highLimit = highLimit
+        self.lowLimit = lowLimit
+        self.units = units
+        self.overrideUnit = overrideUnit
+        self.standing = standing
         self.glucose = glucose
         self.glucose = glucose
     }
     }
 
 

+ 46 - 46
FreeAPS/Sources/Modules/Stat/View/StatRootView.swift

@@ -7,7 +7,7 @@ import Swinject
 extension Stat {
 extension Stat {
     struct RootView: BaseView {
     struct RootView: BaseView {
         let resolver: Resolver
         let resolver: Resolver
-        @StateObject var state = StateModel()
+        @State var state = StateModel()
 
 
         @Environment(\.colorScheme) var colorScheme
         @Environment(\.colorScheme) var colorScheme
 
 
@@ -42,42 +42,42 @@ extension Stat {
                 case .Today:
                 case .Today:
                     StatsView(
                     StatsView(
                         filter: filter.today,
                         filter: filter.today,
-                        $state.highLimit,
-                        $state.lowLimit,
-                        $state.units,
-                        $state.overrideUnit
+                        highLimit: state.highLimit,
+                        lowLimit: state.lowLimit,
+                        units: state.units,
+                        overrideUnit: state.overrideUnit
                     )
                     )
                 case .Day:
                 case .Day:
                     StatsView(
                     StatsView(
                         filter: filter.day,
                         filter: filter.day,
-                        $state.highLimit,
-                        $state.lowLimit,
-                        $state.units,
-                        $state.overrideUnit
+                        highLimit: state.highLimit,
+                        lowLimit: state.lowLimit,
+                        units: state.units,
+                        overrideUnit: state.overrideUnit
                     )
                     )
                 case .Week:
                 case .Week:
                     StatsView(
                     StatsView(
                         filter: filter.week,
                         filter: filter.week,
-                        $state.highLimit,
-                        $state.lowLimit,
-                        $state.units,
-                        $state.overrideUnit
+                        highLimit: state.highLimit,
+                        lowLimit: state.lowLimit,
+                        units: state.units,
+                        overrideUnit: state.overrideUnit
                     )
                     )
                 case .Month:
                 case .Month:
                     StatsView(
                     StatsView(
                         filter: filter.month,
                         filter: filter.month,
-                        $state.highLimit,
-                        $state.lowLimit,
-                        $state.units,
-                        $state.overrideUnit
+                        highLimit: state.highLimit,
+                        lowLimit: state.lowLimit,
+                        units: state.units,
+                        overrideUnit: state.overrideUnit
                     )
                     )
                 case .Total:
                 case .Total:
                     StatsView(
                     StatsView(
                         filter: filter.total,
                         filter: filter.total,
-                        $state.highLimit,
-                        $state.lowLimit,
-                        $state.units,
-                        $state.overrideUnit
+                        highLimit: state.highLimit,
+                        lowLimit: state.lowLimit,
+                        units: state.units,
+                        overrideUnit: state.overrideUnit
                     )
                     )
                 }
                 }
             }
             }
@@ -87,47 +87,47 @@ extension Stat {
             switch state.selectedDuration {
             switch state.selectedDuration {
             case .Today:
             case .Today:
                 ChartsView(
                 ChartsView(
-                    $state.highLimit,
-                    $state.lowLimit,
-                    $state.units,
-                    $state.overrideUnit,
-                    $state.layingChart,
+                    highLimit: state.highLimit,
+                    lowLimit: state.lowLimit,
+                    units: state.units,
+                    overrideUnit: state.overrideUnit,
+                    standing: state.layingChart,
                     glucose: state.glucoseFromPersistence
                     glucose: state.glucoseFromPersistence
                 )
                 )
             case .Day:
             case .Day:
                 ChartsView(
                 ChartsView(
-                    $state.highLimit,
-                    $state.lowLimit,
-                    $state.units,
-                    $state.overrideUnit,
-                    $state.layingChart,
+                    highLimit: state.highLimit,
+                    lowLimit: state.lowLimit,
+                    units: state.units,
+                    overrideUnit: state.overrideUnit,
+                    standing: state.layingChart,
                     glucose: state.glucoseFromPersistence
                     glucose: state.glucoseFromPersistence
                 )
                 )
             case .Week:
             case .Week:
                 ChartsView(
                 ChartsView(
-                    $state.highLimit,
-                    $state.lowLimit,
-                    $state.units,
-                    $state.overrideUnit,
-                    $state.layingChart,
+                    highLimit: state.highLimit,
+                    lowLimit: state.lowLimit,
+                    units: state.units,
+                    overrideUnit: state.overrideUnit,
+                    standing: state.layingChart,
                     glucose: state.glucoseFromPersistence
                     glucose: state.glucoseFromPersistence
                 )
                 )
             case .Month:
             case .Month:
                 ChartsView(
                 ChartsView(
-                    $state.highLimit,
-                    $state.lowLimit,
-                    $state.units,
-                    $state.overrideUnit,
-                    $state.layingChart,
+                    highLimit: state.highLimit,
+                    lowLimit: state.lowLimit,
+                    units: state.units,
+                    overrideUnit: state.overrideUnit,
+                    standing: state.layingChart,
                     glucose: state.glucoseFromPersistence
                     glucose: state.glucoseFromPersistence
                 )
                 )
             case .Total:
             case .Total:
                 ChartsView(
                 ChartsView(
-                    $state.highLimit,
-                    $state.lowLimit,
-                    $state.units,
-                    $state.overrideUnit,
-                    $state.layingChart,
+                    highLimit: state.highLimit,
+                    lowLimit: state.lowLimit,
+                    units: state.units,
+                    overrideUnit: state.overrideUnit,
+                    standing: state.layingChart,
                     glucose: state.glucoseFromPersistence
                     glucose: state.glucoseFromPersistence
                 )
                 )
             }
             }

+ 12 - 12
FreeAPS/Sources/Modules/Stat/View/StatsView.swift

@@ -8,10 +8,10 @@ struct StatsView: View {
 
 
     @State var headline: Color = .secondary
     @State var headline: Color = .secondary
 
 
-    @Binding var highLimit: Decimal
-    @Binding var lowLimit: Decimal
-    @Binding var units: GlucoseUnits
-    @Binding var overrideUnit: Bool
+    var highLimit: Decimal
+    var lowLimit: Decimal
+    var units: GlucoseUnits
+    var overrideUnit: Bool
 
 
     private let conversionFactor = 0.0555
     private let conversionFactor = 0.0555
 
 
@@ -27,10 +27,10 @@ struct StatsView: View {
 
 
     init(
     init(
         filter: NSDate,
         filter: NSDate,
-        _ highLimit: Binding<Decimal>,
-        _ lowLimit: Binding<Decimal>,
-        _ units: Binding<GlucoseUnits>,
-        _ overrideUnit: Binding<Bool>
+        highLimit: Decimal,
+        lowLimit: Decimal,
+        units: GlucoseUnits,
+        overrideUnit: Bool
     ) {
     ) {
         _fetchRequest = FetchRequest<LoopStatRecord>(
         _fetchRequest = FetchRequest<LoopStatRecord>(
             sortDescriptors: [NSSortDescriptor(key: "start", ascending: false)],
             sortDescriptors: [NSSortDescriptor(key: "start", ascending: false)],
@@ -42,10 +42,10 @@ struct StatsView: View {
             predicate: NSPredicate(format: "glucose > 0 AND date > %@", filter)
             predicate: NSPredicate(format: "glucose > 0 AND date > %@", filter)
         )
         )
 
 
-        _highLimit = highLimit
-        _lowLimit = lowLimit
-        _units = units
-        _overrideUnit = overrideUnit
+        self.highLimit = highLimit
+        self.lowLimit = lowLimit
+        self.units = units
+        self.overrideUnit = overrideUnit
     }
     }
 
 
     var loops: some View {
     var loops: some View {