Procházet zdrojové kódy

Override rework (#54)

* rework overrides

* fixes, refactoring...highly wip

* fixes

* extend edit sheet, fixes, show current active Profile

* UI fixes

* title, header of presets section

* reset state variables when adding a preset or enabling a preset

* refactoring

* add override sheet

* fixes

* more fixes

* refactoring

* fix for edit sheet

* fix for profile name

* fixes for decimal textfield and for bolus view

* fix override target in edit sheet

* Fix removing ended overrides by scheduling disabling

---------

Co-authored-by: Deniz Cengiz <d.c.cengiz@googlemail.com>
polscm32 před 1 rokem
rodič
revize
f33988e1f1

+ 20 - 20
FreeAPS.xcodeproj/project.pbxproj

@@ -21,7 +21,6 @@
 		1927C8E62744606D00347C69 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1927C8E82744606D00347C69 /* InfoPlist.strings */; };
 		1927C8E62744606D00347C69 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1927C8E82744606D00347C69 /* InfoPlist.strings */; };
 		1935364028496F7D001E0B16 /* Oref2_variables.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1935363F28496F7D001E0B16 /* Oref2_variables.swift */; };
 		1935364028496F7D001E0B16 /* Oref2_variables.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1935363F28496F7D001E0B16 /* Oref2_variables.swift */; };
 		193F6CDD2A512C8F001240FD /* Loops.swift in Sources */ = {isa = PBXBuildFile; fileRef = 193F6CDC2A512C8F001240FD /* Loops.swift */; };
 		193F6CDD2A512C8F001240FD /* Loops.swift in Sources */ = {isa = PBXBuildFile; fileRef = 193F6CDC2A512C8F001240FD /* Loops.swift */; };
-		1956FB212AFF79E200C7B4FF /* CoreDataStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1956FB202AFF79E200C7B4FF /* CoreDataStorage.swift */; };
 		195D80B42AF6973A00D25097 /* DynamicRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 195D80B32AF6973A00D25097 /* DynamicRootView.swift */; };
 		195D80B42AF6973A00D25097 /* DynamicRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 195D80B32AF6973A00D25097 /* DynamicRootView.swift */; };
 		195D80B72AF697B800D25097 /* DynamicDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 195D80B62AF697B800D25097 /* DynamicDataFlow.swift */; };
 		195D80B72AF697B800D25097 /* DynamicDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 195D80B62AF697B800D25097 /* DynamicDataFlow.swift */; };
 		195D80B92AF697F700D25097 /* DynamicProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 195D80B82AF697F700D25097 /* DynamicProvider.swift */; };
 		195D80B92AF697F700D25097 /* DynamicProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 195D80B82AF697F700D25097 /* DynamicProvider.swift */; };
@@ -275,8 +274,6 @@
 		5825D1412BD4058F00F36E9B /* TempTargetsSlider+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D1132BD4058F00F36E9B /* TempTargetsSlider+CoreDataProperties.swift */; };
 		5825D1412BD4058F00F36E9B /* TempTargetsSlider+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D1132BD4058F00F36E9B /* TempTargetsSlider+CoreDataProperties.swift */; };
 		5825D1422BD4058F00F36E9B /* Presets+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D1142BD4058F00F36E9B /* Presets+CoreDataClass.swift */; };
 		5825D1422BD4058F00F36E9B /* Presets+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D1142BD4058F00F36E9B /* Presets+CoreDataClass.swift */; };
 		5825D1432BD4058F00F36E9B /* Presets+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D1152BD4058F00F36E9B /* Presets+CoreDataProperties.swift */; };
 		5825D1432BD4058F00F36E9B /* Presets+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D1152BD4058F00F36E9B /* Presets+CoreDataProperties.swift */; };
-		5825D1482BD4058F00F36E9B /* Override+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D11A2BD4058F00F36E9B /* Override+CoreDataClass.swift */; };
-		5825D1492BD4058F00F36E9B /* Override+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D11B2BD4058F00F36E9B /* Override+CoreDataProperties.swift */; };
 		5825D14A2BD4058F00F36E9B /* OrefDetermination+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D11C2BD4058F00F36E9B /* OrefDetermination+CoreDataClass.swift */; };
 		5825D14A2BD4058F00F36E9B /* OrefDetermination+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D11C2BD4058F00F36E9B /* OrefDetermination+CoreDataClass.swift */; };
 		5825D14B2BD4058F00F36E9B /* OrefDetermination+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D11D2BD4058F00F36E9B /* OrefDetermination+CoreDataProperties.swift */; };
 		5825D14B2BD4058F00F36E9B /* OrefDetermination+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D11D2BD4058F00F36E9B /* OrefDetermination+CoreDataProperties.swift */; };
 		5825D14C2BD4058F00F36E9B /* TempTargets+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D11E2BD4058F00F36E9B /* TempTargets+CoreDataClass.swift */; };
 		5825D14C2BD4058F00F36E9B /* TempTargets+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D11E2BD4058F00F36E9B /* TempTargets+CoreDataClass.swift */; };
@@ -285,8 +282,6 @@
 		5825D1552BD4058F00F36E9B /* CarbEntryStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D1272BD4058F00F36E9B /* CarbEntryStored+CoreDataProperties.swift */; };
 		5825D1552BD4058F00F36E9B /* CarbEntryStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D1272BD4058F00F36E9B /* CarbEntryStored+CoreDataProperties.swift */; };
 		5825D1582BD4058F00F36E9B /* StatsData+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D12A2BD4058F00F36E9B /* StatsData+CoreDataClass.swift */; };
 		5825D1582BD4058F00F36E9B /* StatsData+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D12A2BD4058F00F36E9B /* StatsData+CoreDataClass.swift */; };
 		5825D1592BD4058F00F36E9B /* StatsData+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D12B2BD4058F00F36E9B /* StatsData+CoreDataProperties.swift */; };
 		5825D1592BD4058F00F36E9B /* StatsData+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D12B2BD4058F00F36E9B /* StatsData+CoreDataProperties.swift */; };
-		5825D15A2BD4058F00F36E9B /* OverridePresets+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D12C2BD4058F00F36E9B /* OverridePresets+CoreDataClass.swift */; };
-		5825D15B2BD4058F00F36E9B /* OverridePresets+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D12D2BD4058F00F36E9B /* OverridePresets+CoreDataProperties.swift */; };
 		582FAE432C05102C00D1C13F /* CoreDataError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582FAE422C05102C00D1C13F /* CoreDataError.swift */; };
 		582FAE432C05102C00D1C13F /* CoreDataError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582FAE422C05102C00D1C13F /* CoreDataError.swift */; };
 		583684062BD178DB00070A60 /* GlucoseStored+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583684052BD178DB00070A60 /* GlucoseStored+helper.swift */; };
 		583684062BD178DB00070A60 /* GlucoseStored+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583684052BD178DB00070A60 /* GlucoseStored+helper.swift */; };
 		583684082BD195A700070A60 /* Determination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583684072BD195A700070A60 /* Determination.swift */; };
 		583684082BD195A700070A60 /* Determination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583684072BD195A700070A60 /* Determination.swift */; };
@@ -359,6 +354,11 @@
 		BDB3C1042C0341E600CEEAA1 /* TempBasalStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDB3C0FE2C0341E500CEEAA1 /* TempBasalStored+CoreDataClass.swift */; };
 		BDB3C1042C0341E600CEEAA1 /* TempBasalStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDB3C0FE2C0341E500CEEAA1 /* TempBasalStored+CoreDataClass.swift */; };
 		BDB3C1052C0341E600CEEAA1 /* TempBasalStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDB3C0FF2C0341E500CEEAA1 /* TempBasalStored+CoreDataProperties.swift */; };
 		BDB3C1052C0341E600CEEAA1 /* TempBasalStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDB3C0FF2C0341E500CEEAA1 /* TempBasalStored+CoreDataProperties.swift */; };
 		BDB3C1192C03DD1000CEEAA1 /* UserDefaultsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDB3C1182C03DD1000CEEAA1 /* UserDefaultsExtension.swift */; };
 		BDB3C1192C03DD1000CEEAA1 /* UserDefaultsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDB3C1182C03DD1000CEEAA1 /* UserDefaultsExtension.swift */; };
+		BDCD47AF2C1F3F1700F8BCD5 /* OverrideStored+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDCD47AE2C1F3F1700F8BCD5 /* OverrideStored+helper.swift */; };
+		BDCD47BA2C2203A600F8BCD5 /* EditProfileForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDCD47B92C2203A600F8BCD5 /* EditProfileForm.swift */; };
+		BDCD47C32C26331400F8BCD5 /* AddProfileForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDCD47C22C26331400F8BCD5 /* AddProfileForm.swift */; };
+		BDCD47DE2C2861F400F8BCD5 /* OverrideStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDCD47DC2C2861F400F8BCD5 /* OverrideStored+CoreDataClass.swift */; };
+		BDCD47DF2C2861F400F8BCD5 /* OverrideStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDCD47DD2C2861F400F8BCD5 /* OverrideStored+CoreDataProperties.swift */; };
 		BDF34EBE2C0A31D100D51995 /* CustomNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDF34EBD2C0A31D000D51995 /* CustomNotification.swift */; };
 		BDF34EBE2C0A31D100D51995 /* CustomNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDF34EBD2C0A31D000D51995 /* CustomNotification.swift */; };
 		BDF34F832C10C5B600D51995 /* DataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDF34F822C10C5B600D51995 /* DataManager.swift */; };
 		BDF34F832C10C5B600D51995 /* DataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDF34F822C10C5B600D51995 /* DataManager.swift */; };
 		BDF34F852C10C62E00D51995 /* GlucoseData.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDF34F842C10C62E00D51995 /* GlucoseData.swift */; };
 		BDF34F852C10C62E00D51995 /* GlucoseData.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDF34F842C10C62E00D51995 /* GlucoseData.swift */; };
@@ -607,7 +607,6 @@
 		1927C8FE274489BA00347C69 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		1927C8FE274489BA00347C69 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		1935363F28496F7D001E0B16 /* Oref2_variables.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Oref2_variables.swift; sourceTree = "<group>"; };
 		1935363F28496F7D001E0B16 /* Oref2_variables.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Oref2_variables.swift; sourceTree = "<group>"; };
 		193F6CDC2A512C8F001240FD /* Loops.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Loops.swift; sourceTree = "<group>"; };
 		193F6CDC2A512C8F001240FD /* Loops.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Loops.swift; sourceTree = "<group>"; };
-		1956FB202AFF79E200C7B4FF /* CoreDataStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataStorage.swift; sourceTree = "<group>"; };
 		195D80B32AF6973A00D25097 /* DynamicRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicRootView.swift; sourceTree = "<group>"; };
 		195D80B32AF6973A00D25097 /* DynamicRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicRootView.swift; sourceTree = "<group>"; };
 		195D80B62AF697B800D25097 /* DynamicDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicDataFlow.swift; sourceTree = "<group>"; };
 		195D80B62AF697B800D25097 /* DynamicDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicDataFlow.swift; sourceTree = "<group>"; };
 		195D80B82AF697F700D25097 /* DynamicProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicProvider.swift; sourceTree = "<group>"; };
 		195D80B82AF697F700D25097 /* DynamicProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicProvider.swift; sourceTree = "<group>"; };
@@ -881,8 +880,6 @@
 		5825D1132BD4058F00F36E9B /* TempTargetsSlider+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargetsSlider+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		5825D1132BD4058F00F36E9B /* TempTargetsSlider+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargetsSlider+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		5825D1142BD4058F00F36E9B /* Presets+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Presets+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
 		5825D1142BD4058F00F36E9B /* Presets+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Presets+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
 		5825D1152BD4058F00F36E9B /* Presets+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Presets+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		5825D1152BD4058F00F36E9B /* Presets+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Presets+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
-		5825D11A2BD4058F00F36E9B /* Override+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Override+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
-		5825D11B2BD4058F00F36E9B /* Override+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Override+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		5825D11C2BD4058F00F36E9B /* OrefDetermination+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrefDetermination+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
 		5825D11C2BD4058F00F36E9B /* OrefDetermination+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrefDetermination+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
 		5825D11D2BD4058F00F36E9B /* OrefDetermination+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrefDetermination+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		5825D11D2BD4058F00F36E9B /* OrefDetermination+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrefDetermination+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		5825D11E2BD4058F00F36E9B /* TempTargets+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargets+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
 		5825D11E2BD4058F00F36E9B /* TempTargets+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargets+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
@@ -891,8 +888,6 @@
 		5825D1272BD4058F00F36E9B /* CarbEntryStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CarbEntryStored+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		5825D1272BD4058F00F36E9B /* CarbEntryStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CarbEntryStored+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		5825D12A2BD4058F00F36E9B /* StatsData+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatsData+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
 		5825D12A2BD4058F00F36E9B /* StatsData+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatsData+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
 		5825D12B2BD4058F00F36E9B /* StatsData+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatsData+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		5825D12B2BD4058F00F36E9B /* StatsData+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatsData+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
-		5825D12C2BD4058F00F36E9B /* OverridePresets+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OverridePresets+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
-		5825D12D2BD4058F00F36E9B /* OverridePresets+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OverridePresets+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		582FAE422C05102C00D1C13F /* CoreDataError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataError.swift; sourceTree = "<group>"; };
 		582FAE422C05102C00D1C13F /* CoreDataError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataError.swift; sourceTree = "<group>"; };
 		583684052BD178DB00070A60 /* GlucoseStored+helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GlucoseStored+helper.swift"; sourceTree = "<group>"; };
 		583684052BD178DB00070A60 /* GlucoseStored+helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GlucoseStored+helper.swift"; sourceTree = "<group>"; };
 		583684072BD195A700070A60 /* Determination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Determination.swift; sourceTree = "<group>"; };
 		583684072BD195A700070A60 /* Determination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Determination.swift; sourceTree = "<group>"; };
@@ -966,6 +961,11 @@
 		BDB3C0FE2C0341E500CEEAA1 /* TempBasalStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempBasalStored+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
 		BDB3C0FE2C0341E500CEEAA1 /* TempBasalStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempBasalStored+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
 		BDB3C0FF2C0341E500CEEAA1 /* TempBasalStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempBasalStored+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		BDB3C0FF2C0341E500CEEAA1 /* TempBasalStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempBasalStored+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		BDB3C1182C03DD1000CEEAA1 /* UserDefaultsExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsExtension.swift; sourceTree = "<group>"; };
 		BDB3C1182C03DD1000CEEAA1 /* UserDefaultsExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsExtension.swift; sourceTree = "<group>"; };
+		BDCD47AE2C1F3F1700F8BCD5 /* OverrideStored+helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OverrideStored+helper.swift"; sourceTree = "<group>"; };
+		BDCD47B92C2203A600F8BCD5 /* EditProfileForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileForm.swift; sourceTree = "<group>"; };
+		BDCD47C22C26331400F8BCD5 /* AddProfileForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddProfileForm.swift; sourceTree = "<group>"; };
+		BDCD47DC2C2861F400F8BCD5 /* OverrideStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OverrideStored+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
+		BDCD47DD2C2861F400F8BCD5 /* OverrideStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OverrideStored+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		BDF34EBD2C0A31D000D51995 /* CustomNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomNotification.swift; sourceTree = "<group>"; };
 		BDF34EBD2C0A31D000D51995 /* CustomNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomNotification.swift; sourceTree = "<group>"; };
 		BDF34F822C10C5B600D51995 /* DataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataManager.swift; sourceTree = "<group>"; };
 		BDF34F822C10C5B600D51995 /* DataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataManager.swift; sourceTree = "<group>"; };
 		BDF34F842C10C62E00D51995 /* GlucoseData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseData.swift; sourceTree = "<group>"; };
 		BDF34F842C10C62E00D51995 /* GlucoseData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseData.swift; sourceTree = "<group>"; };
@@ -1275,6 +1275,8 @@
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
 				19DC678429CA67A400FD9EC4 /* OverrideProfilesRootView.swift */,
 				19DC678429CA67A400FD9EC4 /* OverrideProfilesRootView.swift */,
+				BDCD47B92C2203A600F8BCD5 /* EditProfileForm.swift */,
+				BDCD47C22C26331400F8BCD5 /* AddProfileForm.swift */,
 			);
 			);
 			path = View;
 			path = View;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -2122,6 +2124,8 @@
 		5825D1052BD4056700F36E9B /* Classes+Properties */ = {
 		5825D1052BD4056700F36E9B /* Classes+Properties */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
+				BDCD47DC2C2861F400F8BCD5 /* OverrideStored+CoreDataClass.swift */,
+				BDCD47DD2C2861F400F8BCD5 /* OverrideStored+CoreDataProperties.swift */,
 				BDB3C0FA2C0341E500CEEAA1 /* BolusStored+CoreDataClass.swift */,
 				BDB3C0FA2C0341E500CEEAA1 /* BolusStored+CoreDataClass.swift */,
 				BDB3C0FB2C0341E500CEEAA1 /* BolusStored+CoreDataProperties.swift */,
 				BDB3C0FB2C0341E500CEEAA1 /* BolusStored+CoreDataProperties.swift */,
 				BDB3C0FC2C0341E500CEEAA1 /* PumpEventStored+CoreDataClass.swift */,
 				BDB3C0FC2C0341E500CEEAA1 /* PumpEventStored+CoreDataClass.swift */,
@@ -2144,8 +2148,6 @@
 				5825D1132BD4058F00F36E9B /* TempTargetsSlider+CoreDataProperties.swift */,
 				5825D1132BD4058F00F36E9B /* TempTargetsSlider+CoreDataProperties.swift */,
 				5825D1142BD4058F00F36E9B /* Presets+CoreDataClass.swift */,
 				5825D1142BD4058F00F36E9B /* Presets+CoreDataClass.swift */,
 				5825D1152BD4058F00F36E9B /* Presets+CoreDataProperties.swift */,
 				5825D1152BD4058F00F36E9B /* Presets+CoreDataProperties.swift */,
-				5825D11A2BD4058F00F36E9B /* Override+CoreDataClass.swift */,
-				5825D11B2BD4058F00F36E9B /* Override+CoreDataProperties.swift */,
 				5825D11C2BD4058F00F36E9B /* OrefDetermination+CoreDataClass.swift */,
 				5825D11C2BD4058F00F36E9B /* OrefDetermination+CoreDataClass.swift */,
 				5825D11D2BD4058F00F36E9B /* OrefDetermination+CoreDataProperties.swift */,
 				5825D11D2BD4058F00F36E9B /* OrefDetermination+CoreDataProperties.swift */,
 				5825D11E2BD4058F00F36E9B /* TempTargets+CoreDataClass.swift */,
 				5825D11E2BD4058F00F36E9B /* TempTargets+CoreDataClass.swift */,
@@ -2154,8 +2156,6 @@
 				5825D1272BD4058F00F36E9B /* CarbEntryStored+CoreDataProperties.swift */,
 				5825D1272BD4058F00F36E9B /* CarbEntryStored+CoreDataProperties.swift */,
 				5825D12A2BD4058F00F36E9B /* StatsData+CoreDataClass.swift */,
 				5825D12A2BD4058F00F36E9B /* StatsData+CoreDataClass.swift */,
 				5825D12B2BD4058F00F36E9B /* StatsData+CoreDataProperties.swift */,
 				5825D12B2BD4058F00F36E9B /* StatsData+CoreDataProperties.swift */,
-				5825D12C2BD4058F00F36E9B /* OverridePresets+CoreDataClass.swift */,
-				5825D12D2BD4058F00F36E9B /* OverridePresets+CoreDataProperties.swift */,
 			);
 			);
 			path = "Classes+Properties";
 			path = "Classes+Properties";
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -2163,7 +2163,6 @@
 		5825D1622BD405AE00F36E9B /* Helper */ = {
 		5825D1622BD405AE00F36E9B /* Helper */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
-				1956FB202AFF79E200C7B4FF /* CoreDataStorage.swift */,
 				581516A82BCEEDF800BF67D7 /* NSPredicates.swift */,
 				581516A82BCEEDF800BF67D7 /* NSPredicates.swift */,
 				583684052BD178DB00070A60 /* GlucoseStored+helper.swift */,
 				583684052BD178DB00070A60 /* GlucoseStored+helper.swift */,
 				58F107732BD1A4D000B1A680 /* Determination+helper.swift */,
 				58F107732BD1A4D000B1A680 /* Determination+helper.swift */,
@@ -2175,6 +2174,7 @@
 				BDB3C1182C03DD1000CEEAA1 /* UserDefaultsExtension.swift */,
 				BDB3C1182C03DD1000CEEAA1 /* UserDefaultsExtension.swift */,
 				582FAE422C05102C00D1C13F /* CoreDataError.swift */,
 				582FAE422C05102C00D1C13F /* CoreDataError.swift */,
 				BDF34EBD2C0A31D000D51995 /* CustomNotification.swift */,
 				BDF34EBD2C0A31D000D51995 /* CustomNotification.swift */,
+				BDCD47AE2C1F3F1700F8BCD5 /* OverrideStored+helper.swift */,
 			);
 			);
 			path = Helper;
 			path = Helper;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -2889,9 +2889,7 @@
 				38B4F3CD25E5031100E76A18 /* Broadcaster.swift in Sources */,
 				38B4F3CD25E5031100E76A18 /* Broadcaster.swift in Sources */,
 				383420D925FFEB3F002D46C1 /* Popup.swift in Sources */,
 				383420D925FFEB3F002D46C1 /* Popup.swift in Sources */,
 				3811DE3025C9D49500A708ED /* HomeStateModel.swift in Sources */,
 				3811DE3025C9D49500A708ED /* HomeStateModel.swift in Sources */,
-				5825D15A2BD4058F00F36E9B /* OverridePresets+CoreDataClass.swift in Sources */,
 				38BF021725E7CBBC00579895 /* PumpManagerExtensions.swift in Sources */,
 				38BF021725E7CBBC00579895 /* PumpManagerExtensions.swift in Sources */,
-				5825D1492BD4058F00F36E9B /* Override+CoreDataProperties.swift in Sources */,
 				19F95FF529F10FCF00314DDC /* StatProvider.swift in Sources */,
 				19F95FF529F10FCF00314DDC /* StatProvider.swift in Sources */,
 				38F3B2EF25ED8E2A005C48AA /* TempTargetsStorage.swift in Sources */,
 				38F3B2EF25ED8E2A005C48AA /* TempTargetsStorage.swift in Sources */,
 				19B0EF2128F6D66200069496 /* Statistics.swift in Sources */,
 				19B0EF2128F6D66200069496 /* Statistics.swift in Sources */,
@@ -2901,6 +2899,7 @@
 				3811DE1725C9D40400A708ED /* Screen.swift in Sources */,
 				3811DE1725C9D40400A708ED /* Screen.swift in Sources */,
 				383948DA25CD64D500E91849 /* Glucose.swift in Sources */,
 				383948DA25CD64D500E91849 /* Glucose.swift in Sources */,
 				CE94598029E9E3BD0047C9C6 /* WatchConfigDataFlow.swift in Sources */,
 				CE94598029E9E3BD0047C9C6 /* WatchConfigDataFlow.swift in Sources */,
+				BDCD47C32C26331400F8BCD5 /* AddProfileForm.swift in Sources */,
 				388E596C25AD95110019842D /* OpenAPS.swift in Sources */,
 				388E596C25AD95110019842D /* OpenAPS.swift in Sources */,
 				E00EEC0527368630002FF094 /* StorageAssembly.swift in Sources */,
 				E00EEC0527368630002FF094 /* StorageAssembly.swift in Sources */,
 				384E803825C388640086DB71 /* Script.swift in Sources */,
 				384E803825C388640086DB71 /* Script.swift in Sources */,
@@ -2920,7 +2919,6 @@
 				5825D13A2BD4058F00F36E9B /* LoopStatRecord+CoreDataClass.swift in Sources */,
 				5825D13A2BD4058F00F36E9B /* LoopStatRecord+CoreDataClass.swift in Sources */,
 				5887527C2BD986E1008B081D /* OpenAPSBattery.swift in Sources */,
 				5887527C2BD986E1008B081D /* OpenAPSBattery.swift in Sources */,
 				38569348270B5DFB0002C50D /* GlucoseSource.swift in Sources */,
 				38569348270B5DFB0002C50D /* GlucoseSource.swift in Sources */,
-				5825D15B2BD4058F00F36E9B /* OverridePresets+CoreDataProperties.swift in Sources */,
 				CEB434E328B8F9DB00B70274 /* BluetoothStateManager.swift in Sources */,
 				CEB434E328B8F9DB00B70274 /* BluetoothStateManager.swift in Sources */,
 				3811DE4225C9D4A100A708ED /* SettingsDataFlow.swift in Sources */,
 				3811DE4225C9D4A100A708ED /* SettingsDataFlow.swift in Sources */,
 				3811DE2525C9D48300A708ED /* MainRootView.swift in Sources */,
 				3811DE2525C9D48300A708ED /* MainRootView.swift in Sources */,
@@ -2951,6 +2949,7 @@
 				3811DE1825C9D40400A708ED /* Router.swift in Sources */,
 				3811DE1825C9D40400A708ED /* Router.swift in Sources */,
 				CE7950262998056D00FA576E /* CGMSetupView.swift in Sources */,
 				CE7950262998056D00FA576E /* CGMSetupView.swift in Sources */,
 				582FAE432C05102C00D1C13F /* CoreDataError.swift in Sources */,
 				582FAE432C05102C00D1C13F /* CoreDataError.swift in Sources */,
+				BDCD47BA2C2203A600F8BCD5 /* EditProfileForm.swift in Sources */,
 				38A0363B25ECF07E00FCBB52 /* GlucoseStorage.swift in Sources */,
 				38A0363B25ECF07E00FCBB52 /* GlucoseStorage.swift in Sources */,
 				190EBCC629FF138000BA767D /* StatConfigProvider.swift in Sources */,
 				190EBCC629FF138000BA767D /* StatConfigProvider.swift in Sources */,
 				38E98A2725F52C9300C0CED0 /* CollectionIssueReporter.swift in Sources */,
 				38E98A2725F52C9300C0CED0 /* CollectionIssueReporter.swift in Sources */,
@@ -2970,7 +2969,6 @@
 				384E803425C385E60086DB71 /* JavaScriptWorker.swift in Sources */,
 				384E803425C385E60086DB71 /* JavaScriptWorker.swift in Sources */,
 				3811DE5D25C9D4D500A708ED /* Publisher.swift in Sources */,
 				3811DE5D25C9D4D500A708ED /* Publisher.swift in Sources */,
 				E00EEC0727368630002FF094 /* APSAssembly.swift in Sources */,
 				E00EEC0727368630002FF094 /* APSAssembly.swift in Sources */,
-				5825D1482BD4058F00F36E9B /* Override+CoreDataClass.swift in Sources */,
 				38B4F3AF25E2979F00E76A18 /* IndexedCollection.swift in Sources */,
 				38B4F3AF25E2979F00E76A18 /* IndexedCollection.swift in Sources */,
 				3811DEAE25C9D88300A708ED /* Cache.swift in Sources */,
 				3811DEAE25C9D88300A708ED /* Cache.swift in Sources */,
 				383420D625FFE38C002D46C1 /* LoopView.swift in Sources */,
 				383420D625FFE38C002D46C1 /* LoopView.swift in Sources */,
@@ -3023,6 +3021,7 @@
 				581516A92BCEEDF800BF67D7 /* NSPredicates.swift in Sources */,
 				581516A92BCEEDF800BF67D7 /* NSPredicates.swift in Sources */,
 				38DAB28A260D349500F74C1A /* FetchGlucoseManager.swift in Sources */,
 				38DAB28A260D349500F74C1A /* FetchGlucoseManager.swift in Sources */,
 				38F37828261260DC009DB701 /* Color+Extensions.swift in Sources */,
 				38F37828261260DC009DB701 /* Color+Extensions.swift in Sources */,
+				BDCD47AF2C1F3F1700F8BCD5 /* OverrideStored+helper.swift in Sources */,
 				3811DE3F25C9D4A100A708ED /* SettingsStateModel.swift in Sources */,
 				3811DE3F25C9D4A100A708ED /* SettingsStateModel.swift in Sources */,
 				CE7CA3582A064E2F004BE681 /* ListStateView.swift in Sources */,
 				CE7CA3582A064E2F004BE681 /* ListStateView.swift in Sources */,
 				193F6CDD2A512C8F001240FD /* Loops.swift in Sources */,
 				193F6CDD2A512C8F001240FD /* Loops.swift in Sources */,
@@ -3172,6 +3171,8 @@
 				BDF34F852C10C62E00D51995 /* GlucoseData.swift in Sources */,
 				BDF34F852C10C62E00D51995 /* GlucoseData.swift in Sources */,
 				3862CC1F273FDC9200BF832C /* CalibrationsChart.swift in Sources */,
 				3862CC1F273FDC9200BF832C /* CalibrationsChart.swift in Sources */,
 				19E1F7EC29D082FE005C8D20 /* IconConfigStateModel.swift in Sources */,
 				19E1F7EC29D082FE005C8D20 /* IconConfigStateModel.swift in Sources */,
+				BDCD47DE2C2861F400F8BCD5 /* OverrideStored+CoreDataClass.swift in Sources */,
+				BDCD47DF2C2861F400F8BCD5 /* OverrideStored+CoreDataProperties.swift in Sources */,
 				711C0CB42CAABE788916BC9D /* ManualTempBasalDataFlow.swift in Sources */,
 				711C0CB42CAABE788916BC9D /* ManualTempBasalDataFlow.swift in Sources */,
 				588752842BD9986A008B081D /* OpenAPS_Battery+CoreDataClass.swift in Sources */,
 				588752842BD9986A008B081D /* OpenAPS_Battery+CoreDataClass.swift in Sources */,
 				BF1667ADE69E4B5B111CECAE /* ManualTempBasalProvider.swift in Sources */,
 				BF1667ADE69E4B5B111CECAE /* ManualTempBasalProvider.swift in Sources */,
@@ -3213,7 +3214,6 @@
 				E25073BC86C11C3D6A42F5AC /* CalibrationsStateModel.swift in Sources */,
 				E25073BC86C11C3D6A42F5AC /* CalibrationsStateModel.swift in Sources */,
 				BA90041DC8991147E5C8C3AA /* CalibrationsRootView.swift in Sources */,
 				BA90041DC8991147E5C8C3AA /* CalibrationsRootView.swift in Sources */,
 				E3A08AAE59538BC8A8ABE477 /* NotificationsConfigDataFlow.swift in Sources */,
 				E3A08AAE59538BC8A8ABE477 /* NotificationsConfigDataFlow.swift in Sources */,
-				1956FB212AFF79E200C7B4FF /* CoreDataStorage.swift in Sources */,
 				0F7A65FBD2CD8D6477ED4539 /* NotificationsConfigProvider.swift in Sources */,
 				0F7A65FBD2CD8D6477ED4539 /* NotificationsConfigProvider.swift in Sources */,
 				3171D2818C7C72CD1584BB5E /* NotificationsConfigStateModel.swift in Sources */,
 				3171D2818C7C72CD1584BB5E /* NotificationsConfigStateModel.swift in Sources */,
 				CD78BB94E43B249D60CC1A1B /* NotificationsConfigRootView.swift in Sources */,
 				CD78BB94E43B249D60CC1A1B /* NotificationsConfigRootView.swift in Sources */,

+ 6 - 4
FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift

@@ -296,11 +296,13 @@ final class OpenAPS {
             // requestIsEnbled.fetchLimit = 1
             // requestIsEnbled.fetchLimit = 1
             try? sliderArray = self.context.fetch(requestIsEnbled)
             try? sliderArray = self.context.fetch(requestIsEnbled)
 
 
-            var overrideArray = [Override]()
-            let requestOverrides = Override.fetchRequest() as NSFetchRequest<Override>
+            /// Get the last active Override as only this information is apparently used in oref2
+            var overrideArray = [OverrideStored]()
+            let requestOverrides = OverrideStored.fetchRequest() as NSFetchRequest<OverrideStored>
             let sortOverride = NSSortDescriptor(key: "date", ascending: false)
             let sortOverride = NSSortDescriptor(key: "date", ascending: false)
             requestOverrides.sortDescriptors = [sortOverride]
             requestOverrides.sortDescriptors = [sortOverride]
-            // requestOverrides.fetchLimit = 1
+            requestOverrides.predicate = NSPredicate.lastActiveOverride
+            requestOverrides.fetchLimit = 1
             try? overrideArray = self.context.fetch(requestOverrides)
             try? overrideArray = self.context.fetch(requestOverrides)
 
 
             var tempTargetsArray = [TempTargets]()
             var tempTargetsArray = [TempTargets]()
@@ -354,7 +356,7 @@ final class OpenAPS {
                    !unlimited
                    !unlimited
                 {
                 {
                     useOverride = false
                     useOverride = false
-                    let saveToCoreData = Override(context: self.context)
+                    let saveToCoreData = OverrideStored(context: self.context)
                     saveToCoreData.enabled = false
                     saveToCoreData.enabled = false
                     saveToCoreData.date = Date()
                     saveToCoreData.date = Date()
                     saveToCoreData.duration = 0
                     saveToCoreData.duration = 0

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

@@ -277,7 +277,7 @@ extension Bolus {
                 TextFieldWithToolBar(
                 TextFieldWithToolBar(
                     text: $state.carbs,
                     text: $state.carbs,
                     placeholder: "0",
                     placeholder: "0",
-                    shouldBecomeFirstResponder: true,
+                    shouldBecomeFirstResponder: false,
                     numberFormatter: mealFormatter
                     numberFormatter: mealFormatter
                 )
                 )
                 .onChange(of: state.carbs) { _ in
                 .onChange(of: state.carbs) { _ in
@@ -415,7 +415,7 @@ extension Bolus {
                                 TextFieldWithToolBar(
                                 TextFieldWithToolBar(
                                     text: $state.amount,
                                     text: $state.amount,
                                     placeholder: "0",
                                     placeholder: "0",
-                                    textColor: .blue,
+                                    textColor: colorScheme == .dark ? .white : .blue,
                                     maxLength: 5,
                                     maxLength: 5,
                                     numberFormatter: formatter
                                     numberFormatter: formatter
                                 )
                                 )

+ 10 - 12
FreeAPS/Sources/Modules/Home/HomeStateModel.swift

@@ -220,18 +220,16 @@ extension Home {
             }
             }
         }
         }
 
 
-        func cancelProfile() {
-            context.perform { [self] in
-                let profiles = Override(context: self.context)
-                profiles.enabled = false
-                profiles.date = Date()
-
-                do {
-                    guard self.context.hasChanges else { return }
-                    try self.context.save()
-                } catch {
-                    print(error.localizedDescription)
-                }
+        @MainActor func cancelProfile(withID id: NSManagedObjectID) async {
+            do {
+                let profileToCancel = try viewContext.existingObject(with: id) as? OverrideStored
+                profileToCancel?.enabled = false
+                profileToCancel?.date = Date()
+
+                guard viewContext.hasChanges else { return }
+                try viewContext.save()
+            } catch {
+                debugPrint("\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to cancel Profile")
             }
             }
         }
         }
 
 

+ 47 - 49
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -37,18 +37,11 @@ extension Home {
         @Environment(\.managedObjectContext) var moc
         @Environment(\.managedObjectContext) var moc
         @Environment(\.colorScheme) var colorScheme
         @Environment(\.colorScheme) var colorScheme
 
 
-        // TODO: rework this stuff -> remove fetch requests; no one wants this here.
-        @FetchRequest(
-            entity: Override.entity(),
-            sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)]
-        ) var fetchedPercent: FetchedResults<Override>
-
-        @FetchRequest(
-            entity: OverridePresets.entity(),
-            sortDescriptors: [NSSortDescriptor(key: "name", ascending: true)], predicate: NSPredicate(
-                format: "name != %@", "" as String
-            )
-        ) var fetchedProfiles: FetchedResults<OverridePresets>
+        @FetchRequest(fetchRequest: OverrideStored.fetch(
+            NSPredicate.lastActiveOverride,
+            ascending: false,
+            fetchLimit: 1
+        )) var latestOverride: FetchedResults<OverrideStored>
 
 
         @FetchRequest(
         @FetchRequest(
             entity: TempTargets.entity(),
             entity: TempTargets.entity(),
@@ -201,15 +194,15 @@ extension Home {
         }
         }
 
 
         var overrideString: String? {
         var overrideString: String? {
-            guard let fetched = fetchedPercent.first, fetched.enabled else {
+            guard let latestOverride = latestOverride.first else {
                 return nil
                 return nil
             }
             }
 
 
-            let percent = fetched.percentage
+            let percent = latestOverride.percentage
             let percentString = percent == 100 ? "" : "\(percent.formatted(.number)) %"
             let percentString = percent == 100 ? "" : "\(percent.formatted(.number)) %"
 
 
             let unit = state.units
             let unit = state.units
-            var target = (fetched.target ?? 100) as Decimal
+            var target = (latestOverride.target ?? 100) as Decimal
             target = unit == .mmolL ? target.asMmolL : target
             target = unit == .mmolL ? target.asMmolL : target
 
 
             var targetString = target == 0 ? "" : (fetchedTargetFormatter.string(from: target as NSNumber) ?? "") + " " + unit
             var targetString = target == 0 ? "" : (fetchedTargetFormatter.string(from: target as NSNumber) ?? "") + " " + unit
@@ -218,14 +211,14 @@ extension Home {
                 targetString = ""
                 targetString = ""
             }
             }
 
 
-            let duration = fetched.duration ?? 0
+            let duration = latestOverride.duration ?? 0
             let addedMinutes = Int(truncating: duration)
             let addedMinutes = Int(truncating: duration)
-            let date = fetched.date ?? Date()
+            let date = latestOverride.date ?? Date()
             let newDuration = max(
             let newDuration = max(
                 Decimal(Date().distance(to: date.addingTimeInterval(addedMinutes.minutes.timeInterval)).minutes),
                 Decimal(Date().distance(to: date.addingTimeInterval(addedMinutes.minutes.timeInterval)).minutes),
                 0
                 0
             )
             )
-            let indefinite = fetched.indefinite
+            let indefinite = latestOverride.indefinite
             var durationString = ""
             var durationString = ""
 
 
             if !indefinite {
             if !indefinite {
@@ -238,7 +231,7 @@ extension Home {
                 }
                 }
             }
             }
 
 
-            let smbToggleString = fetched.smbIsOff ? " \u{20e0}" : ""
+            let smbToggleString = latestOverride.smbIsOff ? " \u{20e0}" : ""
 
 
             let components = [percentString, targetString, durationString, smbToggleString].filter { !$0.isEmpty }
             let components = [percentString, targetString, durationString, smbToggleString].filter { !$0.isEmpty }
             return components.isEmpty ? nil : components.joined(separator: ", ")
             return components.isEmpty ? nil : components.joined(separator: ", ")
@@ -357,31 +350,31 @@ extension Home {
             .padding(.bottom)
             .padding(.bottom)
         }
         }
 
 
-        private func selectedProfile() -> (name: String, isOn: Bool) {
-            var profileString = ""
-            var display: Bool = false
-
-            let duration = (fetchedPercent.first?.duration ?? 0) as Decimal
-            let indefinite = fetchedPercent.first?.indefinite ?? false
-            let addedMinutes = Int(duration)
-            let date = fetchedPercent.first?.date ?? Date()
-            if date.addingTimeInterval(addedMinutes.minutes.timeInterval) > Date() || indefinite {
-                display.toggle()
-            }
-
-            if fetchedPercent.first?.enabled ?? false, !(fetchedPercent.first?.isPreset ?? false), display {
-                profileString = NSLocalizedString("Custom Profile", comment: "Custom but unsaved Profile")
-            } else if !(fetchedPercent.first?.enabled ?? false) || !display {
-                profileString = NSLocalizedString("Normal Profile", comment: "Your normal Profile. Use a short string")
-            } else {
-                let id_ = fetchedPercent.first?.id ?? ""
-                let profile = fetchedProfiles.filter({ $0.id == id_ }).first
-                if profile != nil {
-                    profileString = profile?.name?.description ?? ""
-                }
-            }
-            return (name: profileString, isOn: display)
-        }
+//        private func selectedProfile() -> (name: String, isOn: Bool) {
+//            var profileString = ""
+//            var display: Bool = false
+//
+//            let duration = (fetchedPercent.first?.duration ?? 0) as Decimal
+//            let indefinite = fetchedPercent.first?.indefinite ?? false
+//            let addedMinutes = Int(duration)
+//            let date = fetchedPercent.first?.date ?? Date()
+//            if date.addingTimeInterval(addedMinutes.minutes.timeInterval) > Date() || indefinite {
+//                display.toggle()
+//            }
+//
+//            if fetchedPercent.first?.enabled ?? false, !(fetchedPercent.first?.isPreset ?? false), display {
+//                profileString = NSLocalizedString("Custom Profile", comment: "Custom but unsaved Profile")
+//            } else if !(fetchedPercent.first?.enabled ?? false) || !display {
+//                profileString = NSLocalizedString("Normal Profile", comment: "Your normal Profile. Use a short string")
+//            } else {
+//                let id_ = fetchedPercent.first?.id ?? ""
+//                let profile = fetchedProfiles.filter({ $0.id == id_ }).first
+//                if profile != nil {
+//                    profileString = profile?.name?.description ?? ""
+//                }
+//            }
+//            return (name: profileString, isOn: display)
+//        }
 
 
         func highlightButtons() {
         func highlightButtons() {
             for i in 0 ..< timeButtons.count {
             for i in 0 ..< timeButtons.count {
@@ -553,12 +546,14 @@ extension Home {
 
 
                     if let overrideString = overrideString {
                     if let overrideString = overrideString {
                         VStack {
                         VStack {
-                            Text(selectedProfile().name)
+                            Text(latestOverride.first?.name ?? "Custom Override")
                                 .font(.subheadline)
                                 .font(.subheadline)
                                 .frame(maxWidth: .infinity, alignment: .leading)
                                 .frame(maxWidth: .infinity, alignment: .leading)
-                            Text(overrideString)
+
+                            Text("\(overrideString)")
                                 .font(.caption)
                                 .font(.caption)
                                 .frame(maxWidth: .infinity, alignment: .leading)
                                 .frame(maxWidth: .infinity, alignment: .leading)
+
                         }.padding(.leading, 5)
                         }.padding(.leading, 5)
                         Spacer()
                         Spacer()
                         Image(systemName: "xmark.app")
                         Image(systemName: "xmark.app")
@@ -566,7 +561,7 @@ extension Home {
                     } else {
                     } else {
                         if tempTargetString == nil {
                         if tempTargetString == nil {
                             VStack {
                             VStack {
-                                Text(selectedProfile().name)
+                                Text("Normal Profile")
                                     .font(.subheadline)
                                     .font(.subheadline)
                                     .frame(maxWidth: .infinity, alignment: .leading)
                                     .frame(maxWidth: .infinity, alignment: .leading)
                                 Text("100 %")
                                 Text("100 %")
@@ -586,13 +581,16 @@ extension Home {
                         actions: {
                         actions: {
                             Button("No", role: .cancel) {}
                             Button("No", role: .cancel) {}
                             Button("Yes", role: .destructive) {
                             Button("Yes", role: .destructive) {
-                                state.cancelProfile()
+                                Task {
+                                    guard let objectID = latestOverride.first?.objectID else { return }
+                                    await state.cancelProfile(withID: objectID)
+                                }
                             }
                             }
                         }, message: { Text("This will change settings back to your normal profile.") }
                         }, message: { Text("This will change settings back to your normal profile.") }
                     )
                     )
                     .padding(.trailing, 8)
                     .padding(.trailing, 8)
                     .onTapGesture {
                     .onTapGesture {
-                        if selectedProfile().name != "Normal Profile" {
+                        if !latestOverride.isEmpty {
                             showCancelAlert = true
                             showCancelAlert = true
                         }
                         }
                     }
                     }

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 647 - 365
FreeAPS/Sources/Modules/OverrideProfilesConfig/OverrideProfilesStateModel.swift


+ 270 - 0
FreeAPS/Sources/Modules/OverrideProfilesConfig/View/AddProfileForm.swift

@@ -0,0 +1,270 @@
+import Foundation
+import SwiftUI
+
+struct AddProfileForm: View {
+    @Environment(\.presentationMode) var presentationMode
+    @StateObject var state: OverrideProfilesConfig.StateModel
+    @State private var isEditing = false
+    @State private var overrideTarget = false
+    @Environment(\.colorScheme) var colorScheme
+    @State private var showAlert = false
+    @State private var alertString = ""
+
+    @Environment(\.dismiss) var dismiss
+
+    var color: LinearGradient {
+        colorScheme == .dark ? LinearGradient(
+            gradient: Gradient(colors: [
+                Color.bgDarkBlue,
+                Color.bgDarkerDarkBlue
+            ]),
+            startPoint: .top,
+            endPoint: .bottom
+        )
+            :
+            LinearGradient(
+                gradient: Gradient(colors: [Color.gray.opacity(0.1)]),
+                startPoint: .top,
+                endPoint: .bottom
+            )
+    }
+
+    private var formatter: NumberFormatter {
+        let formatter = NumberFormatter()
+        formatter.numberStyle = .decimal
+        formatter.maximumFractionDigits = 0
+        return formatter
+    }
+
+    private var glucoseFormatter: NumberFormatter {
+        let formatter = NumberFormatter()
+        formatter.numberStyle = .decimal
+        formatter.maximumFractionDigits = 0
+        if state.units == .mmolL {
+            formatter.maximumFractionDigits = 1
+        }
+        formatter.roundingMode = .halfUp
+        return formatter
+    }
+
+    var body: some View {
+        NavigationView {
+            Form {
+                addProfile()
+            }.scrollContentBackground(.hidden).background(color)
+                .navigationTitle("Add Profile")
+                .navigationBarItems(trailing: Button("Cancel") {
+                    presentationMode.wrappedValue.dismiss()
+                })
+        }
+    }
+
+    @ViewBuilder private func addProfile() -> some View {
+        Section {
+            VStack {
+                TextField("Name", text: $state.profileName)
+            }
+        } header: {
+            Text("Name")
+        }.listRowBackground(Color.chart)
+
+        Section {
+            VStack {
+                Spacer()
+                Text("\(state.percentageProfiles.formatted(.number)) %")
+                    .foregroundColor(
+                        state
+                            .percentageProfiles >= 130 ? .red :
+                            (isEditing ? .orange : Color.tabBar)
+                    )
+                    .font(.largeTitle)
+                Slider(
+                    value: $state.percentageProfiles,
+                    in: 10 ... 200,
+                    step: 1,
+                    onEditingChanged: { editing in
+                        isEditing = editing
+                    }
+                )
+                Spacer()
+                Toggle(isOn: $state._indefinite) {
+                    Text("Enable indefinitely")
+                }
+            }
+            if !state._indefinite {
+                HStack {
+                    Text("Duration")
+                    TextFieldWithToolBar(text: $state.durationProfile, placeholder: "0", numberFormatter: formatter)
+                    Text("minutes").foregroundColor(.secondary)
+                }
+            }
+
+            HStack {
+                Toggle(isOn: $state.override_target) {
+                    Text("Override Profile Target")
+                }
+            }
+            if state.override_target {
+                HStack {
+                    Text("Target Glucose")
+                    TextFieldWithToolBar(text: $state.target, placeholder: "0", numberFormatter: glucoseFormatter)
+                    Text(state.units.rawValue).foregroundColor(.secondary)
+                }
+            }
+            HStack {
+                Toggle(isOn: $state.advancedSettings) {
+                    Text("More options")
+                }
+            }
+            if state.advancedSettings {
+                HStack {
+                    Toggle(isOn: $state.smbIsOff) {
+                        Text("Disable SMBs")
+                    }
+                }
+                HStack {
+                    Toggle(isOn: $state.smbIsAlwaysOff) {
+                        Text("Schedule when SMBs are Off")
+                    }.disabled(!state.smbIsOff)
+                }
+                if state.smbIsAlwaysOff {
+                    HStack {
+                        Text("First Hour SMBs are Off (24 hours)")
+                        TextFieldWithToolBar(text: $state.start, placeholder: "0", numberFormatter: formatter)
+                        Text("hour").foregroundColor(.secondary)
+                    }
+                    HStack {
+                        Text("Last Hour SMBs are Off (24 hours)")
+                        TextFieldWithToolBar(text: $state.end, placeholder: "0", numberFormatter: formatter)
+                        Text("hour").foregroundColor(.secondary)
+                    }
+                }
+                HStack {
+                    Toggle(isOn: $state.isfAndCr) {
+                        Text("Change ISF and CR")
+                    }
+                }
+                if !state.isfAndCr {
+                    HStack {
+                        Toggle(isOn: $state.isf) {
+                            Text("Change ISF")
+                        }
+                    }
+                    HStack {
+                        Toggle(isOn: $state.cr) {
+                            Text("Change CR")
+                        }
+                    }
+                }
+                HStack {
+                    Text("SMB Minutes")
+                    TextFieldWithToolBar(text: $state.smbMinutes, placeholder: "0", numberFormatter: formatter)
+                    Text("minutes").foregroundColor(.secondary)
+                }
+                HStack {
+                    Text("UAM SMB Minutes")
+                    TextFieldWithToolBar(text: $state.uamMinutes, placeholder: "0", numberFormatter: formatter)
+                    Text("minutes").foregroundColor(.secondary)
+                }
+            }
+
+            startAndSaveProfiles
+        }
+        header: { Text("Add custom Override") }
+        footer: {
+            Text(
+                "Your profile basal insulin will be adjusted with the override percentage and your profile ISF and CR will be inversly adjusted with the percentage."
+            )
+        }.listRowBackground(Color.chart)
+    }
+
+    private var startAndSaveProfiles: some View {
+        HStack {
+            Button("Start new Profile") {
+                showAlert.toggle()
+
+                alertString = "\(state.percentageProfiles.formatted(.number)) %, " +
+                    (
+                        state.durationProfile > 0 || !state
+                            ._indefinite ?
+                            (
+                                state
+                                    .durationProfile
+                                    .formatted(.number.grouping(.never).rounded().precision(.fractionLength(0))) +
+                                    " min."
+                            ) :
+                            NSLocalizedString(" infinite duration.", comment: "")
+                    ) +
+                    (
+                        (state.target == 0 || !state.override_target) ? "" :
+                            (" Target: " + state.target.formatted() + " " + state.units.rawValue + ".")
+                    )
+                    +
+                    (
+                        state
+                            .smbIsOff ?
+                            NSLocalizedString(
+                                " SMBs are disabled either by schedule or during the entire duration.",
+                                comment: ""
+                            ) : ""
+                    )
+                    +
+                    "\n\n"
+                    +
+                    NSLocalizedString(
+                        "Starting this override will change your Profiles and/or your Target Glucose used for looping during the entire selected duration. Tapping ”Start Profile” will start your new profile or edit your current active profile.",
+                        comment: ""
+                    )
+            }
+            .disabled(unChanged())
+            .buttonStyle(BorderlessButtonStyle())
+            .font(.callout)
+            .controlSize(.mini)
+            .alert(
+                "Start Profile",
+                isPresented: $showAlert,
+                actions: {
+                    Button("Cancel", role: .cancel) { state.isEnabled = false }
+                    Button("Start Profile", role: .destructive) {
+                        Task {
+                            if state._indefinite { state.durationProfile = 0 }
+                            state.isEnabled.toggle()
+                            await state.saveAsProfile()
+                            await state.resetStateVariables()
+                            dismiss()
+                        }
+                    }
+                },
+                message: {
+                    Text(alertString)
+                }
+            )
+            Button {
+                Task {
+                    await state.savePreset()
+                    dismiss()
+                }
+            }
+            label: { Text("Save as Profile") }
+                .tint(.orange)
+                .frame(maxWidth: .infinity, alignment: .trailing)
+                .buttonStyle(BorderlessButtonStyle())
+                .controlSize(.mini)
+                .disabled(unChanged())
+        }
+    }
+
+    private func unChanged() -> Bool {
+        let isChanged = (
+            state.percentageProfiles == 100 && !state.override_target && !state.smbIsOff && !state
+                .advancedSettings
+        ) ||
+            (!state._indefinite && state.durationProfile == 0) || (state.override_target && state.target == 0) ||
+            (
+                state.percentageProfiles == 100 && !state.override_target && !state.smbIsOff && state.isf && state.cr && state
+                    .smbMinutes == state.defaultSmbMinutes && state.uamMinutes == state.defaultUamMinutes
+            )
+
+        return isChanged
+    }
+}

+ 341 - 0
FreeAPS/Sources/Modules/OverrideProfilesConfig/View/EditProfileForm.swift

@@ -0,0 +1,341 @@
+import Foundation
+import SwiftUI
+
+struct EditProfileForm: View {
+//    @Injected() var settingsManager: SettingsManager!
+    @ObservedObject var profile: OverrideStored
+    @Environment(\.presentationMode) var presentationMode
+    @Environment(\.colorScheme) var colorScheme
+    @StateObject var state: OverrideProfilesConfig.StateModel
+
+    @State private var name: String
+    @State private var percentage: Double
+    @State private var indefinite: Bool
+    @State private var duration: Decimal
+    @State private var target: Decimal?
+    @State private var advancedSettings: Bool
+    @State private var smbIsOff: Bool
+    @State private var smbIsAlwaysOff: Bool
+    @State private var start: Decimal?
+    @State private var end: Decimal?
+    @State private var isfAndCr: Bool
+    @State private var isf: Bool
+    @State private var cr: Bool
+    @State private var smbMinutes: Decimal?
+    @State private var uamMinutes: Decimal?
+
+    @State private var hasChanges = false
+    @State private var isEditing = false
+    @State private var target_override = false
+
+    init(profile: OverrideStored, state: OverrideProfilesConfig.StateModel) {
+        self.profile = profile
+        _state = StateObject(wrappedValue: state)
+        _name = State(initialValue: profile.name ?? "")
+        _percentage = State(initialValue: profile.percentage)
+        _indefinite = State(initialValue: profile.indefinite)
+        _duration = State(initialValue: profile.duration?.decimalValue ?? 0)
+        _target = State(initialValue: profile.target?.decimalValue)
+        _target_override = State(initialValue: profile.target?.decimalValue != 0)
+        _advancedSettings = State(initialValue: profile.advancedSettings)
+        _smbIsOff = State(initialValue: profile.smbIsOff)
+        _smbIsAlwaysOff = State(initialValue: profile.smbIsAlwaysOff)
+        _start = State(initialValue: profile.start?.decimalValue)
+        _end = State(initialValue: profile.end?.decimalValue)
+        _isfAndCr = State(initialValue: profile.isfAndCr)
+        _isf = State(initialValue: profile.isf)
+        _cr = State(initialValue: profile.cr)
+        _smbMinutes = State(initialValue: profile.smbMinutes?.decimalValue)
+        _uamMinutes = State(initialValue: profile.uamMinutes?.decimalValue)
+    }
+
+    var color: LinearGradient {
+        colorScheme == .dark ? LinearGradient(
+            gradient: Gradient(colors: [
+                Color.bgDarkBlue,
+                Color.bgDarkerDarkBlue
+            ]),
+            startPoint: .top,
+            endPoint: .bottom
+        ) :
+            LinearGradient(
+                gradient: Gradient(colors: [Color.gray.opacity(0.1)]),
+                startPoint: .top,
+                endPoint: .bottom
+            )
+    }
+
+    private var formatter: NumberFormatter {
+        let formatter = NumberFormatter()
+        formatter.numberStyle = .decimal
+        formatter.maximumFractionDigits = 0
+        return formatter
+    }
+
+    private var glucoseFormatter: NumberFormatter {
+        let formatter = NumberFormatter()
+        formatter.numberStyle = .decimal
+        formatter.maximumFractionDigits = 0
+        if state.units == .mmolL {
+            formatter.maximumFractionDigits = 1
+        }
+        formatter.roundingMode = .halfUp
+        return formatter
+    }
+
+    var body: some View {
+        NavigationView {
+            Form {
+                editProfile()
+
+                saveButton
+
+            }.scrollContentBackground(.hidden).background(color)
+                .navigationTitle("Edit Profile")
+                .navigationBarTitleDisplayMode(.inline)
+                .navigationBarItems(leading: Button("Close") {
+                    presentationMode.wrappedValue.dismiss()
+                })
+                .onDisappear {
+                    if !hasChanges {
+                        // Reset UI changes
+                        resetValues()
+                    }
+                }
+        }
+    }
+
+    @ViewBuilder private func editProfile() -> some View {
+        if profile.name != nil {
+            Section {
+                VStack {
+                    TextField("Name", text: $name)
+                        .onChange(of: name) { _ in hasChanges = true }
+                }
+            } header: {
+                Text("Name")
+            }.listRowBackground(Color.chart)
+        }
+        Section {
+            VStack {
+                Spacer()
+                Text("\(percentage.formatted(.number)) %")
+                    .foregroundColor(
+                        state
+                            .percentageProfiles >= 130 ? .red :
+                            (isEditing ? .orange : Color.tabBar)
+                    )
+                    .font(.largeTitle)
+                Slider(
+                    value: $percentage,
+                    in: 10 ... 200,
+                    step: 1
+                ).onChange(of: percentage) { _ in hasChanges = true }
+                Spacer()
+                Toggle(isOn: $indefinite) {
+                    Text("Enable indefinitely")
+                }.onChange(of: indefinite) { _ in hasChanges = true }
+            }
+            if !indefinite {
+                HStack {
+                    Text("Duration")
+                    TextFieldWithToolBar(
+                        text: Binding(
+                            get: { duration },
+                            set: {
+                                duration = $0
+                                hasChanges = true
+                            }
+                        ),
+                        placeholder: "0",
+                        numberFormatter: formatter
+                    )
+                    Text("minutes").foregroundColor(.secondary)
+                }
+            }
+
+            HStack {
+                Toggle(isOn: $target_override) {
+                    Text("Override Profile Target")
+                }.onChange(of: target_override) { _ in
+                    hasChanges = true
+                }
+            }
+            if target_override {
+                HStack {
+                    Text("Target Glucose")
+                    TextFieldWithToolBar(text: Binding(
+                        get: { target ?? 0 },
+                        set: {
+                            target = $0
+                            hasChanges = true
+                        }
+                    ), placeholder: "0", numberFormatter: glucoseFormatter)
+                    Text(state.units.rawValue).foregroundColor(.secondary)
+                }
+            }
+
+            Toggle(isOn: $advancedSettings) {
+                Text("More options")
+            }.onChange(of: advancedSettings) { _ in hasChanges = true }
+
+            if advancedSettings {
+                Toggle(isOn: $smbIsOff) {
+                    Text("Disable SMBs")
+                }.onChange(of: smbIsOff) { _ in hasChanges = true }
+
+                Toggle(isOn: $smbIsAlwaysOff) {
+                    Text("Schedule when SMBs are Off")
+                }.onChange(of: smbIsAlwaysOff) { _ in hasChanges = true }
+
+                if smbIsAlwaysOff {
+                    HStack {
+                        Text("First Hour SMBs are Off (24 hours)")
+                        TextFieldWithToolBar(
+                            text: Binding(
+                                get: { start ?? 0 },
+                                set: {
+                                    start = $0
+                                    hasChanges = true
+                                }
+                            ),
+                            placeholder: "0",
+                            numberFormatter: formatter
+                        )
+                        Text("hour").foregroundColor(.secondary)
+                    }
+
+                    HStack {
+                        Text("Last Hour SMBs are Off (24 hours)")
+                        TextFieldWithToolBar(
+                            text: Binding(
+                                get: { end ?? 23 },
+                                set: {
+                                    end = $0
+                                    hasChanges = true
+                                }
+                            ),
+                            placeholder: "0",
+                            numberFormatter: formatter
+                        )
+                        Text("hour").foregroundColor(.secondary)
+                    }
+                }
+
+                Toggle(isOn: $isfAndCr) {
+                    Text("Change ISF and CR")
+                }.onChange(of: isfAndCr) { _ in hasChanges = true }
+
+                if !isfAndCr {
+                    Toggle(isOn: $isf) {
+                        Text("Change ISF")
+                    }.onChange(of: isf) { _ in hasChanges = true }
+
+                    Toggle(isOn: $cr) {
+                        Text("Change CR")
+                    }.onChange(of: cr) { _ in hasChanges = true }
+                }
+
+                HStack {
+                    Text("SMB Minutes")
+                    TextFieldWithToolBar(
+                        text: Binding(
+                            get: { smbMinutes ?? state.defaultSmbMinutes },
+                            set: {
+                                smbMinutes = $0
+                                hasChanges = true
+                            }
+                        ),
+                        placeholder: "0",
+                        numberFormatter: formatter
+                    )
+                    Text("minutes").foregroundColor(.secondary)
+                }
+
+                HStack {
+                    Text("UAM SMB Minutes")
+                    TextFieldWithToolBar(
+                        text: Binding(
+                            get: { uamMinutes ?? state.defaultUamMinutes },
+                            set: {
+                                uamMinutes = $0
+                                hasChanges = true
+                            }
+                        ),
+                        placeholder: "0",
+                        numberFormatter: formatter
+                    )
+                    Text("minutes").foregroundColor(.secondary)
+                }
+            }
+        }.listRowBackground(Color.chart)
+    }
+
+    private var saveButton: some View {
+        HStack {
+            Spacer()
+            Button(action: {
+                saveChanges()
+                do {
+                    try profile.managedObjectContext?.save()
+                    hasChanges = false
+                    presentationMode.wrappedValue.dismiss()
+                } catch {
+                    debugPrint("\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to edit Profile")
+                }
+            }, label: {
+                Text("Save")
+            })
+                .disabled(!hasChanges)
+                .frame(maxWidth: .infinity, alignment: .center)
+                .tint(.white)
+
+            Spacer()
+        }.listRowBackground(hasChanges ? Color(.systemBlue) : Color(.systemGray4))
+    }
+
+    private func saveChanges() {
+        if !profile.isPreset, hasChanges, name == (profile.name ?? "") {
+            profile.name = "Custom Override"
+        } else {
+            profile.name = name
+        }
+        profile.percentage = percentage
+        profile.indefinite = indefinite
+        profile.duration = NSDecimalNumber(decimal: duration)
+        if target_override {
+            profile.target = target.map { NSDecimalNumber(decimal: $0) }
+        } else {
+            profile.target = 0
+        }
+        profile.advancedSettings = advancedSettings
+        profile.smbIsOff = smbIsOff
+        profile.smbIsAlwaysOff = smbIsAlwaysOff
+        profile.start = start.map { NSDecimalNumber(decimal: $0) }
+        profile.end = end.map { NSDecimalNumber(decimal: $0) }
+        profile.isfAndCr = isfAndCr
+        profile.isf = isf
+        profile.cr = cr
+        profile.smbMinutes = smbMinutes.map { NSDecimalNumber(decimal: $0) }
+        profile.uamMinutes = uamMinutes.map { NSDecimalNumber(decimal: $0) }
+        state.scheduleOverrideDisabling(for: profile)
+    }
+
+    private func resetValues() {
+        name = profile.name ?? ""
+        percentage = profile.percentage
+        indefinite = profile.indefinite
+        duration = profile.duration?.decimalValue ?? 0
+        target = profile.target?.decimalValue
+        advancedSettings = profile.advancedSettings
+        smbIsOff = profile.smbIsOff
+        smbIsAlwaysOff = profile.smbIsAlwaysOff
+        start = profile.start?.decimalValue
+        end = profile.end?.decimalValue
+        isfAndCr = profile.isfAndCr
+        isf = profile.isf
+        cr = profile.cr
+        smbMinutes = profile.smbMinutes?.decimalValue ?? state.defaultSmbMinutes
+        uamMinutes = profile.uamMinutes?.decimalValue ?? state.defaultUamMinutes
+    }
+}

+ 106 - 256
FreeAPS/Sources/Modules/OverrideProfilesConfig/View/OverrideProfilesRootView.swift

@@ -9,19 +9,18 @@ extension OverrideProfilesConfig {
         @StateObject var state = StateModel()
         @StateObject var state = StateModel()
 
 
         @State private var isEditing = false
         @State private var isEditing = false
-        @State private var showAlert = false
+        @State private var showProfileCreationSheet = false
         @State private var showingDetail = false
         @State private var showingDetail = false
-        @State private var alertSring = ""
-        @State var isSheetPresented: Bool = false
         @State private var showCheckmark: Bool = false
         @State private var showCheckmark: Bool = false
         @State private var selectedPresetID: String?
         @State private var selectedPresetID: String?
+        @State private var showOverrideEditSheet = false
+        @State private var selectedProfile: OverrideStored?
         // temp targets
         // temp targets
         @State private var isPromptPresented = false
         @State private var isPromptPresented = false
         @State private var isRemoveAlertPresented = false
         @State private var isRemoveAlertPresented = false
         @State private var removeAlert: Alert?
         @State private var removeAlert: Alert?
         @State private var isEditingTT = false
         @State private var isEditingTT = false
 
 
-        @Environment(\.dismiss) var dismiss
         @Environment(\.managedObjectContext) var moc
         @Environment(\.managedObjectContext) var moc
         @Environment(\.colorScheme) var colorScheme
         @Environment(\.colorScheme) var colorScheme
         var color: LinearGradient {
         var color: LinearGradient {
@@ -42,13 +41,6 @@ extension OverrideProfilesConfig {
         }
         }
 
 
         @FetchRequest(
         @FetchRequest(
-            entity: OverridePresets.entity(),
-            sortDescriptors: [NSSortDescriptor(key: "name", ascending: true)], predicate: NSPredicate(
-                format: "name != %@", "" as String
-            )
-        ) var fetchedProfiles: FetchedResults<OverridePresets>
-
-        @FetchRequest(
             entity: TempTargetsSlider.entity(),
             entity: TempTargetsSlider.entity(),
             sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)]
             sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)]
         ) var isEnabledArray: FetchedResults<TempTargetsSlider>
         ) var isEnabledArray: FetchedResults<TempTargetsSlider>
@@ -71,26 +63,6 @@ extension OverrideProfilesConfig {
             return formatter
             return formatter
         }
         }
 
 
-        var presetPopover: some View {
-            Form {
-                Section {
-                    TextField("Name Of Profile", text: $state.profileName)
-                } header: { Text("Enter Name of Profile") }
-
-                Section {
-                    Button("Save") {
-                        state.savePreset()
-                        isSheetPresented = false
-                    }
-                    .disabled(state.profileName.isEmpty || fetchedProfiles.filter({ $0.name == state.profileName }).isNotEmpty)
-
-                    Button("Cancel") {
-                        isSheetPresented = false
-                    }
-                }
-            }
-        }
-
         var body: some View {
         var body: some View {
             VStack {
             VStack {
                 Picker("Tab", selection: $state.selectedTab) {
                 Picker("Tab", selection: $state.selectedTab) {
@@ -106,214 +78,117 @@ extension OverrideProfilesConfig {
                     case .tempTargets: tempTargets() }
                     case .tempTargets: tempTargets() }
                 }.scrollContentBackground(.hidden).background(color)
                 }.scrollContentBackground(.hidden).background(color)
                     .onAppear(perform: configureView)
                     .onAppear(perform: configureView)
-                    .onAppear { state.savedSettings() }
                     .navigationBarTitle("Profiles")
                     .navigationBarTitle("Profiles")
                     .navigationBarTitleDisplayMode(.large)
                     .navigationBarTitleDisplayMode(.large)
-            }.background(color)
-        }
-
-        @ViewBuilder func profiles() -> some View {
-            if state.presetsProfiles.isNotEmpty {
-                Section {
-                    ForEach(fetchedProfiles) { preset in
-                        profilesView(for: preset)
-                    }.onDelete(perform: removeProfile)
-                }.listRowBackground(Color.chart)
-            }
-            Section {
-                VStack {
-                    Spacer()
-                    Text("\(state.percentageProfiles.formatted(.number)) %")
-                        .foregroundColor(
-                            state
-                                .percentageProfiles >= 130 ? .red :
-                                (isEditing ? .orange : Color.tabBar)
-                        )
-                        .font(.largeTitle)
-                    Slider(
-                        value: $state.percentageProfiles,
-                        in: 10 ... 200,
-                        step: 1,
-                        onEditingChanged: { editing in
-                            isEditing = editing
-                        }
-                    )
-                    Spacer()
-                    Toggle(isOn: $state._indefinite) {
-                        Text("Enable indefinitely")
-                    }
-                }
-                if !state._indefinite {
-                    HStack {
-                        Text("Duration")
-                        TextFieldWithToolBar(text: $state.durationProfile, placeholder: "0", numberFormatter: formatter)
-                        Text("minutes").foregroundColor(.secondary)
-                    }
-                }
-
-                HStack {
-                    Toggle(isOn: $state.override_target) {
-                        Text("Override Profile Target")
-                    }
-                }
-                if state.override_target {
-                    HStack {
-                        Text("Target Glucose")
-                        TextFieldWithToolBar(text: $state.target, placeholder: "0", numberFormatter: glucoseFormatter)
-                        Text(state.units.rawValue).foregroundColor(.secondary)
-                    }
-                }
-                HStack {
-                    Toggle(isOn: $state.advancedSettings) {
-                        Text("More options")
-                    }
-                }
-                if state.advancedSettings {
-                    HStack {
-                        Toggle(isOn: $state.smbIsOff) {
-                            Text("Disable SMBs")
+                    .toolbar {
+                        ToolbarItem(placement: .topBarTrailing) {
+                            switch state.selectedTab {
+                            case .profiles:
+                                Button(action: {
+                                    showProfileCreationSheet = true
+                                }, label: {
+                                    HStack {
+                                        Text("Add Profile")
+                                        Image(systemName: "plus")
+                                    }
+                                })
+                            default:
+                                EmptyView()
+                            }
                         }
                         }
                     }
                     }
-                    HStack {
-                        Toggle(isOn: $state.smbIsAlwaysOff) {
-                            Text("Schedule when SMBs are Off")
-                        }.disabled(!state.smbIsOff)
-                    }
-                    if state.smbIsAlwaysOff {
-                        HStack {
-                            Text("First Hour SMBs are Off (24 hours)")
-                            TextFieldWithToolBar(text: $state.start, placeholder: "0", numberFormatter: formatter)
-                            Text("hour").foregroundColor(.secondary)
+                    .sheet(isPresented: $showOverrideEditSheet, onDismiss: {
+                        Task {
+                            await state.resetStateVariables()
+                            showOverrideEditSheet = false
                         }
                         }
-                        HStack {
-                            Text("Last Hour SMBs are Off (24 hours)")
-                            TextFieldWithToolBar(text: $state.end, placeholder: "0", numberFormatter: formatter)
-                            Text("hour").foregroundColor(.secondary)
+
+                    }) {
+                        if let profile = selectedProfile {
+                            EditProfileForm(profile: profile, state: state)
                         }
                         }
                     }
                     }
-                    HStack {
-                        Toggle(isOn: $state.isfAndCr) {
-                            Text("Change ISF and CR")
+                    .sheet(isPresented: $showProfileCreationSheet, onDismiss: {
+                        Task {
+                            await state.resetStateVariables()
+                            showProfileCreationSheet = false
                         }
                         }
+                    }) {
+                        AddProfileForm(state: state)
                     }
                     }
-                    if !state.isfAndCr {
-                        HStack {
-                            Toggle(isOn: $state.isf) {
-                                Text("Change ISF")
-                            }
-                        }
-                        HStack {
-                            Toggle(isOn: $state.cr) {
-                                Text("Change CR")
-                            }
-                        }
-                    }
-                    HStack {
-                        Text("SMB Minutes")
-                        TextFieldWithToolBar(text: $state.smbMinutes, placeholder: "0", numberFormatter: formatter)
-                        Text("minutes").foregroundColor(.secondary)
-                    }
-                    HStack {
-                        Text("UAM SMB Minutes")
-                        TextFieldWithToolBar(text: $state.uamMinutes, placeholder: "0", numberFormatter: formatter)
-                        Text("minutes").foregroundColor(.secondary)
-                    }
+            }.background(color)
+        }
+
+        @ViewBuilder func profiles() -> some View {
+            if state.profilePresets.isNotEmpty {
+                overridePresets
+
+                if state.isEnabled, state.activeOverrideName.isNotEmpty {
+                    currentActiveOverride
                 }
                 }
+            } else {
+                defaultText
+            }
+            cancelProfileButton
+        }
 
 
-                // MARK: TESTING
+        private var defaultText: some View {
+            Text("Add Preset or Override by tapping the '+'")
+        }
 
 
-                HStack {
-                    Button("Start new Profile") {
-                        showAlert.toggle()
-
-                        alertSring = "\(state.percentageProfiles.formatted(.number)) %, " +
-                            (
-                                state.durationProfile > 0 || !state
-                                    ._indefinite ?
-                                    (
-                                        state
-                                            .durationProfile
-                                            .formatted(.number.grouping(.never).rounded().precision(.fractionLength(0))) +
-                                            " min."
-                                    ) :
-                                    NSLocalizedString(" infinite duration.", comment: "")
-                            ) +
-                            (
-                                (state.target == 0 || !state.override_target) ? "" :
-                                    (" Target: " + state.target.formatted() + " " + state.units.rawValue + ".")
-                            )
-                            +
-                            (
-                                state
-                                    .smbIsOff ?
-                                    NSLocalizedString(
-                                        " SMBs are disabled either by schedule or during the entire duration.",
-                                        comment: ""
-                                    ) : ""
-                            )
-                            +
-                            "\n\n"
-                            +
-                            NSLocalizedString(
-                                "Starting this override will change your Profiles and/or your Target Glucose used for looping during the entire selected duration. Tapping ”Start Profile” will start your new profile or edit your current active profile.",
-                                comment: ""
-                            )
-                    }
-                    .disabled(unChanged())
-                    .buttonStyle(BorderlessButtonStyle())
-                    .font(.callout)
-                    .controlSize(.mini)
-                    .alert(
-                        "Start Profile",
-                        isPresented: $showAlert,
-                        actions: {
-                            Button("Cancel", role: .cancel) { state.isEnabled = false }
-                            Button("Start Profile", role: .destructive) {
-                                if state._indefinite { state.durationProfile = 0 }
-                                state.isEnabled.toggle()
-                                state.saveSettings()
-                                dismiss()
+        private var overridePresets: some View {
+            Section {
+                ForEach(state.profilePresets) { preset in
+                    profilesView(for: preset)
+                        .swipeActions(edge: .trailing, allowsFullSwipe: true) {
+                            Button(role: .none) {
+                                state.invokeProfilePresetDeletion(preset.objectID)
+                            } label: {
+                                Label("Delete", systemImage: "trash")
+                                    .tint(.red)
                             }
                             }
-                        },
-                        message: {
-                            Text(alertSring)
+                            Button(action: {
+                                selectedProfile = preset
+                                showOverrideEditSheet = true
+                            }, label: {
+                                Label("Edit", systemImage: "pencil")
+                                    .tint(.blue)
+                            })
                         }
                         }
-                    )
-                    Button {
-                        isSheetPresented = true
-                    }
-                    label: { Text("Save as Profile") }
-                        .tint(.orange)
-                        .frame(maxWidth: .infinity, alignment: .trailing)
-                        .buttonStyle(BorderlessButtonStyle())
-                        .controlSize(.mini)
-                        .disabled(unChanged())
+                }.listRowBackground(Color.chart)
+            } header: {
+                Text("Presets")
+            } footer: {
+                Text("Swipe left to edit or delete a Profile Preset")
+            }
+        }
+
+        private var currentActiveOverride: some View {
+            Section {
+                HStack {
+                    Text("\(state.activeOverrideName) is running")
+
+                    Spacer()
+                    Image(systemName: "square.and.pencil")
+                        .foregroundStyle(Color.blue)
                 }
                 }
-                .sheet(isPresented: $isSheetPresented) {
-                    presetPopover
+                .contentShape(Rectangle())
+                .onTapGesture {
+                    selectedProfile = state.currentActiveOverride
+                    showOverrideEditSheet = true
                 }
                 }
-
-                // MARK: TESTING END
             }
             }
+            .listRowBackground(Color.blue.opacity(0.2))
+        }
 
 
-            header: { Text("Insulin") }
-            footer: {
-                Text(
-                    "Your profile basal insulin will be adjusted with the override percentage and your profile ISF and CR will be inversly adjusted with the percentage."
-                )
-            }.listRowBackground(Color.chart)
-
+        private var cancelProfileButton: some View {
             Button(action: {
             Button(action: {
-                state.cancelProfile()
-                dismiss()
-            }, label: {
-                HStack {
-                    Spacer()
-                    Text("Cancel Profile")
-                    Spacer()
-                    Image(systemName: "xmark.app")
-                        .font(.title)
+                Task {
+                    await state.disableAllActiveProfiles()
                 }
                 }
+            }, label: {
+                Text("Cancel Profile")
+
             })
             })
                 .frame(maxWidth: .infinity, alignment: .center)
                 .frame(maxWidth: .infinity, alignment: .center)
                 .disabled(!state.isEnabled)
                 .disabled(!state.isEnabled)
@@ -550,7 +425,7 @@ extension OverrideProfilesConfig {
             })
             })
         }
         }
 
 
-        @ViewBuilder private func profilesView(for preset: OverridePresets) -> some View {
+        @ViewBuilder private func profilesView(for preset: OverrideStored) -> some View {
             let target = state.units == .mmolL ? (((preset.target ?? 0) as NSDecimalNumber) as Decimal)
             let target = state.units == .mmolL ? (((preset.target ?? 0) as NSDecimalNumber) as Decimal)
                 .asMmolL : (preset.target ?? 0) as Decimal
                 .asMmolL : (preset.target ?? 0) as Decimal
             let duration = (preset.duration ?? 0) as Decimal
             let duration = (preset.duration ?? 0) as Decimal
@@ -599,14 +474,17 @@ extension OverrideProfilesConfig {
                         }
                         }
                         .contentShape(Rectangle())
                         .contentShape(Rectangle())
                         .onTapGesture {
                         .onTapGesture {
-                            state.selectProfile(id_: preset.id ?? "")
-                            state.hideModal()
-                            showCheckmark.toggle()
-                            selectedPresetID = preset.id
-
-                            // deactivate showCheckmark after 3 seconds
-                            DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
-                                showCheckmark = false
+                            Task {
+                                let objectID = preset.objectID
+                                await state.enactProfilePreset(withID: objectID)
+                                state.hideModal()
+                                showCheckmark.toggle()
+                                selectedPresetID = preset.id
+
+                                // deactivate showCheckmark after 3 seconds
+                                DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
+                                    showCheckmark = false
+                                }
                             }
                             }
                         }
                         }
                     }
                     }
@@ -620,33 +498,5 @@ extension OverrideProfilesConfig {
                 })
                 })
             }
             }
         }
         }
-
-        private func unChanged() -> Bool {
-            let isChanged = (
-                state.percentageProfiles == 100 && !state.override_target && !state.smbIsOff && !state
-                    .advancedSettings
-            ) ||
-                (!state._indefinite && state.durationProfile == 0) || (state.override_target && state.target == 0) ||
-                (
-                    state.percentageProfiles == 100 && !state.override_target && !state.smbIsOff && state.isf && state.cr && state
-                        .smbMinutes == state.defaultSmbMinutes && state.uamMinutes == state.defaultUamMinutes
-                )
-
-            return isChanged
-        }
-
-        private func removeProfile(at offsets: IndexSet) {
-            for index in offsets {
-                let language = fetchedProfiles[index]
-                moc.delete(language)
-            }
-            if moc.hasChanges {
-                do {
-                    try moc.save()
-                } catch {
-                    // To do: add error
-                }
-            }
-        }
     }
     }
 }
 }

+ 2 - 3
FreeAPS/Sources/Services/WatchManager/WatchManager.swift

@@ -18,7 +18,6 @@ final class BaseWatchManager: NSObject, WatchManager, Injectable {
     @Injected() private var tempTargetsStorage: TempTargetsStorage!
     @Injected() private var tempTargetsStorage: TempTargetsStorage!
     @Injected() private var garmin: GarminManager!
     @Injected() private var garmin: GarminManager!
 
 
-    let coreDataStorage = CoreDataStorage()
     let context = CoreDataStack.shared.newTaskContext()
     let context = CoreDataStack.shared.newTaskContext()
 
 
     private var lifetime = Lifetime()
     private var lifetime = Lifetime()
@@ -67,9 +66,9 @@ final class BaseWatchManager: NSObject, WatchManager, Injectable {
         )
         )
     }
     }
 
 
-    private func fetchLatestOverride() -> Override? {
+    private func fetchLatestOverride() -> OverrideStored? {
         CoreDataStack.shared.fetchEntities(
         CoreDataStack.shared.fetchEntities(
-            ofType: Override.self,
+            ofType: OverrideStored.self,
             onContext: context,
             onContext: context,
             predicate: NSPredicate.predicateForOneDayAgo,
             predicate: NSPredicate.predicateForOneDayAgo,
             key: "date",
             key: "date",

+ 0 - 4
FreeAPS/Sources/Views/DecimalTextField.swift

@@ -76,10 +76,6 @@ public struct TextFieldWithToolBar: UIViewRepresentable {
     }
     }
 
 
     public func updateUIView(_ textField: UITextField, context: Context) {
     public func updateUIView(_ textField: UITextField, context: Context) {
-        if textField.isFirstResponder {
-            return
-        }
-
         if text != 0 {
         if text != 0 {
             let newText = numberFormatter.string(from: text as NSNumber) ?? ""
             let newText = numberFormatter.string(from: text as NSNumber) ?? ""
             if textField.text != newText {
             if textField.text != newText {

+ 7 - 22
Model/Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22222" systemVersion="22G120" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithSwiftData="YES" userDefinedModelVersionIdentifier="">
+<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22758" systemVersion="23E214" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithSwiftData="YES" userDefinedModelVersionIdentifier="">
     <entity name="BolusStored" representedClassName="BolusStored" syncable="YES">
     <entity name="BolusStored" representedClassName="BolusStored" syncable="YES">
         <attribute name="amount" optional="YES" attributeType="Decimal" defaultValueString="0"/>
         <attribute name="amount" optional="YES" attributeType="Decimal" defaultValueString="0"/>
         <attribute name="isExternal" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
         <attribute name="isExternal" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
@@ -108,7 +108,7 @@
             <fetchIndexElement property="deliverAt" type="Binary" order="descending"/>
             <fetchIndexElement property="deliverAt" type="Binary" order="descending"/>
         </fetchIndex>
         </fetchIndex>
     </entity>
     </entity>
-    <entity name="Override" representedClassName="Override" syncable="YES">
+    <entity name="OverrideStored" representedClassName="OverrideStored" syncable="YES">
         <attribute name="advancedSettings" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
         <attribute name="advancedSettings" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
         <attribute name="cr" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
         <attribute name="cr" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
@@ -120,35 +120,20 @@
         <attribute name="isf" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
         <attribute name="isf" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
         <attribute name="isfAndCr" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
         <attribute name="isfAndCr" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
         <attribute name="isPreset" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
         <attribute name="isPreset" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
+        <attribute name="name" optional="YES" attributeType="String"/>
         <attribute name="percentage" optional="YES" attributeType="Double" defaultValueString="100" usesScalarValueType="YES"/>
         <attribute name="percentage" optional="YES" attributeType="Double" defaultValueString="100" usesScalarValueType="YES"/>
         <attribute name="smbIsAlwaysOff" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
         <attribute name="smbIsAlwaysOff" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
         <attribute name="smbIsOff" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
         <attribute name="smbIsOff" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
         <attribute name="smbMinutes" optional="YES" attributeType="Decimal" defaultValueString="30"/>
         <attribute name="smbMinutes" optional="YES" attributeType="Decimal" defaultValueString="30"/>
         <attribute name="start" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="start" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
-        <attribute name="target" optional="YES" attributeType="Decimal" defaultValueString="100"/>
+        <attribute name="target" optional="YES" attributeType="Decimal" defaultValueString="0"/>
         <attribute name="uamMinutes" optional="YES" attributeType="Decimal" defaultValueString="30"/>
         <attribute name="uamMinutes" optional="YES" attributeType="Decimal" defaultValueString="30"/>
         <fetchIndex name="byDate">
         <fetchIndex name="byDate">
             <fetchIndexElement property="date" type="Binary" order="descending"/>
             <fetchIndexElement property="date" type="Binary" order="descending"/>
         </fetchIndex>
         </fetchIndex>
-    </entity>
-    <entity name="OverridePresets" representedClassName="OverridePresets" syncable="YES">
-        <attribute name="advancedSettings" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
-        <attribute name="cr" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
-        <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
-        <attribute name="duration" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
-        <attribute name="end" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
-        <attribute name="id" optional="YES" attributeType="String"/>
-        <attribute name="indefinite" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
-        <attribute name="isf" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
-        <attribute name="isfAndCr" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
-        <attribute name="name" optional="YES" attributeType="String"/>
-        <attribute name="percentage" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
-        <attribute name="smbIsAlwaysOff" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
-        <attribute name="smbIsOff" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
-        <attribute name="smbMinutes" optional="YES" attributeType="Decimal" defaultValueString="30"/>
-        <attribute name="start" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
-        <attribute name="target" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
-        <attribute name="uamMinutes" optional="YES" attributeType="Decimal" defaultValueString="30"/>
+        <fetchIndex name="byIsPreset">
+            <fetchIndexElement property="isPreset" type="Binary" order="ascending"/>
+        </fetchIndex>
     </entity>
     </entity>
     <entity name="Presets" representedClassName="Presets" syncable="YES">
     <entity name="Presets" representedClassName="Presets" syncable="YES">
         <attribute name="carbs" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="carbs" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>

+ 0 - 20
Model/Helper/CoreDataStorage.swift

@@ -1,20 +0,0 @@
-import CoreData
-import Foundation
-import SwiftDate
-import Swinject
-
-final class CoreDataStorage {
-    let coredataContext = CoreDataStack.shared.newTaskContext()
-
-    func fetchLatestOverride() -> [Override] {
-        var overrideArray = [Override]()
-        coredataContext.performAndWait {
-            let requestOverrides = Override.fetchRequest() as NSFetchRequest<Override>
-            let sortOverride = NSSortDescriptor(key: "date", ascending: false)
-            requestOverrides.sortDescriptors = [sortOverride]
-            requestOverrides.fetchLimit = 1
-            try? overrideArray = self.coredataContext.fetch(requestOverrides)
-        }
-        return overrideArray
-    }
-}

+ 1 - 0
Model/Helper/CustomNotification.swift

@@ -5,4 +5,5 @@ extension Notification.Name {
     static let didPerformBatchUpdate = Notification.Name("didPerformBatchUpdate")
     static let didPerformBatchUpdate = Notification.Name("didPerformBatchUpdate")
     static let didPerformBatchDelete = Notification.Name("didPerformBatchDelete")
     static let didPerformBatchDelete = Notification.Name("didPerformBatchDelete")
     static let didUpdateDetermination = Notification.Name("didUpdateDetermination")
     static let didUpdateDetermination = Notification.Name("didUpdateDetermination")
+    static let didUpdateOverridePresets = Notification.Name("didUpdateOverridePresets")
 }
 }

+ 29 - 0
Model/Helper/OverrideStored+helper.swift

@@ -0,0 +1,29 @@
+import CoreData
+import Foundation
+
+extension NSPredicate {
+    static var allOverridePresets: NSPredicate {
+        NSPredicate(format: "isPreset == %@", true as NSNumber)
+    }
+
+    static var lastActiveOverride: NSPredicate {
+        let date = Date.oneDayAgo
+        return NSPredicate(
+            format: "date >= %@ AND enabled == %@",
+            date as NSDate,
+            true as NSNumber
+        )
+    }
+}
+
+extension OverrideStored {
+    static func fetch(_ predicate: NSPredicate, ascending: Bool, fetchLimit: Int? = nil) -> NSFetchRequest<OverrideStored> {
+        let request = OverrideStored.fetchRequest()
+        request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: ascending)]
+        request.predicate = predicate
+        if let fetchLimit = fetchLimit {
+            request.fetchLimit = fetchLimit
+        }
+        return request
+    }
+}

+ 0 - 4
Override+CoreDataClass.swift

@@ -1,4 +0,0 @@
-import CoreData
-import Foundation
-
-@objc(Override) public class Override: NSManagedObject {}

+ 0 - 4
OverridePresets+CoreDataClass.swift

@@ -1,4 +0,0 @@
-import CoreData
-import Foundation
-
-@objc(OverridePresets) public class OverridePresets: NSManagedObject {}

+ 0 - 28
OverridePresets+CoreDataProperties.swift

@@ -1,28 +0,0 @@
-import CoreData
-import Foundation
-
-public extension OverridePresets {
-    @nonobjc class func fetchRequest() -> NSFetchRequest<OverridePresets> {
-        NSFetchRequest<OverridePresets>(entityName: "OverridePresets")
-    }
-
-    @NSManaged var advancedSettings: Bool
-    @NSManaged var cr: Bool
-    @NSManaged var date: Date?
-    @NSManaged var duration: NSDecimalNumber?
-    @NSManaged var end: NSDecimalNumber?
-    @NSManaged var id: String?
-    @NSManaged var indefinite: Bool
-    @NSManaged var isf: Bool
-    @NSManaged var isfAndCr: Bool
-    @NSManaged var name: String?
-    @NSManaged var percentage: Double
-    @NSManaged var smbIsAlwaysOff: Bool
-    @NSManaged var smbIsOff: Bool
-    @NSManaged var smbMinutes: NSDecimalNumber?
-    @NSManaged var start: NSDecimalNumber?
-    @NSManaged var target: NSDecimalNumber?
-    @NSManaged var uamMinutes: NSDecimalNumber?
-}
-
-extension OverridePresets: Identifiable {}

+ 4 - 0
OverrideStored+CoreDataClass.swift

@@ -0,0 +1,4 @@
+import CoreData
+import Foundation
+
+@objc(OverrideStored) public class OverrideStored: NSManagedObject {}

+ 10 - 4
Override+CoreDataProperties.swift

@@ -1,9 +1,9 @@
 import CoreData
 import CoreData
 import Foundation
 import Foundation
 
 
-public extension Override {
-    @nonobjc class func fetchRequest() -> NSFetchRequest<Override> {
-        NSFetchRequest<Override>(entityName: "Override")
+public extension OverrideStored {
+    @nonobjc class func fetchRequest() -> NSFetchRequest<OverrideStored> {
+        NSFetchRequest<OverrideStored>(entityName: "OverrideStored")
     }
     }
 
 
     @NSManaged var advancedSettings: Bool
     @NSManaged var advancedSettings: Bool
@@ -17,6 +17,7 @@ public extension Override {
     @NSManaged var isf: Bool
     @NSManaged var isf: Bool
     @NSManaged var isfAndCr: Bool
     @NSManaged var isfAndCr: Bool
     @NSManaged var isPreset: Bool
     @NSManaged var isPreset: Bool
+    @NSManaged var name: String?
     @NSManaged var percentage: Double
     @NSManaged var percentage: Double
     @NSManaged var smbIsAlwaysOff: Bool
     @NSManaged var smbIsAlwaysOff: Bool
     @NSManaged var smbIsOff: Bool
     @NSManaged var smbIsOff: Bool
@@ -26,4 +27,9 @@ public extension Override {
     @NSManaged var uamMinutes: NSDecimalNumber?
     @NSManaged var uamMinutes: NSDecimalNumber?
 }
 }
 
 
-extension Override: Identifiable {}
+extension OverrideStored: Identifiable {
+    override public func awakeFromInsert() {
+        super.awakeFromInsert()
+        id = UUID().uuidString
+    }
+}