Browse Source

Refactor preferences to make them publishable

Deniz Cengiz 1 năm trước cách đây
mục cha
commit
e4c00c14ef

+ 218 - 1
FreeAPS/Sources/Models/Preferences.swift

@@ -1,6 +1,6 @@
 import Foundation
 import Foundation
 
 
-struct Preferences: JSON {
+struct Preferences: JSON, Equatable {
     var maxIOB: Decimal = 0
     var maxIOB: Decimal = 0
     var maxDailySafetyMultiplier: Decimal = 3
     var maxDailySafetyMultiplier: Decimal = 3
     var currentBasalSafetyMultiplier: Decimal = 4
     var currentBasalSafetyMultiplier: Decimal = 4
@@ -120,3 +120,220 @@ enum InsulinCurve: String, JSON, Identifiable, CaseIterable {
 
 
     var id: InsulinCurve { self }
     var id: InsulinCurve { self }
 }
 }
+
+extension Preferences: Decodable {
+    init(from decoder: Decoder) throws {
+        let container = try decoder.container(keyedBy: CodingKeys.self)
+        var preferences = Preferences()
+
+        if let maxIOB = try? container.decode(Decimal.self, forKey: .maxIOB) {
+            preferences.maxIOB = maxIOB
+        }
+
+        if let maxDailySafetyMultiplier = try? container.decode(Decimal.self, forKey: .maxDailySafetyMultiplier) {
+            preferences.maxDailySafetyMultiplier = maxDailySafetyMultiplier
+        }
+
+        if let currentBasalSafetyMultiplier = try? container.decode(Decimal.self, forKey: .currentBasalSafetyMultiplier) {
+            preferences.currentBasalSafetyMultiplier = currentBasalSafetyMultiplier
+        }
+
+        if let autosensMax = try? container.decode(Decimal.self, forKey: .autosensMax) {
+            preferences.autosensMax = autosensMax
+        }
+
+        if let autosensMin = try? container.decode(Decimal.self, forKey: .autosensMin) {
+            preferences.autosensMin = autosensMin
+        }
+
+        if let smbDeliveryRatio = try? container.decode(Decimal.self, forKey: .smbDeliveryRatio) {
+            preferences.smbDeliveryRatio = smbDeliveryRatio
+        }
+
+        if let rewindResetsAutosens = try? container.decode(Bool.self, forKey: .rewindResetsAutosens) {
+            preferences.rewindResetsAutosens = rewindResetsAutosens
+        }
+
+        if let highTemptargetRaisesSensitivity = try? container.decode(Bool.self, forKey: .highTemptargetRaisesSensitivity) {
+            preferences.highTemptargetRaisesSensitivity = highTemptargetRaisesSensitivity
+        }
+
+        if let lowTemptargetLowersSensitivity = try? container.decode(Bool.self, forKey: .lowTemptargetLowersSensitivity) {
+            preferences.lowTemptargetLowersSensitivity = lowTemptargetLowersSensitivity
+        }
+
+        if let sensitivityRaisesTarget = try? container.decode(Bool.self, forKey: .sensitivityRaisesTarget) {
+            preferences.sensitivityRaisesTarget = sensitivityRaisesTarget
+        }
+
+        if let resistanceLowersTarget = try? container.decode(Bool.self, forKey: .resistanceLowersTarget) {
+            preferences.resistanceLowersTarget = resistanceLowersTarget
+        }
+
+        if let advTargetAdjustments = try? container.decode(Bool.self, forKey: .advTargetAdjustments) {
+            preferences.advTargetAdjustments = advTargetAdjustments
+        }
+
+        if let exerciseMode = try? container.decode(Bool.self, forKey: .exerciseMode) {
+            preferences.exerciseMode = exerciseMode
+        }
+
+        if let halfBasalExerciseTarget = try? container.decode(Decimal.self, forKey: .halfBasalExerciseTarget) {
+            preferences.halfBasalExerciseTarget = halfBasalExerciseTarget
+        }
+
+        if let maxCOB = try? container.decode(Decimal.self, forKey: .maxCOB) {
+            preferences.maxCOB = maxCOB
+        }
+
+        if let wideBGTargetRange = try? container.decode(Bool.self, forKey: .wideBGTargetRange) {
+            preferences.wideBGTargetRange = wideBGTargetRange
+        }
+
+        if let skipNeutralTemps = try? container.decode(Bool.self, forKey: .skipNeutralTemps) {
+            preferences.skipNeutralTemps = skipNeutralTemps
+        }
+
+        if let unsuspendIfNoTemp = try? container.decode(Bool.self, forKey: .unsuspendIfNoTemp) {
+            preferences.unsuspendIfNoTemp = unsuspendIfNoTemp
+        }
+
+        if let min5mCarbimpact = try? container.decode(Decimal.self, forKey: .min5mCarbimpact) {
+            preferences.min5mCarbimpact = min5mCarbimpact
+        }
+
+        if let autotuneISFAdjustmentFraction = try? container.decode(Decimal.self, forKey: .autotuneISFAdjustmentFraction) {
+            preferences.autotuneISFAdjustmentFraction = autotuneISFAdjustmentFraction
+        }
+
+        if let remainingCarbsFraction = try? container.decode(Decimal.self, forKey: .remainingCarbsFraction) {
+            preferences.remainingCarbsFraction = remainingCarbsFraction
+        }
+
+        if let remainingCarbsCap = try? container.decode(Decimal.self, forKey: .remainingCarbsCap) {
+            preferences.remainingCarbsCap = remainingCarbsCap
+        }
+
+        if let enableUAM = try? container.decode(Bool.self, forKey: .enableUAM) {
+            preferences.enableUAM = enableUAM
+        }
+
+        if let a52RiskEnable = try? container.decode(Bool.self, forKey: .a52RiskEnable) {
+            preferences.a52RiskEnable = a52RiskEnable
+        }
+
+        if let enableSMBWithCOB = try? container.decode(Bool.self, forKey: .enableSMBWithCOB) {
+            preferences.enableSMBWithCOB = enableSMBWithCOB
+        }
+
+        if let enableSMBWithTemptarget = try? container.decode(Bool.self, forKey: .enableSMBWithTemptarget) {
+            preferences.enableSMBWithTemptarget = enableSMBWithTemptarget
+        }
+
+        if let enableSMBAlways = try? container.decode(Bool.self, forKey: .enableSMBAlways) {
+            preferences.enableSMBAlways = enableSMBAlways
+        }
+
+        if let enableSMBAfterCarbs = try? container.decode(Bool.self, forKey: .enableSMBAfterCarbs) {
+            preferences.enableSMBAfterCarbs = enableSMBAfterCarbs
+        }
+
+        if let allowSMBWithHighTemptarget = try? container.decode(Bool.self, forKey: .allowSMBWithHighTemptarget) {
+            preferences.allowSMBWithHighTemptarget = allowSMBWithHighTemptarget
+        }
+
+        if let maxSMBBasalMinutes = try? container.decode(Decimal.self, forKey: .maxSMBBasalMinutes) {
+            preferences.maxSMBBasalMinutes = maxSMBBasalMinutes
+        }
+
+        if let maxUAMSMBBasalMinutes = try? container.decode(Decimal.self, forKey: .maxUAMSMBBasalMinutes) {
+            preferences.maxUAMSMBBasalMinutes = maxUAMSMBBasalMinutes
+        }
+
+        if let smbInterval = try? container.decode(Decimal.self, forKey: .smbInterval) {
+            preferences.smbInterval = smbInterval
+        }
+
+        if let bolusIncrement = try? container.decode(Decimal.self, forKey: .bolusIncrement) {
+            preferences.bolusIncrement = bolusIncrement
+        }
+
+        if let curve = try? container.decode(InsulinCurve.self, forKey: .curve) {
+            preferences.curve = curve
+        }
+
+        if let useCustomPeakTime = try? container.decode(Bool.self, forKey: .useCustomPeakTime) {
+            preferences.useCustomPeakTime = useCustomPeakTime
+        }
+
+        if let insulinPeakTime = try? container.decode(Decimal.self, forKey: .insulinPeakTime) {
+            preferences.insulinPeakTime = insulinPeakTime
+        }
+
+        if let carbsReqThreshold = try? container.decode(Decimal.self, forKey: .carbsReqThreshold) {
+            preferences.carbsReqThreshold = carbsReqThreshold
+        }
+
+        if let noisyCGMTargetMultiplier = try? container.decode(Decimal.self, forKey: .noisyCGMTargetMultiplier) {
+            preferences.noisyCGMTargetMultiplier = noisyCGMTargetMultiplier
+        }
+
+        if let suspendZerosIOB = try? container.decode(Bool.self, forKey: .suspendZerosIOB) {
+            preferences.suspendZerosIOB = suspendZerosIOB
+        }
+
+        if let maxDeltaBGthreshold = try? container.decode(Decimal.self, forKey: .maxDeltaBGthreshold) {
+            preferences.maxDeltaBGthreshold = maxDeltaBGthreshold
+        }
+
+        if let adjustmentFactor = try? container.decode(Decimal.self, forKey: .adjustmentFactor) {
+            preferences.adjustmentFactor = adjustmentFactor
+        }
+
+        if let adjustmentFactorSigmoid = try? container.decode(Decimal.self, forKey: .adjustmentFactorSigmoid) {
+            preferences.adjustmentFactorSigmoid = adjustmentFactorSigmoid
+        }
+
+        if let sigmoid = try? container.decode(Bool.self, forKey: .sigmoid) {
+            preferences.sigmoid = sigmoid
+        }
+
+        if let enableDynamicCR = try? container.decode(Bool.self, forKey: .enableDynamicCR) {
+            preferences.enableDynamicCR = enableDynamicCR
+        }
+
+        if let useNewFormula = try? container.decode(Bool.self, forKey: .useNewFormula) {
+            preferences.useNewFormula = useNewFormula
+        }
+
+        if let useWeightedAverage = try? container.decode(Bool.self, forKey: .useWeightedAverage) {
+            preferences.useWeightedAverage = useWeightedAverage
+        }
+
+        if let weightPercentage = try? container.decode(Decimal.self, forKey: .weightPercentage) {
+            preferences.weightPercentage = weightPercentage
+        }
+
+        if let tddAdjBasal = try? container.decode(Bool.self, forKey: .tddAdjBasal) {
+            preferences.tddAdjBasal = tddAdjBasal
+        }
+
+        if let enableSMB_high_bg = try? container.decode(Bool.self, forKey: .enableSMB_high_bg) {
+            preferences.enableSMB_high_bg = enableSMB_high_bg
+        }
+
+        if let enableSMB_high_bg_target = try? container.decode(Decimal.self, forKey: .enableSMB_high_bg_target) {
+            preferences.enableSMB_high_bg_target = enableSMB_high_bg_target
+        }
+
+        if let threshold_setting = try? container.decode(Decimal.self, forKey: .threshold_setting) {
+            preferences.threshold_setting = threshold_setting
+        }
+
+        if let updateInterval = try? container.decode(Decimal.self, forKey: .updateInterval) {
+            preferences.updateInterval = updateInterval
+        }
+
+        self = preferences
+    }
+}

+ 11 - 1
FreeAPS/Sources/Modules/Adjustments/AdjustmentsStateModel.swift

@@ -79,6 +79,7 @@ extension Adjustments {
             setupNotification()
             setupNotification()
             setupSettings()
             setupSettings()
             broadcaster.register(SettingsObserver.self, observer: self)
             broadcaster.register(SettingsObserver.self, observer: self)
+            broadcaster.register(PreferencesObserver.self, observer: self)
 
 
             Task {
             Task {
                 await withTaskGroup(of: Void.self) { group in
                 await withTaskGroup(of: Void.self) { group in
@@ -1041,13 +1042,22 @@ extension Adjustments.StateModel {
     }
     }
 }
 }
 
 
-extension Adjustments.StateModel: SettingsObserver {
+extension Adjustments.StateModel: SettingsObserver, PreferencesObserver {
     func settingsDidChange(_: FreeAPSSettings) {
     func settingsDidChange(_: FreeAPSSettings) {
         units = settingsManager.settings.units
         units = settingsManager.settings.units
+        Task {
+            await getCurrentGlucoseTarget()
+        }
+    }
+
+    func preferencesDidChange(_: Preferences) {
         defaultSmbMinutes = settingsManager.preferences.maxSMBBasalMinutes
         defaultSmbMinutes = settingsManager.preferences.maxSMBBasalMinutes
         defaultUamMinutes = settingsManager.preferences.maxUAMSMBBasalMinutes
         defaultUamMinutes = settingsManager.preferences.maxUAMSMBBasalMinutes
         maxValue = settingsManager.preferences.autosensMax
         maxValue = settingsManager.preferences.autosensMax
         minValue = settingsManager.preferences.autosensMin
         minValue = settingsManager.preferences.autosensMin
+        settingHalfBasalTarget = settingsManager.preferences.halfBasalExerciseTarget
+        halfBasalTarget = settingsManager.preferences.halfBasalExerciseTarget
+        percentage = computeAdjustedPercentage()
         Task {
         Task {
             await getCurrentGlucoseTarget()
             await getCurrentGlucoseTarget()
         }
         }

+ 9 - 0
FreeAPS/Sources/Modules/AlgorithmAdvancedSettings/AlgorithmAdvancedSettingsProvider.swift

@@ -15,6 +15,15 @@ extension AlgorithmAdvancedSettings {
                 ?? PumpSettings(insulinActionCurve: 6.0, maxBolus: 10, maxBasal: 2)
                 ?? PumpSettings(insulinActionCurve: 6.0, maxBolus: 10, maxBasal: 2)
         }
         }
 
 
+        func savePreferences(_ preferences: Preferences) {
+            storage.save(preferences, as: OpenAPS.Settings.preferences)
+            processQueue.async {
+                self.broadcaster.notify(PreferencesObserver.self, on: self.processQueue) {
+                    $0.preferencesDidChange(preferences)
+                }
+            }
+        }
+
         func save(settings: PumpSettings) -> AnyPublisher<Void, Error> {
         func save(settings: PumpSettings) -> AnyPublisher<Void, Error> {
             func save(_ settings: PumpSettings) {
             func save(_ settings: PumpSettings) {
                 storage.save(settings, as: OpenAPS.Settings.settings)
                 storage.save(settings, as: OpenAPS.Settings.settings)

+ 32 - 67
FreeAPS/Sources/Modules/AlgorithmAdvancedSettings/AlgorithmAdvancedSettingsStateModel.swift

@@ -3,32 +3,28 @@ import Observation
 import SwiftUI
 import SwiftUI
 
 
 extension AlgorithmAdvancedSettings {
 extension AlgorithmAdvancedSettings {
-    @Observable final class StateModel: BaseStateModel<Provider> {
-        @ObservationIgnored @Injected() var settings: SettingsManager!
-        @ObservationIgnored @Injected() var storage: FileStorage!
-        @ObservationIgnored @Injected() var nightscout: NightscoutManager!
+    final class StateModel: BaseStateModel<Provider> {
+        @Injected() var settings: SettingsManager!
+        @Injected() var storage: FileStorage!
+        @Injected() var nightscout: NightscoutManager!
 
 
         var units: GlucoseUnits = .mgdL
         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
+        @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
 
 
         var insulinActionCurve: Decimal = 6
         var insulinActionCurve: Decimal = 6
 
 
-        var preferences: Preferences {
-            settingsManager.preferences
-        }
-
         var pumpSettings: PumpSettings {
         var pumpSettings: PumpSettings {
             provider.settings()
             provider.settings()
         }
         }
@@ -36,18 +32,22 @@ extension AlgorithmAdvancedSettings {
         override func subscribe() {
         override func subscribe() {
             units = settingsManager.settings.units
             units = settingsManager.settings.units
 
 
-            maxDailySafetyMultiplier = settings.preferences.maxDailySafetyMultiplier
-            currentBasalSafetyMultiplier = settings.preferences.currentBasalSafetyMultiplier
-            useCustomPeakTime = settings.preferences.useCustomPeakTime
-            insulinPeakTime = settings.preferences.insulinPeakTime
-            skipNeutralTemps = settings.preferences.skipNeutralTemps
-            unsuspendIfNoTemp = settings.preferences.unsuspendIfNoTemp
-            suspendZerosIOB = settings.preferences.suspendZerosIOB
-            min5mCarbimpact = settings.preferences.min5mCarbimpact
-            autotuneISFAdjustmentFraction = settings.preferences.autotuneISFAdjustmentFraction
-            remainingCarbsFraction = settings.preferences.remainingCarbsFraction
-            remainingCarbsCap = settings.preferences.remainingCarbsCap
-            noisyCGMTargetMultiplier = settings.preferences.noisyCGMTargetMultiplier
+            subscribePreferencesSetting(\.maxDailySafetyMultiplier, on: $maxDailySafetyMultiplier) {
+                maxDailySafetyMultiplier = $0 }
+            subscribePreferencesSetting(\.currentBasalSafetyMultiplier, on: $currentBasalSafetyMultiplier) {
+                currentBasalSafetyMultiplier = $0 }
+            subscribePreferencesSetting(\.useCustomPeakTime, on: $useCustomPeakTime) { useCustomPeakTime = $0 }
+            subscribePreferencesSetting(\.insulinPeakTime, on: $insulinPeakTime) { insulinPeakTime = $0 }
+            subscribePreferencesSetting(\.unsuspendIfNoTemp, on: $unsuspendIfNoTemp) { unsuspendIfNoTemp = $0 }
+            subscribePreferencesSetting(\.suspendZerosIOB, on: $suspendZerosIOB) { suspendZerosIOB = $0 }
+            subscribePreferencesSetting(\.suspendZerosIOB, on: $suspendZerosIOB) { suspendZerosIOB = $0 }
+            subscribePreferencesSetting(\.min5mCarbimpact, on: $min5mCarbimpact) { min5mCarbimpact = $0 }
+            subscribePreferencesSetting(\.autotuneISFAdjustmentFraction, on: $autotuneISFAdjustmentFraction) {
+                autotuneISFAdjustmentFraction = $0 }
+            subscribePreferencesSetting(\.remainingCarbsFraction, on: $remainingCarbsFraction) { remainingCarbsFraction = $0 }
+            subscribePreferencesSetting(\.remainingCarbsCap, on: $remainingCarbsCap) { remainingCarbsCap = $0 }
+            subscribePreferencesSetting(\.noisyCGMTargetMultiplier, on: $noisyCGMTargetMultiplier) {
+                noisyCGMTargetMultiplier = $0 }
 
 
             insulinActionCurve = pumpSettings.insulinActionCurve
             insulinActionCurve = pumpSettings.insulinActionCurve
         }
         }
@@ -56,42 +56,7 @@ extension AlgorithmAdvancedSettings {
             pumpSettings.insulinActionCurve == insulinActionCurve
             pumpSettings.insulinActionCurve == insulinActionCurve
         }
         }
 
 
-        var isSettingUnchanged: Bool {
-            preferences.maxDailySafetyMultiplier == maxDailySafetyMultiplier &&
-                preferences.currentBasalSafetyMultiplier == currentBasalSafetyMultiplier &&
-                preferences.useCustomPeakTime == useCustomPeakTime &&
-                preferences.insulinPeakTime == insulinPeakTime &&
-                preferences.skipNeutralTemps == skipNeutralTemps &&
-                preferences.unsuspendIfNoTemp == unsuspendIfNoTemp &&
-                preferences.suspendZerosIOB == suspendZerosIOB &&
-                preferences.min5mCarbimpact == min5mCarbimpact &&
-                preferences.autotuneISFAdjustmentFraction == autotuneISFAdjustmentFraction &&
-                preferences.remainingCarbsFraction == remainingCarbsFraction &&
-                preferences.remainingCarbsCap == remainingCarbsCap &&
-                preferences.noisyCGMTargetMultiplier == noisyCGMTargetMultiplier
-        }
-
         func saveIfChanged() {
         func saveIfChanged() {
-            if !isSettingUnchanged {
-                var newSettings = storage.retrieve(OpenAPS.Settings.preferences, as: Preferences.self) ?? Preferences()
-
-                newSettings.maxDailySafetyMultiplier = maxDailySafetyMultiplier
-                newSettings.currentBasalSafetyMultiplier = currentBasalSafetyMultiplier
-                newSettings.useCustomPeakTime = useCustomPeakTime
-                newSettings.insulinPeakTime = insulinPeakTime
-                newSettings.skipNeutralTemps = skipNeutralTemps
-                newSettings.unsuspendIfNoTemp = unsuspendIfNoTemp
-                newSettings.suspendZerosIOB = suspendZerosIOB
-                newSettings.min5mCarbimpact = min5mCarbimpact
-                newSettings.autotuneISFAdjustmentFraction = autotuneISFAdjustmentFraction
-                newSettings.remainingCarbsFraction = remainingCarbsFraction
-                newSettings.remainingCarbsCap = remainingCarbsCap
-                newSettings.noisyCGMTargetMultiplier = noisyCGMTargetMultiplier
-
-                newSettings.timestamp = Date()
-                storage.save(newSettings, as: OpenAPS.Settings.preferences)
-            }
-
             if !isPumpSettingUnchanged {
             if !isPumpSettingUnchanged {
                 let settings = PumpSettings(
                 let settings = PumpSettings(
                     insulinActionCurve: insulinActionCurve,
                     insulinActionCurve: insulinActionCurve,

+ 9 - 32
FreeAPS/Sources/Modules/AutosensSettings/AutosensSettingsStateModel.swift

@@ -2,45 +2,22 @@ import Observation
 import SwiftUI
 import SwiftUI
 
 
 extension AutosensSettings {
 extension AutosensSettings {
-    @Observable final class StateModel: BaseStateModel<Provider> {
-        @ObservationIgnored @Injected() var settings: SettingsManager!
-        @ObservationIgnored @Injected() var storage: FileStorage!
+    final class StateModel: BaseStateModel<Provider> {
+        @Injected() var settings: SettingsManager!
+        @Injected() var storage: FileStorage!
 
 
         var units: GlucoseUnits = .mgdL
         var units: GlucoseUnits = .mgdL
 
 
-        var autosensMax: Decimal = 1.2
-        var autosensMin: Decimal = 0.7
-        var rewindResetsAutosens: Bool = true
-
-        var preferences: Preferences {
-            settingsManager.preferences
-        }
+        @Published var autosensMax: Decimal = 1.2
+        @Published var autosensMin: Decimal = 0.7
+        @Published var rewindResetsAutosens: Bool = true
 
 
         override func subscribe() {
         override func subscribe() {
             units = settingsManager.settings.units
             units = settingsManager.settings.units
 
 
-            autosensMax = settings.preferences.autosensMax
-            autosensMin = settings.preferences.autosensMin
-            rewindResetsAutosens = settings.preferences.rewindResetsAutosens
-        }
-
-        var isSettingUnchanged: Bool {
-            preferences.autosensMax == autosensMax &&
-                preferences.autosensMin == autosensMin &&
-                preferences.rewindResetsAutosens == rewindResetsAutosens
-        }
-
-        func saveIfChanged() {
-            if !isSettingUnchanged {
-                var newSettings = storage.retrieve(OpenAPS.Settings.preferences, as: Preferences.self) ?? Preferences()
-
-                newSettings.autosensMax = autosensMax
-                newSettings.autosensMin = autosensMin
-                newSettings.rewindResetsAutosens = rewindResetsAutosens
-
-                newSettings.timestamp = Date()
-                storage.save(newSettings, as: OpenAPS.Settings.preferences)
-            }
+            subscribePreferencesSetting(\.autosensMax, on: $autosensMax) { autosensMax = $0 }
+            subscribePreferencesSetting(\.autosensMin, on: $autosensMin) { autosensMin = $0 }
+            subscribePreferencesSetting(\.rewindResetsAutosens, on: $rewindResetsAutosens) { rewindResetsAutosens = $0 }
         }
         }
     }
     }
 }
 }

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

@@ -111,9 +111,6 @@ extension AutosensSettings {
             .onAppear(perform: configureView)
             .onAppear(perform: configureView)
             .navigationTitle("Autosens")
             .navigationTitle("Autosens")
             .navigationBarTitleDisplayMode(.automatic)
             .navigationBarTitleDisplayMode(.automatic)
-            .onDisappear {
-                state.saveIfChanged()
-            }
         }
         }
     }
     }
 }
 }

+ 15 - 0
FreeAPS/Sources/Modules/Base/BaseStateModel.swift

@@ -58,4 +58,19 @@ class BaseStateModel<Provider>: StateModel, Injectable where Provider: FreeAPS.P
             }
             }
             .store(in: &lifetime)
             .store(in: &lifetime)
     }
     }
+
+    func subscribePreferencesSetting<T: Equatable, U: Publisher>(
+        _ keyPath: WritableKeyPath<Preferences, T>,
+        on preferencesPublisher: U, initial: (T) -> Void, map: ((T) -> (T))? = nil, didSet: ((T) -> Void)? = nil
+    ) where U.Output == T, U.Failure == Never {
+        initial(settingsManager.preferences[keyPath: keyPath])
+        preferencesPublisher
+            .removeDuplicates()
+            .map(map ?? { $0 })
+            .sink { [weak self] value in
+                self?.settingsManager.preferences[keyPath: keyPath] = value
+                didSet?(value)
+            }
+            .store(in: &lifetime)
+    }
 }
 }

+ 20 - 49
FreeAPS/Sources/Modules/DynamicSettings/DynamicSettingsStateModel.swift

@@ -2,61 +2,32 @@ import Observation
 import SwiftUI
 import SwiftUI
 
 
 extension DynamicSettings {
 extension DynamicSettings {
-    @Observable final class StateModel: BaseStateModel<Provider> {
-        @ObservationIgnored @Injected() var settings: SettingsManager!
-        @ObservationIgnored @Injected() var storage: FileStorage!
+    final class StateModel: BaseStateModel<Provider> {
+        @Injected() var settings: SettingsManager!
+        @Injected() var storage: FileStorage!
 
 
-        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
+        @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
 
 
-        var preferences: Preferences {
-            settingsManager.preferences
-        }
+        var units: GlucoseUnits = .mgdL
 
 
         override func subscribe() {
         override func subscribe() {
             units = settingsManager.settings.units
             units = settingsManager.settings.units
-            useNewFormula = settings.preferences.useNewFormula
-            enableDynamicCR = settings.preferences.enableDynamicCR
-            sigmoid = settings.preferences.sigmoid
-            adjustmentFactor = settings.preferences.adjustmentFactor
-            adjustmentFactorSigmoid = settings.preferences.adjustmentFactorSigmoid
-            weightPercentage = settings.preferences.weightPercentage
-            tddAdjBasal = settings.preferences.tddAdjBasal
-            threshold_setting = settings.preferences.threshold_setting
-        }
-
-        var unChanged: Bool {
-            preferences.enableDynamicCR == enableDynamicCR &&
-                preferences.adjustmentFactor == adjustmentFactor &&
-                preferences.sigmoid == sigmoid &&
-                preferences.adjustmentFactorSigmoid == adjustmentFactorSigmoid &&
-                preferences.tddAdjBasal == tddAdjBasal &&
-                preferences.threshold_setting == threshold_setting &&
-                preferences.useNewFormula == useNewFormula &&
-                preferences.weightPercentage == weightPercentage
-        }
 
 
-        func saveIfChanged() {
-            if !unChanged {
-                var newSettings = storage.retrieve(OpenAPS.Settings.preferences, as: Preferences.self) ?? Preferences()
-                newSettings.enableDynamicCR = enableDynamicCR
-                newSettings.adjustmentFactor = adjustmentFactor
-                newSettings.sigmoid = sigmoid
-                newSettings.adjustmentFactorSigmoid = adjustmentFactorSigmoid
-                newSettings.tddAdjBasal = tddAdjBasal
-                newSettings.threshold_setting = threshold_setting
-                newSettings.useNewFormula = useNewFormula
-                newSettings.weightPercentage = weightPercentage
-                newSettings.timestamp = Date()
-                storage.save(newSettings, as: OpenAPS.Settings.preferences)
-            }
+            subscribePreferencesSetting(\.useNewFormula, on: $useNewFormula) { useNewFormula = $0 }
+            subscribePreferencesSetting(\.enableDynamicCR, on: $enableDynamicCR) { enableDynamicCR = $0 }
+            subscribePreferencesSetting(\.sigmoid, on: $sigmoid) { sigmoid = $0 }
+            subscribePreferencesSetting(\.adjustmentFactor, on: $adjustmentFactor) { adjustmentFactor = $0 }
+            subscribePreferencesSetting(\.adjustmentFactorSigmoid, on: $adjustmentFactorSigmoid) { adjustmentFactorSigmoid = $0 }
+            subscribePreferencesSetting(\.weightPercentage, on: $weightPercentage) { weightPercentage = $0 }
+            subscribePreferencesSetting(\.tddAdjBasal, on: $tddAdjBasal) { tddAdjBasal = $0 }
+            subscribePreferencesSetting(\.tddAdjBasal, on: $tddAdjBasal) { tddAdjBasal = $0 }
         }
         }
     }
     }
 }
 }

+ 0 - 3
FreeAPS/Sources/Modules/DynamicSettings/View/DynamicSettingsRootView.swift

@@ -218,9 +218,6 @@ extension DynamicSettings {
             .onAppear(perform: configureView)
             .onAppear(perform: configureView)
             .navigationBarTitle("Dynamic Settings")
             .navigationBarTitle("Dynamic Settings")
             .navigationBarTitleDisplayMode(.automatic)
             .navigationBarTitleDisplayMode(.automatic)
-            .onDisappear {
-                state.saveIfChanged()
-            }
         }
         }
     }
     }
 }
 }

+ 3 - 17
FreeAPS/Sources/Modules/GeneralSettings/UnitsLimitsSettingsStateModel.swift

@@ -29,10 +29,11 @@ extension UnitsLimitsSettings {
                 unitsIndex = $0 == .mgdL ? 0 : 1
                 unitsIndex = $0 == .mgdL ? 0 : 1
             }
             }
 
 
+            subscribePreferencesSetting(\.maxIOB, on: $maxIOB) { maxIOB = $0 }
+            subscribePreferencesSetting(\.maxCOB, on: $maxCOB) { maxCOB = $0 }
+
             maxBasal = pumpSettings.maxBasal
             maxBasal = pumpSettings.maxBasal
             maxBolus = pumpSettings.maxBolus
             maxBolus = pumpSettings.maxBolus
-            maxIOB = settings.preferences.maxIOB
-            maxCOB = settings.preferences.maxCOB
         }
         }
 
 
         var isPumpSettingUnchanged: Bool {
         var isPumpSettingUnchanged: Bool {
@@ -40,22 +41,7 @@ extension UnitsLimitsSettings {
                 pumpSettings.maxBolus == maxBolus
                 pumpSettings.maxBolus == maxBolus
         }
         }
 
 
-        var isSettingUnchanged: Bool {
-            preferences.maxIOB == maxIOB &&
-                preferences.maxCOB == maxCOB
-        }
-
         func saveIfChanged() {
         func saveIfChanged() {
-            if !isSettingUnchanged {
-                var newSettings = storage.retrieve(OpenAPS.Settings.preferences, as: Preferences.self) ?? Preferences()
-
-                newSettings.maxIOB = maxIOB
-                newSettings.maxCOB = maxCOB
-
-                newSettings.timestamp = Date()
-                storage.save(newSettings, as: OpenAPS.Settings.preferences)
-            }
-
             if !isPumpSettingUnchanged {
             if !isPumpSettingUnchanged {
                 let settings = PumpSettings(
                 let settings = PumpSettings(
                     insulinActionCurve: pumpSettings.insulinActionCurve,
                     insulinActionCurve: pumpSettings.insulinActionCurve,

+ 33 - 74
FreeAPS/Sources/Modules/SMBSettings/SMBSettingsStateModel.swift

@@ -2,87 +2,46 @@ import Observation
 import SwiftUI
 import SwiftUI
 
 
 extension SMBSettings {
 extension SMBSettings {
-    @Observable final class StateModel: BaseStateModel<Provider> {
-        @ObservationIgnored @Injected() var settings: SettingsManager!
-        @ObservationIgnored @Injected() var storage: FileStorage!
+    final class StateModel: BaseStateModel<Provider> {
+        @Injected() var settings: SettingsManager!
+        @Injected() var storage: FileStorage!
 
 
         var units: GlucoseUnits = .mgdL
         var units: GlucoseUnits = .mgdL
 
 
-        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 {
-            settingsManager.preferences
-        }
+        @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
 
 
         override func subscribe() {
         override func subscribe() {
             units = settingsManager.settings.units
             units = settingsManager.settings.units
-            enableSMBAlways = settings.preferences.enableSMBAlways
-            maxDeltaBGthreshold = settings.preferences.maxDeltaBGthreshold
-            enableSMBWithCOB = settings.preferences.enableSMBWithCOB
-            enableSMBWithTemptarget = settings.preferences.enableSMBWithTemptarget
-            enableSMBAfterCarbs = settings.preferences.enableSMBAfterCarbs
-            allowSMBWithHighTemptarget = settings.preferences.allowSMBWithHighTemptarget
-            enableSMB_high_bg = settings.preferences.enableSMB_high_bg
-            enableSMB_high_bg_target = settings
-                .preferences.enableSMB_high_bg_target
-            maxSMBBasalMinutes = settings.preferences.maxSMBBasalMinutes
-            smbDeliveryRatio = settings.preferences.smbDeliveryRatio
-            smbInterval = settings.preferences.smbInterval
-            bolusIncrement = settings.preferences.bolusIncrement
-            enableUAM = settings.preferences.enableUAM
-            maxUAMSMBBasalMinutes = settings.preferences.maxUAMSMBBasalMinutes
-        }
-
-        var isSettingUnchanged: Bool {
-            preferences.enableSMBAlways == enableSMBAlways &&
-                preferences.maxDeltaBGthreshold == maxDeltaBGthreshold &&
-                preferences.enableSMBWithCOB == enableSMBWithCOB &&
-                preferences.enableSMBWithTemptarget == enableSMBWithTemptarget &&
-                preferences.enableSMBAfterCarbs == enableSMBAfterCarbs &&
-                preferences.allowSMBWithHighTemptarget == allowSMBWithHighTemptarget &&
-                preferences.enableSMB_high_bg == enableSMB_high_bg &&
-                preferences.enableSMB_high_bg_target == enableSMB_high_bg_target &&
-                preferences.maxSMBBasalMinutes == maxSMBBasalMinutes &&
-                preferences.smbDeliveryRatio == smbDeliveryRatio &&
-                preferences.smbInterval == smbInterval &&
-                preferences.bolusIncrement == bolusIncrement &&
-                preferences.enableUAM == enableUAM &&
-                preferences.maxUAMSMBBasalMinutes == maxUAMSMBBasalMinutes
-        }
 
 
-        func saveIfChanged() {
-            if !isSettingUnchanged {
-                var newSettings = storage.retrieve(OpenAPS.Settings.preferences, as: Preferences.self) ?? Preferences()
-                newSettings.enableSMBAlways = enableSMBAlways
-                newSettings.maxDeltaBGthreshold = maxDeltaBGthreshold
-                newSettings.enableSMBWithCOB = enableSMBWithCOB
-                newSettings.enableSMBWithTemptarget = enableSMBWithTemptarget
-                newSettings.enableSMBAfterCarbs = enableSMBAfterCarbs
-                newSettings.allowSMBWithHighTemptarget = allowSMBWithHighTemptarget
-                newSettings.enableSMB_high_bg = enableSMB_high_bg
-                newSettings.enableSMB_high_bg_target = enableSMB_high_bg_target
-                newSettings.maxSMBBasalMinutes = maxSMBBasalMinutes
-                newSettings.smbDeliveryRatio = smbDeliveryRatio
-                newSettings.smbInterval = smbInterval
-                newSettings.bolusIncrement = bolusIncrement
-                newSettings.enableUAM = enableUAM
-                newSettings.maxUAMSMBBasalMinutes = maxUAMSMBBasalMinutes
-                newSettings.timestamp = Date()
-                storage.save(newSettings, as: OpenAPS.Settings.preferences)
-            }
+            subscribePreferencesSetting(\.enableSMBAlways, on: $enableSMBAlways) { enableSMBAlways = $0 }
+            subscribePreferencesSetting(\.maxDeltaBGthreshold, on: $maxDeltaBGthreshold) { maxDeltaBGthreshold = $0 }
+            subscribePreferencesSetting(\.enableSMBWithCOB, on: $enableSMBWithCOB) { enableSMBWithCOB = $0 }
+            subscribePreferencesSetting(\.enableSMBWithTemptarget, on: $enableSMBWithTemptarget) { enableSMBWithTemptarget = $0 }
+            subscribePreferencesSetting(\.enableSMBAfterCarbs, on: $enableSMBAfterCarbs) { enableSMBAfterCarbs = $0 }
+            subscribePreferencesSetting(\.allowSMBWithHighTemptarget, on: $allowSMBWithHighTemptarget) {
+                allowSMBWithHighTemptarget = $0 }
+            subscribePreferencesSetting(\.enableSMB_high_bg, on: $enableSMB_high_bg) { enableSMB_high_bg = $0 }
+            subscribePreferencesSetting(\.enableSMB_high_bg_target, on: $enableSMB_high_bg_target) {
+                enableSMB_high_bg_target = $0 }
+            subscribePreferencesSetting(\.maxSMBBasalMinutes, on: $maxSMBBasalMinutes) { maxSMBBasalMinutes = $0 }
+            subscribePreferencesSetting(\.smbDeliveryRatio, on: $smbDeliveryRatio) { smbDeliveryRatio = $0 }
+            subscribePreferencesSetting(\.smbInterval, on: $smbInterval) { smbInterval = $0 }
+            subscribePreferencesSetting(\.bolusIncrement, on: $bolusIncrement) { bolusIncrement = $0 }
+            subscribePreferencesSetting(\.enableUAM, on: $enableUAM) { enableUAM = $0 }
+            subscribePreferencesSetting(\.maxUAMSMBBasalMinutes, on: $maxUAMSMBBasalMinutes) { maxUAMSMBBasalMinutes = $0 }
         }
         }
     }
     }
 }
 }

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

@@ -309,9 +309,6 @@ extension SMBSettings {
             .onAppear(perform: configureView)
             .onAppear(perform: configureView)
             .navigationTitle("SMB Settings")
             .navigationTitle("SMB Settings")
             .navigationBarTitleDisplayMode(.automatic)
             .navigationBarTitleDisplayMode(.automatic)
-            .onDisappear {
-                state.saveIfChanged()
-            }
         }
         }
     }
     }
 }
 }

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

@@ -13,44 +13,16 @@ extension TargetBehavoir {
         @Published var resistanceLowersTarget: Bool = false
         @Published var resistanceLowersTarget: Bool = false
         @Published var halfBasalExerciseTarget: Decimal = 160
         @Published var halfBasalExerciseTarget: Decimal = 160
 
 
-        var preferences: Preferences {
-            settingsManager.preferences
-        }
-
         override func subscribe() {
         override func subscribe() {
             units = settingsManager.settings.units
             units = settingsManager.settings.units
 
 
-            highTemptargetRaisesSensitivity = settings.preferences.highTemptargetRaisesSensitivity
-            lowTemptargetLowersSensitivity = settings.preferences.lowTemptargetLowersSensitivity
-            sensitivityRaisesTarget = settings.preferences.sensitivityRaisesTarget
-            resistanceLowersTarget = settings.preferences.resistanceLowersTarget
-            halfBasalExerciseTarget = settings.preferences.halfBasalExerciseTarget
-
-            halfBasalExerciseTarget = settings
-                .preferences.halfBasalExerciseTarget
-        }
-
-        var isSettingUnchanged: Bool {
-            preferences.highTemptargetRaisesSensitivity == highTemptargetRaisesSensitivity &&
-                preferences.lowTemptargetLowersSensitivity == lowTemptargetLowersSensitivity &&
-                preferences.sensitivityRaisesTarget == sensitivityRaisesTarget &&
-                preferences.resistanceLowersTarget == resistanceLowersTarget &&
-                preferences.halfBasalExerciseTarget == halfBasalExerciseTarget
-        }
-
-        func saveIfChanged() {
-            if !isSettingUnchanged {
-                var newSettings = storage.retrieve(OpenAPS.Settings.preferences, as: Preferences.self) ?? Preferences()
-
-                newSettings.highTemptargetRaisesSensitivity = highTemptargetRaisesSensitivity
-                newSettings.lowTemptargetLowersSensitivity = lowTemptargetLowersSensitivity
-                newSettings.sensitivityRaisesTarget = sensitivityRaisesTarget
-                newSettings.resistanceLowersTarget = resistanceLowersTarget
-                newSettings.halfBasalExerciseTarget = halfBasalExerciseTarget
-
-                newSettings.timestamp = Date()
-                storage.save(newSettings, as: OpenAPS.Settings.preferences)
-            }
+            subscribePreferencesSetting(\.highTemptargetRaisesSensitivity, on: $highTemptargetRaisesSensitivity) {
+                highTemptargetRaisesSensitivity = $0 }
+            subscribePreferencesSetting(\.lowTemptargetLowersSensitivity, on: $lowTemptargetLowersSensitivity) {
+                lowTemptargetLowersSensitivity = $0 }
+            subscribePreferencesSetting(\.sensitivityRaisesTarget, on: $sensitivityRaisesTarget) { sensitivityRaisesTarget = $0 }
+            subscribePreferencesSetting(\.resistanceLowersTarget, on: $resistanceLowersTarget) { resistanceLowersTarget = $0 }
+            subscribePreferencesSetting(\.halfBasalExerciseTarget, on: $halfBasalExerciseTarget) { halfBasalExerciseTarget = $0 }
         }
         }
     }
     }
 }
 }

+ 3 - 3
FreeAPS/Sources/Modules/TargetBehavoir/View/TargetBehavoirRootView.swift

@@ -165,9 +165,9 @@ extension TargetBehavoir {
             .onAppear(perform: configureView)
             .onAppear(perform: configureView)
             .navigationTitle("Target Behavior")
             .navigationTitle("Target Behavior")
             .navigationBarTitleDisplayMode(.automatic)
             .navigationBarTitleDisplayMode(.automatic)
-            .onDisappear {
-                state.saveIfChanged()
-            }
+//            .onDisappear {
+//                state.saveIfChanged()
+//            }
         }
         }
     }
     }
 }
 }

+ 35 - 13
FreeAPS/Sources/Services/SettingsManager/SettingsManager.swift

@@ -4,7 +4,7 @@ import Swinject
 
 
 protocol SettingsManager: AnyObject {
 protocol SettingsManager: AnyObject {
     var settings: FreeAPSSettings { get set }
     var settings: FreeAPSSettings { get set }
-    var preferences: Preferences { get }
+    var preferences: Preferences { get set }
     var pumpSettings: PumpSettings { get }
     var pumpSettings: PumpSettings { get }
     func updateInsulinCurve(_ insulinType: InsulinType?)
     func updateInsulinCurve(_ insulinType: InsulinType?)
 }
 }
@@ -13,6 +13,10 @@ protocol SettingsObserver {
     func settingsDidChange(_: FreeAPSSettings)
     func settingsDidChange(_: FreeAPSSettings)
 }
 }
 
 
+protocol PreferencesObserver {
+    func preferencesDidChange(_: Preferences)
+}
+
 final class BaseSettingsManager: SettingsManager, Injectable {
 final class BaseSettingsManager: SettingsManager, Injectable {
     @Injected() var broadcaster: Broadcaster!
     @Injected() var broadcaster: Broadcaster!
     @Injected() var storage: FileStorage!
     @Injected() var storage: FileStorage!
@@ -20,7 +24,7 @@ final class BaseSettingsManager: SettingsManager, Injectable {
     @SyncAccess var settings: FreeAPSSettings {
     @SyncAccess var settings: FreeAPSSettings {
         didSet {
         didSet {
             if oldValue != settings {
             if oldValue != settings {
-                save()
+                saveSettings()
                 DispatchQueue.main.async {
                 DispatchQueue.main.async {
                     self.broadcaster.notify(SettingsObserver.self, on: .main) {
                     self.broadcaster.notify(SettingsObserver.self, on: .main) {
                         $0.settingsDidChange(self.settings)
                         $0.settingsDidChange(self.settings)
@@ -30,23 +34,39 @@ final class BaseSettingsManager: SettingsManager, Injectable {
         }
         }
     }
     }
 
 
+    @SyncAccess var preferences: Preferences {
+        didSet {
+            if oldValue != preferences {
+                savePreferences()
+                DispatchQueue.main.async {
+                    self.broadcaster.notify(PreferencesObserver.self, on: .main) {
+                        $0.preferencesDidChange(self.preferences)
+                    }
+                }
+            }
+        }
+    }
+
+    private func saveSettings() {
+        storage.save(settings, as: OpenAPS.FreeAPS.settings)
+    }
+
+    private func savePreferences() {
+        storage.save(preferences, as: OpenAPS.Settings.preferences)
+    }
+
     init(resolver: Resolver) {
     init(resolver: Resolver) {
         let storage = resolver.resolve(FileStorage.self)!
         let storage = resolver.resolve(FileStorage.self)!
         settings = storage.retrieve(OpenAPS.FreeAPS.settings, as: FreeAPSSettings.self)
         settings = storage.retrieve(OpenAPS.FreeAPS.settings, as: FreeAPSSettings.self)
             ?? FreeAPSSettings(from: OpenAPS.defaults(for: OpenAPS.FreeAPS.settings))
             ?? FreeAPSSettings(from: OpenAPS.defaults(for: OpenAPS.FreeAPS.settings))
             ?? FreeAPSSettings()
             ?? FreeAPSSettings()
 
 
-        injectServices(resolver)
-    }
+        preferences =
+            storage.retrieve(OpenAPS.Settings.preferences, as: Preferences.self)
+                ?? Preferences(from: OpenAPS.defaults(for: OpenAPS.Settings.preferences))
+                ?? Preferences()
 
 
-    private func save() {
-        storage.save(settings, as: OpenAPS.FreeAPS.settings)
-    }
-
-    var preferences: Preferences {
-        storage.retrieve(OpenAPS.Settings.preferences, as: Preferences.self)
-            ?? Preferences(from: OpenAPS.defaults(for: OpenAPS.Settings.preferences))
-            ?? Preferences()
+        injectServices(resolver)
     }
     }
 
 
     var pumpSettings: PumpSettings {
     var pumpSettings: PumpSettings {
@@ -70,6 +90,8 @@ final class BaseSettingsManager: SettingsManager, Injectable {
         default:
         default:
             prefs.curve = .rapidActing
             prefs.curve = .rapidActing
         }
         }
-        storage.save(prefs, as: OpenAPS.Settings.preferences)
+
+        preferences = prefs
+        savePreferences()
     }
     }
 }
 }