diff --git a/FreeAPS.xcodeproj/project.pbxproj b/FreeAPS.xcodeproj/project.pbxproj index 0ed29a662..ac5c0d98d 100644 --- a/FreeAPS.xcodeproj/project.pbxproj +++ b/FreeAPS.xcodeproj/project.pbxproj @@ -22,6 +22,10 @@ 1927C8E62744606D00347C69 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1927C8E82744606D00347C69 /* InfoPlist.strings */; }; 1935364028496F7D001E0B16 /* Oref2_variables.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1935363F28496F7D001E0B16 /* Oref2_variables.swift */; }; 193F6CDD2A512C8F001240FD /* Loops.swift in Sources */ = {isa = PBXBuildFile; fileRef = 193F6CDC2A512C8F001240FD /* Loops.swift */; }; + 195D80B42AF6973A00D25097 /* DynamicRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 195D80B32AF6973A00D25097 /* DynamicRootView.swift */; }; + 195D80B72AF697B800D25097 /* DynamicDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 195D80B62AF697B800D25097 /* DynamicDataFlow.swift */; }; + 195D80B92AF697F700D25097 /* DynamicProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 195D80B82AF697F700D25097 /* DynamicProvider.swift */; }; + 195D80BB2AF6980B00D25097 /* DynamicStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 195D80BA2AF6980B00D25097 /* DynamicStateModel.swift */; }; 1967DFBE29D052C200759F30 /* Icons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1967DFBD29D052C200759F30 /* Icons.swift */; }; 1967DFC029D053AC00759F30 /* IconSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1967DFBF29D053AC00759F30 /* IconSelection.swift */; }; 1967DFC229D053D300759F30 /* IconImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1967DFC129D053D300759F30 /* IconImage.swift */; }; @@ -526,6 +530,10 @@ 1927C8FE274489BA00347C69 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = ""; }; 1935363F28496F7D001E0B16 /* Oref2_variables.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Oref2_variables.swift; sourceTree = ""; }; 193F6CDC2A512C8F001240FD /* Loops.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Loops.swift; sourceTree = ""; }; + 195D80B32AF6973A00D25097 /* DynamicRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicRootView.swift; sourceTree = ""; }; + 195D80B62AF697B800D25097 /* DynamicDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicDataFlow.swift; sourceTree = ""; }; + 195D80B82AF697F700D25097 /* DynamicProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicProvider.swift; sourceTree = ""; }; + 195D80BA2AF6980B00D25097 /* DynamicStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicStateModel.swift; sourceTree = ""; }; 1967DFBD29D052C200759F30 /* Icons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Icons.swift; sourceTree = ""; }; 1967DFBF29D053AC00759F30 /* IconSelection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconSelection.swift; sourceTree = ""; }; 1967DFC129D053D300759F30 /* IconImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconImage.swift; sourceTree = ""; }; @@ -1050,6 +1058,25 @@ name = "Recovered References"; sourceTree = ""; }; + 195D80B22AF696EE00D25097 /* Dynamic */ = { + isa = PBXGroup; + children = ( + 195D80B62AF697B800D25097 /* DynamicDataFlow.swift */, + 195D80B82AF697F700D25097 /* DynamicProvider.swift */, + 195D80BA2AF6980B00D25097 /* DynamicStateModel.swift */, + 195D80B52AF6974200D25097 /* View */, + ); + path = Dynamic; + sourceTree = ""; + }; + 195D80B52AF6974200D25097 /* View */ = { + isa = PBXGroup; + children = ( + 195D80B32AF6973A00D25097 /* DynamicRootView.swift */, + ); + path = View; + sourceTree = ""; + }; 198377CF266BFEDE004DE65E /* Localizations */ = { isa = PBXGroup; children = ( @@ -1168,10 +1195,11 @@ 3811DE0325C9D31700A708ED /* Modules */ = { isa = PBXGroup; children = ( + 195D80B22AF696EE00D25097 /* Dynamic */, BD7DA9A32AE06DBA00601B20 /* BolusCalculatorConfig */, 190EBCC229FF134900BA767D /* StatConfig */, - CE94597C29E9E1CD0047C9C6 /* WatchConfig */, 19F95FF129F10F9C00314DDC /* Stat */, + CE94597C29E9E1CD0047C9C6 /* WatchConfig */, 19E1F7E629D0828B005C8D20 /* IconConfig */, 19D466A129AA2B0A004D5F33 /* FPUConfig */, F90692CD274B99850037068D /* HealthKit */, @@ -2576,6 +2604,7 @@ 38C4D33725E9A1A300D30B77 /* DispatchQueue+Extensions.swift in Sources */, F90692CF274B999A0037068D /* HealthKitDataFlow.swift in Sources */, CE7CA3552A064973004BE681 /* ListStateIntent.swift in Sources */, + 195D80B72AF697B800D25097 /* DynamicDataFlow.swift in Sources */, 3862CC2E2743F9F700BF832C /* CalendarManager.swift in Sources */, CEA4F62329BE10F70011ADF7 /* SavitzkyGolayFilter.swift in Sources */, 38B4F3C325E2A20B00E76A18 /* PumpSetupView.swift in Sources */, @@ -2758,6 +2787,7 @@ 19F95FF729F10FEE00314DDC /* StatStateModel.swift in Sources */, 385CEAC125F2EA52002D6D5B /* Announcement.swift in Sources */, 8B759CFCF47B392BB365C251 /* BasalProfileEditorDataFlow.swift in Sources */, + 195D80B42AF6973A00D25097 /* DynamicRootView.swift in Sources */, 389442CB25F65F7100FA1F27 /* NightscoutTreatment.swift in Sources */, CE7CA3512A064973004BE681 /* ApplyTempPresetIntent.swift in Sources */, FA630397F76B582C8D8681A7 /* BasalProfileEditorProvider.swift in Sources */, @@ -2770,6 +2800,7 @@ 38887CCE25F5725200944304 /* IOBEntry.swift in Sources */, 38E98A2425F52C9300C0CED0 /* Logger.swift in Sources */, CA370FC152BC98B3D1832968 /* BasalProfileEditorRootView.swift in Sources */, + 195D80BB2AF6980B00D25097 /* DynamicStateModel.swift in Sources */, E00EEC0327368630002FF094 /* ServiceAssembly.swift in Sources */, 38192E07261BA9960094D973 /* FetchTreatmentsManager.swift in Sources */, 19012CDC291D2CB900FB8210 /* LoopStats.swift in Sources */, @@ -2853,6 +2884,7 @@ 1D845DF2E3324130E1D95E67 /* DataTableProvider.swift in Sources */, 19F95FFA29F1102A00314DDC /* StatRootView.swift in Sources */, 0D9A5E34A899219C5C4CDFAF /* DataTableStateModel.swift in Sources */, + 195D80B92AF697F700D25097 /* DynamicProvider.swift in Sources */, D6D02515BBFBE64FEBE89856 /* DataTableRootView.swift in Sources */, 38569349270B5DFB0002C50D /* AppGroupSource.swift in Sources */, F5CA3DB1F9DC8B05792BBFAA /* CGMDataFlow.swift in Sources */, diff --git a/FreeAPS/Sources/Modules/Dynamic/DynamicDataFlow.swift b/FreeAPS/Sources/Modules/Dynamic/DynamicDataFlow.swift new file mode 100644 index 000000000..9e7412dd2 --- /dev/null +++ b/FreeAPS/Sources/Modules/Dynamic/DynamicDataFlow.swift @@ -0,0 +1,5 @@ +enum Dynamic { + enum Config {} +} + +protocol DynamicProvider: Provider {} diff --git a/FreeAPS/Sources/Modules/Dynamic/DynamicProvider.swift b/FreeAPS/Sources/Modules/Dynamic/DynamicProvider.swift new file mode 100644 index 000000000..2a1d9fca0 --- /dev/null +++ b/FreeAPS/Sources/Modules/Dynamic/DynamicProvider.swift @@ -0,0 +1,3 @@ +extension Dynamic { + final class Provider: BaseProvider, DynamicProvider {} +} diff --git a/FreeAPS/Sources/Modules/Dynamic/DynamicStateModel.swift b/FreeAPS/Sources/Modules/Dynamic/DynamicStateModel.swift new file mode 100644 index 000000000..6a4f30e6f --- /dev/null +++ b/FreeAPS/Sources/Modules/Dynamic/DynamicStateModel.swift @@ -0,0 +1,69 @@ +import SwiftUI + +extension Dynamic { + final class StateModel: BaseStateModel { + @Injected() var settings: SettingsManager! + @Injected() var storage: FileStorage! + + @Published var useNewFormula: Bool = false + @Published var enableDynamicCR: Bool = false + @Published var sigmoid: Bool = false + @Published var adjustmentFactor: Decimal = 0.5 + @Published var weightPercentage: Decimal = 0.65 + @Published var tddAdjBasal: Bool = false + @Published var threshold_setting: Decimal = 65 + @Published var unit: GlucoseUnits = .mmolL + + var preferences: Preferences { + settingsManager.preferences + } + + override func subscribe() { + unit = settingsManager.settings.units + useNewFormula = settings.preferences.useNewFormula + enableDynamicCR = settings.preferences.enableDynamicCR + sigmoid = settings.preferences.sigmoid + adjustmentFactor = settings.preferences.adjustmentFactor + weightPercentage = settings.preferences.weightPercentage + tddAdjBasal = settings.preferences.tddAdjBasal + + if unit == .mmolL { + threshold_setting = settings.preferences.threshold_setting.asMmolL + } else { + threshold_setting = settings.preferences.threshold_setting + } + } + + var unChanged: Bool { + preferences.enableDynamicCR == enableDynamicCR && + preferences.adjustmentFactor == adjustmentFactor && + preferences.sigmoid == sigmoid && + preferences.tddAdjBasal == tddAdjBasal && + preferences.threshold_setting == convertBack(threshold_setting) && + preferences.useNewFormula == useNewFormula && + preferences.weightPercentage == weightPercentage + } + + func convertBack(_ glucose: Decimal) -> Decimal { + if unit == .mmolL { + return glucose.asMgdL + } + return glucose + } + + 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.tddAdjBasal = tddAdjBasal + newSettings.threshold_setting = convertBack(threshold_setting) + newSettings.useNewFormula = useNewFormula + newSettings.weightPercentage = weightPercentage + newSettings.timestamp = Date() + storage.save(newSettings, as: OpenAPS.Settings.preferences) + } + } + } +} diff --git a/FreeAPS/Sources/Modules/Dynamic/View/DynamicRootView.swift b/FreeAPS/Sources/Modules/Dynamic/View/DynamicRootView.swift new file mode 100644 index 000000000..35bce423c --- /dev/null +++ b/FreeAPS/Sources/Modules/Dynamic/View/DynamicRootView.swift @@ -0,0 +1,89 @@ +import SwiftUI +import Swinject + +extension Dynamic { + struct RootView: BaseView { + let resolver: Resolver + @StateObject var state = StateModel() + + private var conversionFormatter: NumberFormatter { + let formatter = NumberFormatter() + formatter.numberStyle = .decimal + formatter.maximumFractionDigits = 1 + + return formatter + } + + private var formatter: NumberFormatter { + let formatter = NumberFormatter() + formatter.numberStyle = .decimal + return formatter + } + + private var glucoseFormatter: NumberFormatter { + let formatter = NumberFormatter() + formatter.numberStyle = .decimal + if state.unit == .mmolL { + formatter.maximumFractionDigits = 1 + } else { formatter.maximumFractionDigits = 0 } + formatter.roundingMode = .halfUp + return formatter + } + + var body: some View { + Form { + Section { + HStack { + Toggle("Activate Dynamic Sensitivity (ISF)", isOn: $state.useNewFormula) + } + if state.useNewFormula { + HStack { + Toggle("Activate Dynamic Carb Ratio (CR)", isOn: $state.enableDynamicCR) + } + } + } header: { Text("Enable") } + + if state.useNewFormula { + Section { + HStack { + Toggle("Use Sigmoid Formula", isOn: $state.sigmoid) + } + } header: { Text("Formula") } + + Section { + HStack { + Text("Adjustment Factor") + Spacer() + DecimalTextField("0", value: $state.adjustmentFactor, formatter: formatter) + } + + HStack { + Text("Weighted Average of TDD. Weight of past 24 hours:") + Spacer() + DecimalTextField("0", value: $state.weightPercentage, formatter: formatter) + } + + HStack { + Toggle("Adjust basal", isOn: $state.tddAdjBasal) + } + } header: { Text("Settings") } + + Section { + HStack { + Text("Threshold Setting") + Spacer() + DecimalTextField("0", value: $state.threshold_setting, formatter: glucoseFormatter) + Text(state.unit.rawValue) + } + } header: { Text("Safety") } + } + } + .onAppear(perform: configureView) + .navigationBarTitle("Dynamic ISF") + .navigationBarTitleDisplayMode(.automatic) + .onDisappear { + state.saveIfChanged() + } + } + } +} diff --git a/FreeAPS/Sources/Modules/NightscoutConfig/NightscoutConfigStateModel.swift b/FreeAPS/Sources/Modules/NightscoutConfig/NightscoutConfigStateModel.swift index cab1f7219..bbcc981c6 100644 --- a/FreeAPS/Sources/Modules/NightscoutConfig/NightscoutConfigStateModel.swift +++ b/FreeAPS/Sources/Modules/NightscoutConfig/NightscoutConfigStateModel.swift @@ -32,6 +32,7 @@ extension NightscoutConfig { @Published var dia: Decimal = 6 @Published var maxBasal: Decimal = 2 @Published var maxBolus: Decimal = 10 + @Published var allowAnnouncements: Bool = false override func subscribe() { url = keychain.getValue(String.self, forKey: Config.urlKey) ?? "" @@ -41,6 +42,7 @@ extension NightscoutConfig { maxBasal = settingsManager.pumpSettings.maxBasal maxBolus = settingsManager.pumpSettings.maxBolus + subscribeSetting(\.allowAnnouncements, on: $allowAnnouncements) { allowAnnouncements = $0 } subscribeSetting(\.isUploadEnabled, on: $isUploadEnabled) { isUploadEnabled = $0 } subscribeSetting(\.useLocalGlucoseSource, on: $useLocalSource) { useLocalSource = $0 } subscribeSetting(\.localGlucosePort, on: $localPort.map(Int.init)) { localPort = Decimal($0) } diff --git a/FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConfigRootView.swift b/FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConfigRootView.swift index 157f5a773..6c07ccdea 100644 --- a/FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConfigRootView.swift +++ b/FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConfigRootView.swift @@ -119,6 +119,10 @@ extension NightscoutConfig { Button("Backfill glucose") { state.backfillGlucose() } .disabled(state.url.isEmpty || state.connecting || state.backfilling) } + + Section { + Toggle("Remote control", isOn: $state.allowAnnouncements) + } header: { Text("Allow Remote control of iAPS") } } .onAppear(perform: configureView) .navigationBarTitle("Nightscout Config") diff --git a/FreeAPS/Sources/Modules/PreferencesEditor/PreferencesEditorStateModel.swift b/FreeAPS/Sources/Modules/PreferencesEditor/PreferencesEditorStateModel.swift index 7054d24ad..136f924d0 100644 --- a/FreeAPS/Sources/Modules/PreferencesEditor/PreferencesEditorStateModel.swift +++ b/FreeAPS/Sources/Modules/PreferencesEditor/PreferencesEditorStateModel.swift @@ -12,9 +12,7 @@ extension PreferencesEditor { override func subscribe() { preferences = provider.preferences - useAlternativeBolusCalc = settingsManager.settings.useCalc units = settingsManager.settings.units - subscribeSetting(\.allowAnnouncements, on: $allowAnnouncements) { allowAnnouncements = $0 } subscribeSetting(\.units, on: $unitsIndex.map { $0 == 0 ? GlucoseUnits.mgdL : .mmolL }) { unitsIndex = $0 == .mgdL ? 0 : 1 @@ -80,75 +78,6 @@ extension PreferencesEditor { ) ] - let dynamicISF = [ - Field( - displayName: NSLocalizedString("Enable Dynamic ISF", comment: "Enable Dynamic ISF"), - type: .boolean(keypath: \.useNewFormula), - infoText: NSLocalizedString( - "Calculate a new ISF with every loop cycle. New ISF will be based on current BG, TDD of insulin (past 24 hours or a weighted average) and an Adjustment Factor (default is 1).\n\nDynamic ISF and CR ratios will be limited by your autosens.min/max limits.\n\nDynamic ratio replaces the autosens.ratio:\n\nNew ISF = Static ISF / Dynamic ratio,\n\nDynamic ratio = profile.sens * adjustmentFactor * tdd * Math.log(BG/insulinFactor+1) / 1800,\n\ninsulinFactor = 120 - InsulinPeakTimeInMinutes", - comment: "Enable Dynamic ISF" - ), - settable: self - ), - Field( - displayName: NSLocalizedString("Enable Dynamic CR", comment: "Use Dynamic CR together with Dynamic ISF"), - type: .boolean(keypath: \.enableDynamicCR), - infoText: NSLocalizedString( - "Use Dynamic CR. The dynamic ratio will be used for CR as follows:\n\n When ratio > 1: dynCR = (newRatio - 1) / 2 + 1.\nWhen ratio < 1: dynCR = CR/dynCR.\n\nDon't use toghether with a high Insulin Fraction (> 2)", - comment: "Use Dynamic CR together with Dynamic ISF" - ), - settable: self - ), - Field( - displayName: NSLocalizedString("Adjustment Factor", comment: "Adjust Dynamic ISF constant"), - type: .decimal(keypath: \.adjustmentFactor), - infoText: NSLocalizedString( - "Adjust Dynamic ratios by a constant. Default is 0.5. The higher the value, the larger the correction of your ISF will be for a high or a low BG. Maximum correction is determined by the Autosens min/max settings. For Sigmoid function an adjustment factor of 0.4 - 0.5 is recommended to begin with. For the logaritmic formula threre is less consensus, but starting with 0.5 - 0.8 is more appropiate for most users", - comment: "Adjust Dynamic ISF constant" - ), - settable: self - ), - Field( - displayName: NSLocalizedString("Use Sigmoid Function", comment: "Use Sigmoid Function"), - type: .boolean(keypath: \.sigmoid), - infoText: NSLocalizedString( - "Use a sigmoid function for ISF (and for CR, when enabled), instead of the default Logarithmic formula. Requires the Dynamic ISF setting to be enabled in settings\n\nThe Adjustment setting adjusts the slope of the curve (Y: Dynamic ratio, X: Blood Glucose). A lower value ==> less steep == less aggressive.\n\nThe autosens.min/max settings determines both the max/min limits for the dynamic ratio AND how much the dynamic ratio is adjusted. If AF is the slope of the curve, the autosens.min/max is the height of the graph, the Y-interval, where Y: dynamic ratio. The curve will always have a sigmoid shape, no matter which autosens.min/max settings are used, meaning these settings have big consequences for the outcome of the computed dynamic ISF. Please be careful setting a too high autosens.max value. With a proper profile ISF setting, you will probably never need it to be higher than 1.5\n\nAn Autosens.max limit > 1.5 is not advisable when using the sigmoid function.", - comment: "Use Sigmoid Function" - ), - settable: self - ), - Field( - displayName: NSLocalizedString( - "Weighted Average of TDD. Weight of past 24 hours:", - comment: "Weight of past 24 hours of insulin" - ), - type: .decimal(keypath: \.weightPercentage), - infoText: NSLocalizedString( - "Has to be > 0 and <= 1.\nDefault is 0.65 (65 %) * TDD. The rest will be from average of total data (up to 14 days) of all TDD calculations (35 %). To only use past 24 hours, set this to 1.\n\nTo avoid sudden fluctuations, for instance after a big meal, an average of the past 2 hours of TDD calculations is used instead of just the current TDD (past 24 hours at this moment).", - comment: "Weight of past 24 hours of insulin" - ), - settable: self - ), - Field( - displayName: NSLocalizedString("Adjust basal", comment: "Enable adjustment of basal profile"), - type: .boolean(keypath: \.tddAdjBasal), - infoText: NSLocalizedString( - "Enable adjustment of basal based on the ratio of current TDD / 7 day average TDD", - comment: "Enable adjustment of basal profile" - ), - settable: self - ), - Field( - displayName: NSLocalizedString("Threshold Setting (mg/dl)", comment: "Threshold Setting"), - type: .decimal(keypath: \.threshold_setting), - infoText: NSLocalizedString( - "The default threshold in FAX depends on your current minimum BG target, as follows:\n\nIf your minimum BG target = 90 mg/dl -> threshold = 65 mg/dl,\n\nif minimum BG target = 100 mg/dl -> threshold = 70 mg/dl,\n\nminimum BG target = 110 mg/dl -> threshold = 75 mg/dl,\n\nand if minimum BG target = 130 mg/dl -> threshold = 85 mg/dl.\n\nThis setting allows you to change the default to a higher threshold for looping with dynISF. Valid values are 65 mg/dl<= Threshold Setting <= 120 mg/dl.", - comment: "Threshold Setting" - ), - settable: self - ) - ] - let smbFields = [ Field( displayName: NSLocalizedString("Enable SMB Always", comment: "Enable SMB Always"), @@ -450,10 +379,6 @@ extension PreferencesEditor { FieldSection( displayName: NSLocalizedString("OpenAPS main settings", comment: "OpenAPS main settings"), fields: mainFields ), - FieldSection( - displayName: NSLocalizedString("Dynamic settings", comment: "Dynamic settings"), - fields: dynamicISF - ), FieldSection( displayName: NSLocalizedString("OpenAPS SMB settings", comment: "OpenAPS SMB settings"), fields: smbFields diff --git a/FreeAPS/Sources/Modules/PreferencesEditor/View/PreferencesEditorRootView.swift b/FreeAPS/Sources/Modules/PreferencesEditor/View/PreferencesEditorRootView.swift index 1aa1c26bd..18e06eee9 100644 --- a/FreeAPS/Sources/Modules/PreferencesEditor/View/PreferencesEditorRootView.swift +++ b/FreeAPS/Sources/Modules/PreferencesEditor/View/PreferencesEditorRootView.swift @@ -27,8 +27,6 @@ extension PreferencesEditor { Text("mg/dL").tag(0) Text("mmol/L").tag(1) } - - Toggle("Remote control", isOn: $state.allowAnnouncements) } ForEach(state.sections.indexed(), id: \.1.id) { sectionIndex, section in diff --git a/FreeAPS/Sources/Modules/Settings/View/SettingsRootView.swift b/FreeAPS/Sources/Modules/Settings/View/SettingsRootView.swift index 5321c16bf..6cce471d0 100644 --- a/FreeAPS/Sources/Modules/Settings/View/SettingsRootView.swift +++ b/FreeAPS/Sources/Modules/Settings/View/SettingsRootView.swift @@ -50,6 +50,7 @@ extension Settings { Text("App Icons").navigationLink(to: .iconConfig, from: self) Text("Bolus Calculator").navigationLink(to: .bolusCalculatorConfig, from: self) Text("Fat And Protein Conversion").navigationLink(to: .fpuConfig, from: self) + Text("Dynamic ISF").navigationLink(to: .dynamicISF, from: self) } header: { Text("Extra Features") } Section { diff --git a/FreeAPS/Sources/Router/Screen.swift b/FreeAPS/Sources/Router/Screen.swift index 26a2b13dc..23f83f4a9 100644 --- a/FreeAPS/Sources/Router/Screen.swift +++ b/FreeAPS/Sources/Router/Screen.swift @@ -33,6 +33,7 @@ enum Screen: Identifiable, Hashable { case watch case statisticsConfig case bolusCalculatorConfig + case dynamicISF var id: Int { String(reflecting: self).hashValue } } @@ -102,6 +103,8 @@ extension Screen { StatConfig.RootView(resolver: resolver) case .bolusCalculatorConfig: BolusCalculatorConfig.RootView(resolver: resolver) + case .dynamicISF: + Dynamic.RootView(resolver: resolver) } }