diff --git a/Loop.xcodeproj/project.pbxproj b/Loop.xcodeproj/project.pbxproj index 7a4f3a4b8d..a27a450351 100644 --- a/Loop.xcodeproj/project.pbxproj +++ b/Loop.xcodeproj/project.pbxproj @@ -332,7 +332,6 @@ 89F9119624358E6900ECCAF3 /* BolusPickerValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89F9119524358E6900ECCAF3 /* BolusPickerValues.swift */; }; 89FE21AD24AC57E30033F501 /* Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89FE21AC24AC57E30033F501 /* Collection.swift */; }; 9E1B653A26F7D14E00D5BD24 /* IntegralRetrospectiveCorrection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1B653926F7D14E00D5BD24 /* IntegralRetrospectiveCorrection.swift */; }; - 9E9C350626F69B2A0077F908 /* RetrospectiveCorrectionSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9C350526F69B2A0077F908 /* RetrospectiveCorrectionSelectionView.swift */; }; A90EF53C25DEF06200F32D61 /* PluginManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16DA84122E8E112008624C2 /* PluginManager.swift */; }; A90EF54425DEF0A000F32D61 /* OSLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4374B5EE209D84BE00D17AA8 /* OSLog.swift */; }; A91D2A3F26CF0FF80023B075 /* IconTitleSubtitleTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91D2A3E26CF0FF80023B075 /* IconTitleSubtitleTableViewCell.swift */; }; @@ -518,6 +517,8 @@ C1FB428D21791D2500FAB378 /* PumpManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C3B6F620BBCAA30026CAFA /* PumpManager.swift */; }; C1FB428F217921D600FAB378 /* PumpManagerUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1FB428E217921D600FAB378 /* PumpManagerUI.swift */; }; C1FB4290217922A100FAB378 /* PumpManagerUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1FB428E217921D600FAB378 /* PumpManagerUI.swift */; }; + DD3DBD272A33AEC8000F8B5B /* SettingsView+algorithmExperimentsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3DBD262A33AEC8000F8B5B /* SettingsView+algorithmExperimentsSection.swift */; }; + DD3DBD292A33AFE9000F8B5B /* IntegralRetrospectiveCorrectionSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3DBD282A33AFE9000F8B5B /* IntegralRetrospectiveCorrectionSelectionView.swift */; }; E90909D124E34AC500F963D2 /* high_and_rising_with_cob_momentum_effect.json in Resources */ = {isa = PBXBuildFile; fileRef = E90909CC24E34AC500F963D2 /* high_and_rising_with_cob_momentum_effect.json */; }; E90909D224E34AC500F963D2 /* high_and_rising_with_cob_insulin_effect.json in Resources */ = {isa = PBXBuildFile; fileRef = E90909CD24E34AC500F963D2 /* high_and_rising_with_cob_insulin_effect.json */; }; E90909D324E34AC500F963D2 /* high_and_rising_with_cob_predicted_glucose.json in Resources */ = {isa = PBXBuildFile; fileRef = E90909CE24E34AC500F963D2 /* high_and_rising_with_cob_predicted_glucose.json */; }; @@ -1317,7 +1318,6 @@ 89F9119524358E6900ECCAF3 /* BolusPickerValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BolusPickerValues.swift; sourceTree = ""; }; 89FE21AC24AC57E30033F501 /* Collection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Collection.swift; sourceTree = ""; }; 9E1B653926F7D14E00D5BD24 /* IntegralRetrospectiveCorrection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegralRetrospectiveCorrection.swift; sourceTree = ""; }; - 9E9C350526F69B2A0077F908 /* RetrospectiveCorrectionSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RetrospectiveCorrectionSelectionView.swift; sourceTree = ""; }; A900531A28D60862000BC15B /* Loop.shortcut */ = {isa = PBXFileReference; lastKnownFileType = file; path = Loop.shortcut; sourceTree = ""; }; A900531B28D608CA000BC15B /* Cancel Override.shortcut */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Cancel Override.shortcut"; sourceTree = ""; }; A900531C28D6090D000BC15B /* Loop Remote Overrides.shortcut */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Loop Remote Overrides.shortcut"; sourceTree = ""; }; @@ -1687,6 +1687,8 @@ C1FF3D4B29C786A900BDC1EC /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Localizable.strings; sourceTree = ""; }; C1FF3D4C29C786A900BDC1EC /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Localizable.strings; sourceTree = ""; }; C1FF3D4D29C786A900BDC1EC /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/InfoPlist.strings; sourceTree = ""; }; + DD3DBD262A33AEC8000F8B5B /* SettingsView+algorithmExperimentsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SettingsView+algorithmExperimentsSection.swift"; sourceTree = ""; }; + DD3DBD282A33AFE9000F8B5B /* IntegralRetrospectiveCorrectionSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegralRetrospectiveCorrectionSelectionView.swift; sourceTree = ""; }; E90909CC24E34AC500F963D2 /* high_and_rising_with_cob_momentum_effect.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = high_and_rising_with_cob_momentum_effect.json; sourceTree = ""; }; E90909CD24E34AC500F963D2 /* high_and_rising_with_cob_insulin_effect.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = high_and_rising_with_cob_insulin_effect.json; sourceTree = ""; }; E90909CE24E34AC500F963D2 /* high_and_rising_with_cob_predicted_glucose.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = high_and_rising_with_cob_predicted_glucose.json; sourceTree = ""; }; @@ -2351,6 +2353,7 @@ C13DA2AF24F6C7690098BB29 /* UIViewController.swift */, 430B29922041F5B200BA9F93 /* UserDefaults+Loop.swift */, A9B607AF247F000F00792BE4 /* UserNotifications+Loop.swift */, + DD3DBD262A33AEC8000F8B5B /* SettingsView+algorithmExperimentsSection.swift */, ); path = Extensions; sourceTree = ""; @@ -2400,7 +2403,7 @@ 43F64DD81D9C92C900D24DC6 /* TitleSubtitleTableViewCell.swift */, 4311FB9A1F37FE1B00D4C0A7 /* TitleSubtitleTextFieldTableViewCell.swift */, C1AF062229426300002C1B19 /* ManualGlucoseEntryRow.swift */, - 9E9C350526F69B2A0077F908 /* RetrospectiveCorrectionSelectionView.swift */, + DD3DBD282A33AFE9000F8B5B /* IntegralRetrospectiveCorrectionSelectionView.swift */, ); path = Views; sourceTree = ""; @@ -3876,9 +3879,9 @@ 892D7C5123B54A15008A9656 /* CarbEntryViewController.swift in Sources */, B4E202302661063E009421B5 /* AutomaticDosingStatus.swift in Sources */, C191D2A125B3ACAA00C26C0B /* DosingStrategySelectionView.swift in Sources */, - 9E9C350626F69B2A0077F908 /* RetrospectiveCorrectionSelectionView.swift in Sources */, A977A2F424ACFECF0059C207 /* CriticalEventLogExportManager.swift in Sources */, 89CA2B32226C18B8004D9350 /* TestingScenariosTableViewController.swift in Sources */, + DD3DBD272A33AEC8000F8B5B /* SettingsView+algorithmExperimentsSection.swift in Sources */, 43E93FB71E469A5100EAB8DB /* HKUnit.swift in Sources */, 43C05CAF21EB2C24006FB252 /* NSBundle.swift in Sources */, A91D2A3F26CF0FF80023B075 /* IconTitleSubtitleTableViewCell.swift in Sources */, @@ -3898,6 +3901,7 @@ C178249A1E1999FA00D9D25C /* CaseCountable.swift in Sources */, B4F3D25124AF890C0095CE44 /* BluetoothStateManager.swift in Sources */, 1DDE273D24AEA4B000796622 /* SettingsViewModel.swift in Sources */, + DD3DBD292A33AFE9000F8B5B /* IntegralRetrospectiveCorrectionSelectionView.swift in Sources */, A9347F3124E7521800C99C34 /* CarbBackfillRequestUserInfo.swift in Sources */, A9CBE458248AB564008E7BA2 /* DoseStore+SimulatedCoreData.swift in Sources */, 897A5A9924C22DE800C4E71D /* BolusEntryViewModel.swift in Sources */, diff --git a/Loop/Extensions/SettingsView+algorithmExperimentsSection.swift b/Loop/Extensions/SettingsView+algorithmExperimentsSection.swift new file mode 100644 index 0000000000..b6f07ba186 --- /dev/null +++ b/Loop/Extensions/SettingsView+algorithmExperimentsSection.swift @@ -0,0 +1,25 @@ +// +// SettingsView+algorithmExperimentsSection.swift +// Loop +// +// Created by Jonas Björkert on 2023-06-03. +// Copyright © 2023 LoopKit Authors. All rights reserved. +// +import Foundation +import SwiftUI +import LoopKitUI + +extension SettingsView { + internal var algorithmExperimentsSection: some View { + Section(header: SectionHeader(label: NSLocalizedString("Algorithm Experiments ⚠️", comment: "The title of the Algorithm Experiments section in settings"))) { + + NavigationLink(destination: IntegralRetrospectiveCorrectionSelectionView(isIntegralRetrospectiveCorrectionEnabled: $isIntegralRetrospectiveCorrectionEnabled)) { + HStack { + Text("Integral Retrospective Correction") + Spacer() + Text(isIntegralRetrospectiveCorrectionEnabled ? "On" : "Off") + } + } + } + } +} diff --git a/Loop/Managers/AnalyticsServicesManager.swift b/Loop/Managers/AnalyticsServicesManager.swift index ff0de5fa31..682407ac43 100644 --- a/Loop/Managers/AnalyticsServicesManager.swift +++ b/Loop/Managers/AnalyticsServicesManager.swift @@ -132,10 +132,6 @@ final class AnalyticsServicesManager { if newValue.dosingEnabled != oldValue.dosingEnabled { logEvent("Closed loop enabled change") } - - if newValue.retrospectiveCorrection != oldValue.retrospectiveCorrection { - logEvent("Retrospective correction change") - } if newValue.basalRateSchedule?.timeZone != oldValue.basalRateSchedule?.timeZone { logEvent("Therapy schedule time zone change") diff --git a/Loop/Managers/LoopDataManager.swift b/Loop/Managers/LoopDataManager.swift index a8cb1d37ae..15c593e77f 100644 --- a/Loop/Managers/LoopDataManager.swift +++ b/Loop/Managers/LoopDataManager.swift @@ -72,7 +72,7 @@ final class LoopDataManager { NotificationCenter.default.removeObserver(observer) } } - + init( lastLoopCompleted: Date?, basalDeliveryState: PumpManagerStatus.BasalDeliveryState?, @@ -123,9 +123,8 @@ final class LoopDataManager { self.trustedTimeOffset = trustedTimeOffset - /// Creates an instance of the enabled retrospective correction implementation - retrospectiveCorrection = settings.enabledRetrospectiveCorrectionAlgorithm(retrospectiveCorrection: settings.retrospectiveCorrection) - + retrospectiveCorrection = settings.enabledRetrospectiveCorrectionAlgorithm() + overrideIntentObserver = UserDefaults.appGroup?.observe(\.intentExtensionOverrideToSet, options: [.new], changeHandler: {[weak self] (defaults, change) in guard let name = change.newValue??.lowercased(), let appGroup = UserDefaults.appGroup else { return @@ -318,10 +317,6 @@ final class LoopDataManager { self.insulinEffect = nil } } - - if newValue.retrospectiveCorrection != oldValue.retrospectiveCorrection { - retrospectiveCorrection = settings.enabledRetrospectiveCorrectionAlgorithm(retrospectiveCorrection: settings.retrospectiveCorrection) - } notify(forChange: .preferences) analyticsServicesManager.didChangeLoopSettings(from: oldValue, to: newValue) @@ -2109,6 +2104,8 @@ extension LoopDataManager { }), "]", + "isExperimentalIntegralRetrospectiveCorrectionEnabled: \(UserDefaults.standard.bool(forKey: "isExperimentalIntegralRetrospectiveCorrectionEnabled"))", + "retrospectiveGlucoseDiscrepancies: [", "* GlucoseEffect(start, mg/dL)", (manager.retrospectiveGlucoseDiscrepancies ?? []).reduce(into: "", { (entries, entry) in diff --git a/Loop/Models/LoopSettings+Loop.swift b/Loop/Models/LoopSettings+Loop.swift index 576aa89519..f66cfc04d1 100644 --- a/Loop/Models/LoopSettings+Loop.swift +++ b/Loop/Models/LoopSettings+Loop.swift @@ -21,14 +21,17 @@ extension LoopSettings { static let retrospectiveCorrectionEffectDuration = TimeInterval(hours: 1) /// Creates an instance of the enabled retrospective correction implementation - func enabledRetrospectiveCorrectionAlgorithm(retrospectiveCorrection: RetrospectiveCorrectionOptions) -> RetrospectiveCorrection { + func enabledRetrospectiveCorrectionAlgorithm() -> RetrospectiveCorrection { var enabledRetrospectiveCorrectionAlgorithm: RetrospectiveCorrection - switch retrospectiveCorrection { - case .standardRetrospectiveCorrection: - enabledRetrospectiveCorrectionAlgorithm = StandardRetrospectiveCorrection(effectDuration: LoopSettings.retrospectiveCorrectionEffectDuration) - case .integralRetrospectiveCorrection: + + let isIntegralRetrospectiveCorrectionEnabled = UserDefaults.standard.bool(forKey: "isExperimentalIntegralRetrospectiveCorrectionEnabled") + + if isIntegralRetrospectiveCorrectionEnabled { enabledRetrospectiveCorrectionAlgorithm = IntegralRetrospectiveCorrection(effectDuration: LoopSettings.retrospectiveCorrectionEffectDuration) + } else { + enabledRetrospectiveCorrectionAlgorithm = StandardRetrospectiveCorrection(effectDuration: LoopSettings.retrospectiveCorrectionEffectDuration) } + return enabledRetrospectiveCorrectionAlgorithm } diff --git a/Loop/View Controllers/PredictionTableViewController.swift b/Loop/View Controllers/PredictionTableViewController.swift index 616c0aa29b..a0eea7618a 100644 --- a/Loop/View Controllers/PredictionTableViewController.swift +++ b/Loop/View Controllers/PredictionTableViewController.swift @@ -276,10 +276,9 @@ class PredictionTableViewController: LoopChartsTableViewController, Identifiable format: NSLocalizedString("Predicted: %1$@\nActual: %2$@ (%3$@)", comment: "Format string describing retrospective glucose prediction comparison. (1: Predicted glucose)(2: Actual glucose)(3: difference)"), values[0], values[1], values[2] ) - switch deviceManager.settings.retrospectiveCorrection { - case .standardRetrospectiveCorrection: - subtitleText = String(format: "%@\n%@", subtitleText, retro) - case .integralRetrospectiveCorrection: + let isIntegralRetrospectiveCorrectionEnabled = UserDefaults.standard.bool(forKey: "isExperimentalIntegralRetrospectiveCorrectionEnabled") + + if isIntegralRetrospectiveCorrectionEnabled { var integralEffectDisplay = "?" var totalEffectDisplay = "?" if let totalEffect = self.totalRetrospectiveCorrection { @@ -293,6 +292,8 @@ class PredictionTableViewController: LoopChartsTableViewController, Identifiable integralEffectDisplay, totalEffectDisplay ) subtitleText = String(format: "%@\n%@", retro, integralRetro) + } else { + subtitleText = String(format: "%@\n%@", subtitleText, retro) } } diff --git a/Loop/View Controllers/StatusTableViewController.swift b/Loop/View Controllers/StatusTableViewController.swift index 17a6a2c7bf..da7072498b 100644 --- a/Loop/View Controllers/StatusTableViewController.swift +++ b/Loop/View Controllers/StatusTableViewController.swift @@ -1547,7 +1547,6 @@ final class StatusTableViewController: LoopChartsTableViewController { initialDosingEnabled: deviceManager.loopManager.settings.dosingEnabled, isClosedLoopAllowed: automaticDosingStatus.$isAutomaticDosingAllowed, automaticDosingStrategy: deviceManager.loopManager.settings.automaticDosingStrategy, - retrospectiveCorrection: deviceManager.loopManager.settings.retrospectiveCorrection, availableSupports: supportManager.availableSupports, isOnboardingComplete: onboardingManager.isComplete, therapySettingsViewModelDelegate: deviceManager, @@ -2133,12 +2132,6 @@ extension StatusTableViewController: SettingsViewModelDelegate { var closedLoopDescriptiveText: String? { return deviceManager.closedLoopDisallowedLocalizedDescription } - - func retrospectiveCorrectionChanged(_ retrospectiveCorrection: RetrospectiveCorrectionOptions) { - self.deviceManager.loopManager.mutateSettings { settings in - settings.retrospectiveCorrection = retrospectiveCorrection - } - } func dosingEnabledChanged(_ value: Bool) { deviceManager.loopManager.mutateSettings { settings in diff --git a/Loop/View Models/SettingsViewModel.swift b/Loop/View Models/SettingsViewModel.swift index e339e78e61..3e3c20be24 100644 --- a/Loop/View Models/SettingsViewModel.swift +++ b/Loop/View Models/SettingsViewModel.swift @@ -53,7 +53,6 @@ public protocol SettingsViewModelDelegate: AnyObject { func dosingEnabledChanged(_: Bool) func dosingStrategyChanged(_: AutomaticDosingStrategy) func didTapIssueReport() - func retrospectiveCorrectionChanged(_: RetrospectiveCorrectionOptions) var closedLoopDescriptiveText: String? { get } } @@ -93,12 +92,6 @@ public class SettingsViewModel: ObservableObject { delegate?.dosingStrategyChanged(automaticDosingStrategy) } } - - @Published var retrospectiveCorrection: RetrospectiveCorrectionOptions { - didSet { - delegate?.retrospectiveCorrectionChanged(retrospectiveCorrection) - } - } var closedLoopPreference: Bool { didSet { @@ -120,7 +113,6 @@ public class SettingsViewModel: ObservableObject { initialDosingEnabled: Bool, isClosedLoopAllowed: Published.Publisher, automaticDosingStrategy: AutomaticDosingStrategy, - retrospectiveCorrection: RetrospectiveCorrectionOptions, availableSupports: [SupportUI], isOnboardingComplete: Bool, therapySettingsViewModelDelegate: TherapySettingsViewModelDelegate?, @@ -138,7 +130,6 @@ public class SettingsViewModel: ObservableObject { self.closedLoopPreference = initialDosingEnabled self.isClosedLoopAllowed = false self.automaticDosingStrategy = automaticDosingStrategy - self.retrospectiveCorrection = retrospectiveCorrection self.availableSupports = availableSupports self.isOnboardingComplete = isOnboardingComplete self.therapySettingsViewModelDelegate = therapySettingsViewModelDelegate @@ -187,7 +178,6 @@ extension SettingsViewModel { initialDosingEnabled: true, isClosedLoopAllowed: FakeClosedLoopAllowedPublisher().$mockIsClosedLoopAllowed, automaticDosingStrategy: .automaticBolus, - retrospectiveCorrection: .standardRetrospectiveCorrection, availableSupports: [], isOnboardingComplete: false, therapySettingsViewModelDelegate: nil, diff --git a/Loop/Views/IntegralRetrospectiveCorrectionSelectionView.swift b/Loop/Views/IntegralRetrospectiveCorrectionSelectionView.swift new file mode 100644 index 0000000000..b38cba6bf3 --- /dev/null +++ b/Loop/Views/IntegralRetrospectiveCorrectionSelectionView.swift @@ -0,0 +1,54 @@ +// +// IntegralRetrospectiveCorrectionSelectionView.swift +// Loop +// +// Created by Jonas Björkert on 2023-06-04. +// Copyright © 2023 LoopKit Authors. All rights reserved. +// +import Foundation +import SwiftUI +import LoopKit +import LoopKitUI + +public struct IntegralRetrospectiveCorrectionSelectionView: View { + @Binding var isIntegralRetrospectiveCorrectionEnabled: Bool + + public init(isIntegralRetrospectiveCorrectionEnabled: Binding) { + self._isIntegralRetrospectiveCorrectionEnabled = isIntegralRetrospectiveCorrectionEnabled + } + + public var body: some View { + List { + retrospectiveCorrectionSection + } + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .principal) { + VStack { + Text("Integral") + .font(.headline) + Text("Retrospective Correction") + .font(.subheadline) + } + } + } + } + + private var retrospectiveCorrectionSection: some View { + VStack { + DescriptiveText(label: NSLocalizedString("Integral Retrospective Correction (IRC) is an advanced control technique applied to glucose forecasting based on the history of discrepancies between predicted and actual glucose levels. The predictions are made using carbohydrate and insulin data. When enabled, IRC adjusts insulin delivery in response to consistent patterns: it increases insulin delivery when glucose levels consistently measure higher than expected, and decreases it when glucose levels are consistently lower than expected. IRC uses a proportional-integral-differential (PID) controller that adjusts insulin recommendations based on immediate, accumulated, and rate of change discrepancies. This provides a more adaptive and responsive control compared to standard retrospective correction. However, it's important to know that the effectiveness of IRC will heavily depend on the accuracy of your insulin sensitivity, carbohydrate ratios, and basal rates settings. While IRC can improve glucose management in cases of consistent discrepancies, please note that it might potentially lead to more aggressive corrections.", comment: "Description of Integral Retrospective Correction toggle."), color: .black) + Section() { + Toggle(NSLocalizedString("Integral Retrospective Correction", comment: "Title for Integral Retrospective Correction toggle"), isOn: $isIntegralRetrospectiveCorrectionEnabled) + .onChange(of: isIntegralRetrospectiveCorrectionEnabled) { newValue in + UserDefaults.standard.set(newValue, forKey: "isExperimentalIntegralRetrospectiveCorrectionEnabled") + } + } + } + } +} + +struct EnhancedAutoBolusSelectionView_Previews: PreviewProvider { + static var previews: some View { + IntegralRetrospectiveCorrectionSelectionView(isIntegralRetrospectiveCorrectionEnabled: .constant(true)) + } +} diff --git a/Loop/Views/RetrospectiveCorrectionSelectionView.swift b/Loop/Views/RetrospectiveCorrectionSelectionView.swift deleted file mode 100644 index d2cf6174fb..0000000000 --- a/Loop/Views/RetrospectiveCorrectionSelectionView.swift +++ /dev/null @@ -1,70 +0,0 @@ -// -// RetrospectiveCorrectionSelectionView.swift -// Loop -// -// Created by Dragan Maksimovic on 9/18/21. -// Copyright © 2021 LoopKit Authors. All rights reserved. -// - -import SwiftUI -import LoopCore -import LoopKitUI - -public struct RetrospectiveCorrectionSelectionView: View { - - @Binding private var retrospectiveCorrection: RetrospectiveCorrectionOptions - - @State private var internalRetrospectiveCorrection: RetrospectiveCorrectionOptions - - public init(retrospectiveCorrection: Binding) { - self._retrospectiveCorrection = retrospectiveCorrection - self._internalRetrospectiveCorrection = State(initialValue: retrospectiveCorrection.wrappedValue) - } - - public var body: some View { - List { - Section { - options - } - .buttonStyle(PlainButtonStyle()) // Disable row highlighting on selection - } - .insetGroupedListStyle() - } - - public var options: some View { - ForEach(RetrospectiveCorrectionOptions.allCases, id: \.self) { rcOption in - CheckmarkListItem( - title: Text(rcOption.title), - description: Text(rcOption.informationalText), - isSelected: Binding( - get: { self.retrospectiveCorrection == rcOption }, - set: { isSelected in - if isSelected { - self.retrospectiveCorrection = rcOption - self.internalRetrospectiveCorrection = rcOption // Hack to force update. :( - } - } - ) - ) - .padding(.vertical, 4) - } - } -} - -extension RetrospectiveCorrectionOptions { - var informationalText: String { - switch self { - case .standardRetrospectiveCorrection: - return NSLocalizedString("Correcton to glucose forecast based on the most recent 30 min comparison of glucose prediction vs actual, continued with decay over 60 min.", comment: "Description string for standard retrospective correction") - case .integralRetrospectiveCorrection: - return NSLocalizedString("Correction to glucose forecast based on the history of discrepancies between glucose prediction based on carb and insuln data vs actual. Results in increased insulin corrections when glucose is persistently higher than expected, and in reduced insulin delivery when glucose is persistently lower than expected.", comment: "Description string for integral retrospective correction") - } - } - -} - -struct RetrospectiveCorrectionSelectionView_Previews: PreviewProvider { - static var previews: some View { - RetrospectiveCorrectionSelectionView(retrospectiveCorrection: .constant(.standardRetrospectiveCorrection)) - } -} diff --git a/Loop/Views/SettingsView.swift b/Loop/Views/SettingsView.swift index 03ed8e9964..7f9dd33fdf 100644 --- a/Loop/Views/SettingsView.swift +++ b/Loop/Views/SettingsView.swift @@ -30,7 +30,8 @@ public struct SettingsView: View { @State private var therapySettingsIsPresented: Bool = false @State private var deletePumpDataAlertIsPresented = false @State private var deleteCGMDataAlertIsPresented = false - + @State internal var isIntegralRetrospectiveCorrectionEnabled = UserDefaults.standard.bool(forKey: "isExperimentalIntegralRetrospectiveCorrectionEnabled") + var localizedAppNameAndVersion: String public init(viewModel: SettingsViewModel, localizedAppNameAndVersion: String) { @@ -51,7 +52,9 @@ public struct SettingsView: View { dosingStrategySection } alertManagementSection - retrospectiveCorrectionSection + // Note: Experimental Section + algorithmExperimentsSection + // if viewModel.pumpManagerSettingsViewModel.isSetUp() { configurationSection } @@ -175,19 +178,6 @@ extension SettingsView { } } - private var retrospectiveCorrectionSection: some View { - Section(header: SectionHeader(label: NSLocalizedString("Retrospective Correction", comment: "The title of the Retrospective Correction section in settings"))) { - - NavigationLink(destination: RetrospectiveCorrectionSelectionView(retrospectiveCorrection: $viewModel.retrospectiveCorrection)) - { - HStack { - Text(viewModel.retrospectiveCorrection.title) - } - } - } - } - - private var alertManagementSection: some View { Section { NavigationLink(destination: AlertManagementView(checker: viewModel.alertPermissionsChecker, alertMuter: viewModel.alertMuter)) @@ -505,24 +495,6 @@ public struct SettingsView_Previews: PreviewProvider { public static var previews: some View { let displayGlucoseUnitObservable = DisplayGlucoseUnitObservable(displayGlucoseUnit: .milligramsPerDeciliter) let viewModel = SettingsViewModel.preview - /* DM check - let viewModel = SettingsViewModel(notificationsCriticalAlertPermissionsViewModel: NotificationsCriticalAlertPermissionsViewModel(), - pumpManagerSettingsViewModel: DeviceViewModel(), - cgmManagerSettingsViewModel: DeviceViewModel(), - servicesViewModel: servicesViewModel, - criticalEventLogExportViewModel: CriticalEventLogExportViewModel(exporterFactory: MockCriticalEventLogExporterFactory()), - therapySettings: { TherapySettings() }, - pumpSupportedIncrements: nil, - syncPumpSchedule: nil, - sensitivityOverridesEnabled: false, - initialDosingEnabled: true, - isClosedLoopAllowed: fakeClosedLoopAllowedPublisher.$mockIsClosedLoopAllowed, - supportInfoProvider: MockSupportInfoProvider(), - dosingStrategy: .automaticBolus, - retrospectiveCorrection: .standardRetrospectiveCorrection, - availableSupports: [], - delegate: nil) -*/ return Group { SettingsView(viewModel: viewModel, localizedAppNameAndVersion: "Loop Demo V1") .colorScheme(.light) diff --git a/LoopCore/LoopSettings.swift b/LoopCore/LoopSettings.swift index 1da7ab2d13..63418ad4aa 100644 --- a/LoopCore/LoopSettings.swift +++ b/LoopCore/LoopSettings.swift @@ -19,22 +19,6 @@ public extension AutomaticDosingStrategy { } } -public enum RetrospectiveCorrectionOptions: Int, CaseIterable { - case standardRetrospectiveCorrection - case integralRetrospectiveCorrection -} - -public extension RetrospectiveCorrectionOptions { - var title: String { - switch self { - case .standardRetrospectiveCorrection: - return NSLocalizedString("Standard Retrospective Correction", comment: "Title string for standard retrospective correction") - case .integralRetrospectiveCorrection: - return NSLocalizedString("Integral Retrospective Correction", comment: "Title string for integral retrospective correction") - } - } -} - public struct LoopSettings: Equatable { public var isScheduleOverrideInfiniteWorkout: Bool { guard let scheduleOverride = scheduleOverride else { return false } @@ -90,8 +74,6 @@ public struct LoopSettings: Equatable { public var automaticDosingStrategy: AutomaticDosingStrategy = .tempBasalOnly public var defaultRapidActingModel: ExponentialInsulinModelPreset? - - public var retrospectiveCorrection: RetrospectiveCorrectionOptions = .integralRetrospectiveCorrection public var glucoseUnit: HKUnit? { return glucoseTargetRangeSchedule?.unit @@ -112,7 +94,6 @@ public struct LoopSettings: Equatable { maximumBolus: Double? = nil, suspendThreshold: GlucoseThreshold? = nil, automaticDosingStrategy: AutomaticDosingStrategy = .tempBasalOnly, - retrospectiveCorrection: RetrospectiveCorrectionOptions = .integralRetrospectiveCorrection, defaultRapidActingModel: ExponentialInsulinModelPreset? = nil ) { self.dosingEnabled = dosingEnabled @@ -129,7 +110,6 @@ public struct LoopSettings: Equatable { self.maximumBolus = maximumBolus self.suspendThreshold = suspendThreshold self.automaticDosingStrategy = automaticDosingStrategy - self.retrospectiveCorrection = retrospectiveCorrection self.defaultRapidActingModel = defaultRapidActingModel } } @@ -297,11 +277,6 @@ extension LoopSettings: RawRepresentable { { self.automaticDosingStrategy = automaticDosingStrategy } - - if let rawRetrospectiveCorrection = rawValue["retrospectiveCorrection"] as? RetrospectiveCorrectionOptions.RawValue, - let retrospectiveCorrection = RetrospectiveCorrectionOptions(rawValue: rawRetrospectiveCorrection) { - self.retrospectiveCorrection = retrospectiveCorrection - } } public var rawValue: RawValue { @@ -320,7 +295,6 @@ extension LoopSettings: RawRepresentable { raw["maximumBolus"] = maximumBolus raw["minimumBGGuard"] = suspendThreshold?.rawValue raw["dosingStrategy"] = automaticDosingStrategy.rawValue - raw["retrospectiveCorrection"] = retrospectiveCorrection.rawValue return raw }