Browse Source

Remove UserDefaults.swift

Jonas Björkert 1 năm trước cách đây
mục cha
commit
52c4b2c132
47 tập tin đã thay đổi với 270 bổ sung241 xóa
  1. 0 4
      LoopFollow.xcodeproj/project.pbxproj
  2. 1 1
      LoopFollow/BackgroundRefresh/BT/BLEManager.swift
  3. 1 1
      LoopFollow/Contact/ContactImageUpdater.swift
  4. 5 5
      LoopFollow/Controllers/Graphs.swift
  5. 1 1
      LoopFollow/Controllers/Nightscout/BGData.swift
  6. 1 1
      LoopFollow/Controllers/Nightscout/CAge.swift
  7. 1 1
      LoopFollow/Controllers/Nightscout/DeviceStatusLoop.swift
  8. 1 1
      LoopFollow/Controllers/Nightscout/Profile.swift
  9. 2 2
      LoopFollow/Controllers/Nightscout/SAge.swift
  10. 1 1
      LoopFollow/Controllers/Nightscout/Treatments.swift
  11. 1 1
      LoopFollow/Controllers/Nightscout/Treatments/BGCheck.swift
  12. 1 1
      LoopFollow/Controllers/Nightscout/Treatments/Basals.swift
  13. 1 1
      LoopFollow/Controllers/Nightscout/Treatments/Bolus.swift
  14. 1 1
      LoopFollow/Controllers/Nightscout/Treatments/Carbs.swift
  15. 1 1
      LoopFollow/Controllers/Nightscout/Treatments/InsulinCartridgeChange.swift
  16. 1 1
      LoopFollow/Controllers/Nightscout/Treatments/Notes.swift
  17. 1 1
      LoopFollow/Controllers/Nightscout/Treatments/Overrides.swift
  18. 1 1
      LoopFollow/Controllers/Nightscout/Treatments/ResumePump.swift
  19. 1 1
      LoopFollow/Controllers/Nightscout/Treatments/SMB.swift
  20. 1 1
      LoopFollow/Controllers/Nightscout/Treatments/SensorStart.swift
  21. 1 1
      LoopFollow/Controllers/Nightscout/Treatments/SuspendPump.swift
  22. 1 1
      LoopFollow/Controllers/Nightscout/Treatments/TemporaryTarget.swift
  23. 1 1
      LoopFollow/Controllers/Stats.swift
  24. 10 10
      LoopFollow/Helpers/AppVersionManager.swift
  25. 14 4
      LoopFollow/Helpers/Localizer.swift
  26. 4 4
      LoopFollow/Helpers/NightscoutUtils.swift
  27. 1 1
      LoopFollow/Helpers/Views/BGPicker.swift
  28. 2 2
      LoopFollow/InfoDisplaySettings/InfoDisplaySettingsView.swift
  29. 4 4
      LoopFollow/InfoDisplaySettings/InfoDisplaySettingsViewModel.swift
  30. 2 2
      LoopFollow/InfoTable/InfoManager.swift
  31. 3 3
      LoopFollow/Nightscout/NightscoutSettingsViewModel.swift
  32. 1 1
      LoopFollow/Remote/Loop/LoopOverrideView.swift
  33. 7 7
      LoopFollow/Remote/Nightscout/TrioNightscoutRemoteView.swift
  34. 1 1
      LoopFollow/Remote/TRC/OverrideView.swift
  35. 5 5
      LoopFollow/Remote/TRC/TempTargetView.swift
  36. 14 14
      LoopFollow/Settings/AdvancedSettingsViewModel.swift
  37. 6 6
      LoopFollow/Settings/DexcomSettingsViewModel.swift
  38. 2 2
      LoopFollow/Settings/SettingsMenuView.swift
  39. 2 2
      LoopFollow/Snoozer/SnoozerView.swift
  40. 66 5
      LoopFollow/Storage/Storage+Migrate.swift
  41. 42 0
      LoopFollow/Storage/Storage.swift
  42. 0 116
      LoopFollow/Storage/UserDefaults.swift
  43. 2 2
      LoopFollow/Task/AlarmTask.swift
  44. 4 4
      LoopFollow/Task/BGTask.swift
  45. 1 1
      LoopFollow/Task/TreatmentsTask.swift
  46. 49 13
      LoopFollow/ViewControllers/MainViewController.swift
  47. 1 1
      LoopFollow/ViewControllers/NightScoutViewController.swift

+ 0 - 4
LoopFollow.xcodeproj/project.pbxproj

@@ -350,7 +350,6 @@
 		FCC0FAC224922A22003E610E /* DictionaryKeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC0FAC124922A22003E610E /* DictionaryKeyPath.swift */; };
 		FCC0FAC224922A22003E610E /* DictionaryKeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC0FAC124922A22003E610E /* DictionaryKeyPath.swift */; };
 		FCC6885C2489559400A0279D /* blank.wav in Resources */ = {isa = PBXBuildFile; fileRef = FCC6885B2489559400A0279D /* blank.wav */; };
 		FCC6885C2489559400A0279D /* blank.wav in Resources */ = {isa = PBXBuildFile; fileRef = FCC6885B2489559400A0279D /* blank.wav */; };
 		FCC6885E24896A6C00A0279D /* silence.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = FCC6885D24896A6C00A0279D /* silence.mp3 */; };
 		FCC6885E24896A6C00A0279D /* silence.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = FCC6885D24896A6C00A0279D /* silence.mp3 */; };
-		FCC6886524898EEE00A0279D /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC6886424898EEE00A0279D /* UserDefaults.swift */; };
 		FCC6886724898F8000A0279D /* UserDefaultsValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC6886624898F8000A0279D /* UserDefaultsValue.swift */; };
 		FCC6886724898F8000A0279D /* UserDefaultsValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC6886624898F8000A0279D /* UserDefaultsValue.swift */; };
 		FCC6886924898FB100A0279D /* UserDefaultsValueGroups.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC6886824898FB100A0279D /* UserDefaultsValueGroups.swift */; };
 		FCC6886924898FB100A0279D /* UserDefaultsValueGroups.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC6886824898FB100A0279D /* UserDefaultsValueGroups.swift */; };
 		FCC6886B24898FD800A0279D /* ObservationToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC6886A24898FD800A0279D /* ObservationToken.swift */; };
 		FCC6886B24898FD800A0279D /* ObservationToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC6886A24898FD800A0279D /* ObservationToken.swift */; };
@@ -724,7 +723,6 @@
 		FCC688592489554800A0279D /* BackgroundTaskAudio.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackgroundTaskAudio.swift; sourceTree = "<group>"; };
 		FCC688592489554800A0279D /* BackgroundTaskAudio.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackgroundTaskAudio.swift; sourceTree = "<group>"; };
 		FCC6885B2489559400A0279D /* blank.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = blank.wav; sourceTree = "<group>"; };
 		FCC6885B2489559400A0279D /* blank.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = blank.wav; sourceTree = "<group>"; };
 		FCC6885D24896A6C00A0279D /* silence.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = silence.mp3; sourceTree = "<group>"; };
 		FCC6885D24896A6C00A0279D /* silence.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = silence.mp3; sourceTree = "<group>"; };
-		FCC6886424898EEE00A0279D /* UserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaults.swift; sourceTree = "<group>"; };
 		FCC6886624898F8000A0279D /* UserDefaultsValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsValue.swift; sourceTree = "<group>"; };
 		FCC6886624898F8000A0279D /* UserDefaultsValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsValue.swift; sourceTree = "<group>"; };
 		FCC6886824898FB100A0279D /* UserDefaultsValueGroups.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsValueGroups.swift; sourceTree = "<group>"; };
 		FCC6886824898FB100A0279D /* UserDefaultsValueGroups.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsValueGroups.swift; sourceTree = "<group>"; };
 		FCC6886A24898FD800A0279D /* ObservationToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservationToken.swift; sourceTree = "<group>"; };
 		FCC6886A24898FD800A0279D /* ObservationToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservationToken.swift; sourceTree = "<group>"; };
@@ -1085,7 +1083,6 @@
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
 				FCC688512489363F00A0279D /* Framework */,
 				FCC688512489363F00A0279D /* Framework */,
-				FCC6886424898EEE00A0279D /* UserDefaults.swift */,
 				DDD10F002C510C6B00D76A8E /* ObservableUserDefaults.swift */,
 				DDD10F002C510C6B00D76A8E /* ObservableUserDefaults.swift */,
 				DDD10F062C529DE800D76A8E /* Observable.swift */,
 				DDD10F062C529DE800D76A8E /* Observable.swift */,
 				DD4878042C7B2C970048F05C /* Storage.swift */,
 				DD4878042C7B2C970048F05C /* Storage.swift */,
@@ -1910,7 +1907,6 @@
 				FCEF87AC24A141A700AE6FA0 /* Localizer.swift in Sources */,
 				FCEF87AC24A141A700AE6FA0 /* Localizer.swift in Sources */,
 				FC1BDD3224A2585C001B652C /* DataStructs.swift in Sources */,
 				FC1BDD3224A2585C001B652C /* DataStructs.swift in Sources */,
 				DDF6999E2C5AAA640058A8D9 /* ErrorMessageView.swift in Sources */,
 				DDF6999E2C5AAA640058A8D9 /* ErrorMessageView.swift in Sources */,
-				FCC6886524898EEE00A0279D /* UserDefaults.swift in Sources */,
 				DD4878152C7B75230048F05C /* MealView.swift in Sources */,
 				DD4878152C7B75230048F05C /* MealView.swift in Sources */,
 				FC16A97F249969E2003D6245 /* Graphs.swift in Sources */,
 				FC16A97F249969E2003D6245 /* Graphs.swift in Sources */,
 				FC8589BF252B54F500C8FC73 /* Mobileprovision.swift in Sources */,
 				FC8589BF252B54F500C8FC73 /* Mobileprovision.swift in Sources */,

+ 1 - 1
LoopFollow/BackgroundRefresh/BT/BLEManager.swift

@@ -248,7 +248,7 @@ extension BLEManager {
             return nil
             return nil
         }
         }
 
 
-        let pollingDelay: TimeInterval = Double(UserDefaultsRepository.bgUpdateDelay.value)
+        let pollingDelay: TimeInterval = Double(Storage.shared.bgUpdateDelay.value)
 
 
         let expectedOffset = sensorOffset + pollingDelay
         let expectedOffset = sensorOffset + pollingDelay
 
 

+ 1 - 1
LoopFollow/Contact/ContactImageUpdater.swift

@@ -85,7 +85,7 @@ class ContactImageUpdater {
         paragraphStyle.alignment = .center
         paragraphStyle.alignment = .center
 
 
         // Format extraDelta based on the user's unit preference
         // Format extraDelta based on the user's unit preference
-        let unitPreference = UserDefaultsRepository.units.value
+        let unitPreference = Storage.shared.units.value
         let yOffset: CGFloat = 48
         let yOffset: CGFloat = 48
         if contactType == .Trend, Storage.shared.contactTrend.value == .separate {
         if contactType == .Trend, Storage.shared.contactTrend.value == .separate {
             let trendRect = CGRect(x: 0, y: 46, width: size.width, height: size.height - 80)
             let trendRect = CGRect(x: 0, y: 46, width: size.width, height: size.height - 80)

+ 5 - 5
LoopFollow/Controllers/Graphs.swift

@@ -200,7 +200,7 @@ class TempTargetRenderer: LineChartRenderer {
     }
     }
 }
 }
 
 
-let ScaleXMax: Float = 150.0
+let ScaleXMax: Double = 150.0
 extension MainViewController {
 extension MainViewController {
     func updateChartRenderers() {
     func updateChartRenderers() {
         let tempTargetDataIndex = GraphDataIndex.tempTarget.rawValue
         let tempTargetDataIndex = GraphDataIndex.tempTarget.rawValue
@@ -230,11 +230,11 @@ extension MainViewController {
 
 
     func chartScaled(_: ChartViewBase, scaleX _: CGFloat, scaleY _: CGFloat) {
     func chartScaled(_: ChartViewBase, scaleX _: CGFloat, scaleY _: CGFloat) {
         // dont store huge values
         // dont store huge values
-        var scale = Float(BGChart.scaleX)
+        var scale = Double(BGChart.scaleX)
         if scale > ScaleXMax {
         if scale > ScaleXMax {
             scale = ScaleXMax
             scale = ScaleXMax
         }
         }
-        UserDefaultsRepository.chartScaleX.value = Float(scale)
+        Storage.shared.chartScaleX.value = scale
     }
     }
 
 
     func createGraph() {
     func createGraph() {
@@ -853,10 +853,10 @@ extension MainViewController {
         BGChartFull.notifyDataSetChanged()
         BGChartFull.notifyDataSetChanged()
 
 
         if firstGraphLoad {
         if firstGraphLoad {
-            var scaleX = CGFloat(UserDefaultsRepository.chartScaleX.value)
+            var scaleX = CGFloat(Storage.shared.chartScaleX.value)
             if scaleX > CGFloat(ScaleXMax) {
             if scaleX > CGFloat(ScaleXMax) {
                 scaleX = CGFloat(ScaleXMax)
                 scaleX = CGFloat(ScaleXMax)
-                UserDefaultsRepository.chartScaleX.value = ScaleXMax
+                Storage.shared.chartScaleX.value = ScaleXMax
             }
             }
             BGChart.zoom(scaleX: scaleX, scaleY: 1, x: 1, y: 1)
             BGChart.zoom(scaleX: scaleX, scaleY: 1, x: 1, y: 1)
             firstGraphLoad = false
             firstGraphLoad = false

+ 1 - 1
LoopFollow/Controllers/Nightscout/BGData.swift

@@ -167,7 +167,7 @@ extension MainViewController {
                                       message: "Reading is close to 5 minutes old (\(secondsAgo) sec). Scheduling next fetch in 5 seconds.",
                                       message: "Reading is close to 5 minutes old (\(secondsAgo) sec). Scheduling next fetch in 5 seconds.",
                                       isDebug: true)
                                       isDebug: true)
             } else {
             } else {
-                delayToSchedule = 300 - secondsAgo + Double(UserDefaultsRepository.bgUpdateDelay.value)
+                delayToSchedule = 300 - secondsAgo + Double(Storage.shared.bgUpdateDelay.value)
                 LogManager.shared.log(category: .nightscout,
                 LogManager.shared.log(category: .nightscout,
                                       message: "Fresh reading. Scheduling next fetch in \(delayToSchedule) seconds.",
                                       message: "Fresh reading. Scheduling next fetch in \(delayToSchedule) seconds.",
                                       isDebug: true)
                                       isDebug: true)

+ 1 - 1
LoopFollow/Controllers/Nightscout/CAge.swift

@@ -40,7 +40,7 @@ extension MainViewController {
                                    .withTime,
                                    .withTime,
                                    .withDashSeparatorInDate,
                                    .withDashSeparatorInDate,
                                    .withColonSeparatorInTime]
                                    .withColonSeparatorInTime]
-        UserDefaultsRepository.alertCageInsertTime.value = formatter.date(from: lastCageString)?.timeIntervalSince1970 as! TimeInterval
+        Storage.shared.cageInsertTime.value = formatter.date(from: lastCageString)?.timeIntervalSince1970 as! TimeInterval
         if let cageTime = formatter.date(from: lastCageString)?.timeIntervalSince1970 {
         if let cageTime = formatter.date(from: lastCageString)?.timeIntervalSince1970 {
             let now = dateTimeUtils.getNowTimeIntervalUTC()
             let now = dateTimeUtils.getNowTimeIntervalUTC()
             let secondsAgo = now - cageTime
             let secondsAgo = now - cageTime

+ 1 - 1
LoopFollow/Controllers/Nightscout/DeviceStatusLoop.swift

@@ -66,7 +66,7 @@ extension MainViewController {
                     let prediction = predictdata["values"] as! [Double]
                     let prediction = predictdata["values"] as! [Double]
                     PredictionLabel.text = Localizer.toDisplayUnits(String(Int(prediction.last!)))
                     PredictionLabel.text = Localizer.toDisplayUnits(String(Int(prediction.last!)))
                     PredictionLabel.textColor = UIColor.systemPurple
                     PredictionLabel.textColor = UIColor.systemPurple
-                    if UserDefaultsRepository.downloadPrediction.value, previousLastLoopTime < lastLoopTime {
+                    if Storage.shared.downloadPrediction.value, previousLastLoopTime < lastLoopTime {
                         predictionData.removeAll()
                         predictionData.removeAll()
                         var predictionTime = lastLoopTime
                         var predictionTime = lastLoopTime
                         let toLoad = Int(Storage.shared.predictionToLoad.value * 12)
                         let toLoad = Int(Storage.shared.predictionToLoad.value * 12)

+ 1 - 1
LoopFollow/Controllers/Nightscout/Profile.swift

@@ -110,7 +110,7 @@ extension MainViewController {
             }
             }
         }
         }
 
 
-        if UserDefaultsRepository.graphBasal.value {
+        if Storage.shared.graphBasal.value {
             updateBasalScheduledGraph()
             updateBasalScheduledGraph()
         }
         }
     }
     }

+ 2 - 2
LoopFollow/Controllers/Nightscout/SAge.swift

@@ -44,7 +44,7 @@ extension MainViewController {
                                    .withTime,
                                    .withTime,
                                    .withDashSeparatorInDate,
                                    .withDashSeparatorInDate,
                                    .withColonSeparatorInTime]
                                    .withColonSeparatorInTime]
-        UserDefaultsRepository.alertSageInsertTime.value = formatter.date(from: lastSageString)?.timeIntervalSince1970 as! TimeInterval
+        Storage.shared.sageInsertTime.value = formatter.date(from: lastSageString)?.timeIntervalSince1970 as! TimeInterval
 
 
         // -- Auto-snooze CGM start ────────────────────────────────────────────────
         // -- Auto-snooze CGM start ────────────────────────────────────────────────
         let now = Date()
         let now = Date()
@@ -52,7 +52,7 @@ extension MainViewController {
         // 1.  Do we *want* the automatic global snooze?
         // 1.  Do we *want* the automatic global snooze?
         if Storage.shared.alarmConfiguration.value.autoSnoozeCGMStart {
         if Storage.shared.alarmConfiguration.value.autoSnoozeCGMStart {
             // 2.  When did the sensor start?
             // 2.  When did the sensor start?
-            let insertTime = UserDefaultsRepository.alertSageInsertTime.value
+            let insertTime = Storage.shared.sageInsertTime.value
 
 
             // 3.  If the start is less than 2 h ago, snooze *all* alarms for the
             // 3.  If the start is less than 2 h ago, snooze *all* alarms for the
             //     remainder of that 2-hour window.
             //     remainder of that 2-hour window.

+ 1 - 1
LoopFollow/Controllers/Nightscout/Treatments.swift

@@ -8,7 +8,7 @@ extension MainViewController {
     // NS Treatments Web Call
     // NS Treatments Web Call
     // Downloads Basal, Bolus, Carbs, BG Check, Notes, Overrides
     // Downloads Basal, Bolus, Carbs, BG Check, Notes, Overrides
     func WebLoadNSTreatments() {
     func WebLoadNSTreatments() {
-        if !UserDefaultsRepository.downloadTreatments.value { return }
+        if !Storage.shared.downloadTreatments.value { return }
 
 
         let startTimeString = dateTimeUtils.getDateTimeString(addingDays: -1 * Storage.shared.downloadDays.value)
         let startTimeString = dateTimeUtils.getDateTimeString(addingDays: -1 * Storage.shared.downloadDays.value)
         let currentTimeString = dateTimeUtils.getDateTimeString(addingHours: 6)
         let currentTimeString = dateTimeUtils.getDateTimeString(addingHours: 6)

+ 1 - 1
LoopFollow/Controllers/Nightscout/Treatments/BGCheck.swift

@@ -29,7 +29,7 @@ extension MainViewController {
             }
             }
         }
         }
 
 
-        if UserDefaultsRepository.graphOtherTreatments.value {
+        if Storage.shared.graphOtherTreatments.value {
             updateBGCheckGraph()
             updateBGCheckGraph()
         }
         }
     }
     }

+ 1 - 1
LoopFollow/Controllers/Nightscout/Treatments/Basals.swift

@@ -138,7 +138,7 @@ extension MainViewController {
             basalData.append(endDot)
             basalData.append(endDot)
         }
         }
 
 
-        if UserDefaultsRepository.graphBasal.value {
+        if Storage.shared.graphBasal.value {
             updateBasalGraph()
             updateBasalGraph()
         }
         }
 
 

+ 1 - 1
LoopFollow/Controllers/Nightscout/Treatments/Bolus.swift

@@ -35,7 +35,7 @@ extension MainViewController {
             }
             }
         }
         }
 
 
-        if UserDefaultsRepository.graphBolus.value {
+        if Storage.shared.graphBolus.value {
             updateBolusGraph()
             updateBolusGraph()
         }
         }
     }
     }

+ 1 - 1
LoopFollow/Controllers/Nightscout/Treatments/Carbs.swift

@@ -46,7 +46,7 @@ extension MainViewController {
             }
             }
         }
         }
 
 
-        if UserDefaultsRepository.graphCarbs.value {
+        if Storage.shared.graphCarbs.value {
             updateCarbGraph()
             updateCarbGraph()
         }
         }
     }
     }

+ 1 - 1
LoopFollow/Controllers/Nightscout/Treatments/InsulinCartridgeChange.swift

@@ -10,7 +10,7 @@ extension MainViewController {
             updateIage(data: entries)
             updateIage(data: entries)
         } else if let iage = currentIage {
         } else if let iage = currentIage {
             updateIage(data: [iage])
             updateIage(data: [iage])
-        } else if UserDefaultsRepository.infoVisible.value[InfoType.iage.rawValue] {
+        } else if Storage.shared.infoVisible.value[InfoType.iage.rawValue] {
             webLoadNSIage()
             webLoadNSIage()
         }
         }
     }
     }

+ 1 - 1
LoopFollow/Controllers/Nightscout/Treatments/Notes.swift

@@ -40,7 +40,7 @@ extension MainViewController {
             }
             }
         }
         }
 
 
-        if UserDefaultsRepository.graphOtherTreatments.value {
+        if Storage.shared.graphOtherTreatments.value {
             updateNotes()
             updateNotes()
         }
         }
     }
     }

+ 1 - 1
LoopFollow/Controllers/Nightscout/Treatments/Overrides.swift

@@ -79,7 +79,7 @@ extension MainViewController {
                 infoManager.clearInfoData(type: .override)
                 infoManager.clearInfoData(type: .override)
             }
             }
         }
         }
-        if UserDefaultsRepository.graphOtherTreatments.value {
+        if Storage.shared.graphOtherTreatments.value {
             updateOverrideGraph()
             updateOverrideGraph()
         }
         }
     }
     }

+ 1 - 1
LoopFollow/Controllers/Nightscout/Treatments/ResumePump.swift

@@ -28,7 +28,7 @@ extension MainViewController {
             }
             }
         }
         }
 
 
-        if UserDefaultsRepository.graphOtherTreatments.value {
+        if Storage.shared.graphOtherTreatments.value {
             updateResumeGraph()
             updateResumeGraph()
         }
         }
     }
     }

+ 1 - 1
LoopFollow/Controllers/Nightscout/Treatments/SMB.swift

@@ -34,7 +34,7 @@ extension MainViewController {
             }
             }
         }
         }
 
 
-        if UserDefaultsRepository.graphBolus.value {
+        if Storage.shared.graphBolus.value {
             updateSmbGraph()
             updateSmbGraph()
             updateChartRenderers()
             updateChartRenderers()
         }
         }

+ 1 - 1
LoopFollow/Controllers/Nightscout/Treatments/SensorStart.swift

@@ -35,7 +35,7 @@ extension MainViewController {
                 print("Failed to parse date")
                 print("Failed to parse date")
             }
             }
         }
         }
-        if UserDefaultsRepository.graphOtherTreatments.value {
+        if Storage.shared.graphOtherTreatments.value {
             updateSensorStart()
             updateSensorStart()
         }
         }
     }
     }

+ 1 - 1
LoopFollow/Controllers/Nightscout/Treatments/SuspendPump.swift

@@ -28,7 +28,7 @@ extension MainViewController {
             }
             }
         }
         }
 
 
-        if UserDefaultsRepository.graphOtherTreatments.value {
+        if Storage.shared.graphOtherTreatments.value {
             updateSuspendGraph()
             updateSuspendGraph()
         }
         }
     }
     }

+ 1 - 1
LoopFollow/Controllers/Nightscout/Treatments/TemporaryTarget.swift

@@ -63,7 +63,7 @@ extension MainViewController {
             }
             }
         }
         }
 
 
-        if UserDefaultsRepository.graphOtherTreatments.value {
+        if Storage.shared.graphOtherTreatments.value {
             updateTempTargetGraph()
             updateTempTargetGraph()
             updateChartRenderers()
             updateChartRenderers()
         }
         }

+ 1 - 1
LoopFollow/Controllers/Stats.swift

@@ -62,7 +62,7 @@ class StatsData {
         }
         }
 
 
         stdDev = sqrt(partialSum / Float(bgData.count))
         stdDev = sqrt(partialSum / Float(bgData.count))
-        if UserDefaultsRepository.units.value != "mg/dL" {
+        if Storage.shared.units.value != "mg/dL" {
             stdDev = stdDev * Float(GlucoseConversion.mgDlToMmolL)
             stdDev = stdDev * Float(GlucoseConversion.mgDlToMmolL)
         }
         }
 
 

+ 10 - 10
LoopFollow/Helpers/AppVersionManager.swift

@@ -23,15 +23,15 @@ class AppVersionManager {
         let now = Date()
         let now = Date()
 
 
         // Retrieve cache
         // Retrieve cache
-        let latestVersionChecked = UserDefaultsRepository.latestVersionChecked.value ?? Date.distantPast
-        let latestVersion = UserDefaultsRepository.latestVersion.value
-        let currentVersionBlackListed = UserDefaultsRepository.currentVersionBlackListed.value
-        let cachedForVersion = UserDefaultsRepository.cachedForVersion.value
+        let latestVersionChecked = Storage.shared.latestVersionChecked.value ?? Date.distantPast
+        let latestVersion = Storage.shared.latestVersion.value
+        let currentVersionBlackListed = Storage.shared.currentVersionBlackListed.value
+        let cachedForVersion = Storage.shared.cachedForVersion.value
 
 
         // Reset notifications if version has changed
         // Reset notifications if version has changed
         if let cachedVersion = cachedForVersion, cachedVersion != currentVersion {
         if let cachedVersion = cachedForVersion, cachedVersion != currentVersion {
-            UserDefaultsRepository.lastBlacklistNotificationShown.value = Date.distantPast
-            UserDefaultsRepository.lastVersionUpdateNotificationShown.value = Date.distantPast
+            Storage.shared.lastBlacklistNotificationShown.value = Date.distantPast
+            Storage.shared.lastVersionUpdateNotificationShown.value = Date.distantPast
         }
         }
 
 
         // Check if the cache is still valid
         // Check if the cache is still valid
@@ -59,10 +59,10 @@ class AppVersionManager {
                         .map { $0.blacklistedVersions.map { $0.version }.contains(currentVersion) } ?? false
                         .map { $0.blacklistedVersions.map { $0.version }.contains(currentVersion) } ?? false
 
 
                     // Update cache with new data
                     // Update cache with new data
-                    UserDefaultsRepository.latestVersion.value = fetchedVersion
-                    UserDefaultsRepository.latestVersionChecked.value = Date()
-                    UserDefaultsRepository.currentVersionBlackListed.value = isBlacklisted
-                    UserDefaultsRepository.cachedForVersion.value = currentVersion
+                    Storage.shared.latestVersion.value = fetchedVersion
+                    Storage.shared.latestVersionChecked.value = Date()
+                    Storage.shared.currentVersionBlackListed.value = isBlacklisted
+                    Storage.shared.cachedForVersion.value = currentVersion
 
 
                     // Call completion with new data
                     // Call completion with new data
                     completion(fetchedVersion, isNewer, isBlacklisted)
                     completion(fetchedVersion, isNewer, isBlacklisted)

+ 14 - 4
LoopFollow/Helpers/Localizer.swift

@@ -6,6 +6,16 @@ import Foundation
 import HealthKit
 import HealthKit
 
 
 class Localizer {
 class Localizer {
+    static func getPreferredUnit() -> HKUnit {
+        let unitString = Storage.shared.units.value
+        switch unitString {
+        case "mmol/L":
+            return .millimolesPerLiter
+        default:
+            return .milligramsPerDeciliter
+        }
+    }
+
     static func formatToLocalizedString(_ value: Double, maxFractionDigits: Int = 1, minFractionDigits: Int = 0) -> String {
     static func formatToLocalizedString(_ value: Double, maxFractionDigits: Int = 1, minFractionDigits: Int = 0) -> String {
         let numberFormatter = NumberFormatter()
         let numberFormatter = NumberFormatter()
         numberFormatter.numberStyle = .decimal
         numberFormatter.numberStyle = .decimal
@@ -18,7 +28,7 @@ class Localizer {
     }
     }
 
 
     static func formatQuantity(_ quantity: HKQuantity) -> String {
     static func formatQuantity(_ quantity: HKQuantity) -> String {
-        let unit: HKUnit = UserDefaultsRepository.getPreferredUnit()
+        let unit: HKUnit = getPreferredUnit()
         let value = quantity.doubleValue(for: unit)
         let value = quantity.doubleValue(for: unit)
 
 
         return formatToLocalizedString(value, maxFractionDigits: unit.preferredFractionDigits, minFractionDigits: unit.preferredFractionDigits)
         return formatToLocalizedString(value, maxFractionDigits: unit.preferredFractionDigits, minFractionDigits: unit.preferredFractionDigits)
@@ -41,7 +51,7 @@ class Localizer {
         let numberFormatter = NumberFormatter()
         let numberFormatter = NumberFormatter()
         numberFormatter.numberStyle = .decimal
         numberFormatter.numberStyle = .decimal
 
 
-        let units = unit ?? UserDefaultsRepository.units.value
+        let units = unit ?? Storage.shared.units.value
 
 
         if units == "mg/dL" {
         if units == "mg/dL" {
             numberFormatter.maximumFractionDigits = 0 // No decimal places for mg/dL
             numberFormatter.maximumFractionDigits = 0 // No decimal places for mg/dL
@@ -60,7 +70,7 @@ class Localizer {
         let numberFormatter = NumberFormatter()
         let numberFormatter = NumberFormatter()
         numberFormatter.numberStyle = .decimal
         numberFormatter.numberStyle = .decimal
 
 
-        if UserDefaultsRepository.units.value == "mg/dL" {
+        if Storage.shared.units.value == "mg/dL" {
             numberFormatter.maximumFractionDigits = 0 // No decimal places for mg/dL
             numberFormatter.maximumFractionDigits = 0 // No decimal places for mg/dL
         } else {
         } else {
             numberFormatter.maximumFractionDigits = 1 // Always one decimal place for mmol/L
             numberFormatter.maximumFractionDigits = 1 // Always one decimal place for mmol/L
@@ -70,7 +80,7 @@ class Localizer {
         numberFormatter.locale = Locale.current
         numberFormatter.locale = Locale.current
 
 
         if let number = Float(value) {
         if let number = Float(value) {
-            if UserDefaultsRepository.units.value == "mg/dL" {
+            if Storage.shared.units.value == "mg/dL" {
                 let numberValue = NSNumber(value: number)
                 let numberValue = NSNumber(value: number)
                 return numberFormatter.string(from: numberValue) ?? value
                 return numberFormatter.string(from: numberValue) ?? value
             } else {
             } else {

+ 4 - 4
LoopFollow/Helpers/NightscoutUtils.swift

@@ -64,7 +64,7 @@ class NightscoutUtils {
 
 
     static func executeRequest<T: Decodable>(eventType: EventType, parameters: [String: String], completion: @escaping (Result<T, Error>) -> Void) {
     static func executeRequest<T: Decodable>(eventType: EventType, parameters: [String: String], completion: @escaping (Result<T, Error>) -> Void) {
         let baseURL = ObservableUserDefaults.shared.url.value
         let baseURL = ObservableUserDefaults.shared.url.value
-        let token = UserDefaultsRepository.token.value
+        let token = Storage.shared.token.value
 
 
         guard let url = NightscoutUtils.constructURL(baseURL: baseURL, token: token, endpoint: eventType.endpoint, parameters: parameters) else {
         guard let url = NightscoutUtils.constructURL(baseURL: baseURL, token: token, endpoint: eventType.endpoint, parameters: parameters) else {
             completion(.failure(NSError(domain: "NightscoutUtils", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to construct URL"])))
             completion(.failure(NSError(domain: "NightscoutUtils", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to construct URL"])))
@@ -114,7 +114,7 @@ class NightscoutUtils {
 
 
     static func executeDynamicRequest(eventType: EventType, parameters: [String: String], completion: @escaping (Result<Any, Error>) -> Void) {
     static func executeDynamicRequest(eventType: EventType, parameters: [String: String], completion: @escaping (Result<Any, Error>) -> Void) {
         let baseURL = ObservableUserDefaults.shared.url.value
         let baseURL = ObservableUserDefaults.shared.url.value
-        let token = UserDefaultsRepository.token.value
+        let token = Storage.shared.token.value
 
 
         guard let url = NightscoutUtils.constructURL(baseURL: baseURL, token: token, endpoint: eventType.endpoint, parameters: parameters) else {
         guard let url = NightscoutUtils.constructURL(baseURL: baseURL, token: token, endpoint: eventType.endpoint, parameters: parameters) else {
             completion(.failure(NSError(domain: "NightscoutUtils", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to construct URL"])))
             completion(.failure(NSError(domain: "NightscoutUtils", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to construct URL"])))
@@ -187,7 +187,7 @@ class NightscoutUtils {
 
 
     static func verifyURLAndToken(completion: @escaping (NightscoutError?, String?, Bool, Bool) -> Void) {
     static func verifyURLAndToken(completion: @escaping (NightscoutError?, String?, Bool, Bool) -> Void) {
         let urlUser = ObservableUserDefaults.shared.url.value
         let urlUser = ObservableUserDefaults.shared.url.value
-        let token = UserDefaultsRepository.token.value
+        let token = Storage.shared.token.value
 
 
         if urlUser.isEmpty {
         if urlUser.isEmpty {
             completion(.emptyAddress, nil, false, false)
             completion(.emptyAddress, nil, false, false)
@@ -285,7 +285,7 @@ class NightscoutUtils {
 
 
     static func retrieveJWTToken() async throws -> String {
     static func retrieveJWTToken() async throws -> String {
         let urlUser = ObservableUserDefaults.shared.url.value
         let urlUser = ObservableUserDefaults.shared.url.value
-        let token = UserDefaultsRepository.token.value
+        let token = Storage.shared.token.value
 
 
         if urlUser.isEmpty {
         if urlUser.isEmpty {
             throw NightscoutError.emptyAddress
             throw NightscoutError.emptyAddress

+ 1 - 1
LoopFollow/Helpers/Views/BGPicker.swift

@@ -13,7 +13,7 @@ struct BGPicker: View {
 
 
     // MARK: – Helpers
     // MARK: – Helpers
 
 
-    private var unit: HKUnit { UserDefaultsRepository.getPreferredUnit() }
+    private var unit: HKUnit { Localizer.getPreferredUnit() }
 
 
     private var allValues: [Double] {
     private var allValues: [Double] {
         if unit == .millimolesPerLiter {
         if unit == .millimolesPerLiter {

+ 2 - 2
LoopFollow/InfoDisplaySettings/InfoDisplaySettingsView.swift

@@ -12,8 +12,8 @@ struct InfoDisplaySettingsView: View {
             Form {
             Form {
                 Section(header: Text("General")) {
                 Section(header: Text("General")) {
                     Toggle(isOn: Binding(
                     Toggle(isOn: Binding(
-                        get: { UserDefaultsRepository.hideInfoTable.value },
-                        set: { UserDefaultsRepository.hideInfoTable.value = $0 }
+                        get: { Storage.shared.hideInfoTable.value },
+                        set: { Storage.shared.hideInfoTable.value = $0 }
                     )) {
                     )) {
                         Text("Hide Information Table")
                         Text("Hide Information Table")
                     }
                     }

+ 4 - 4
LoopFollow/InfoDisplaySettings/InfoDisplaySettingsViewModel.swift

@@ -10,18 +10,18 @@ class InfoDisplaySettingsViewModel: ObservableObject {
     @Published var infoVisible: [Bool]
     @Published var infoVisible: [Bool]
 
 
     init() {
     init() {
-        infoSort = UserDefaultsRepository.infoSort.value
-        infoVisible = UserDefaultsRepository.infoVisible.value
+        infoSort = Storage.shared.infoSort.value
+        infoVisible = Storage.shared.infoVisible.value
     }
     }
 
 
     func toggleVisibility(for sortedIndex: Int) {
     func toggleVisibility(for sortedIndex: Int) {
         infoVisible[sortedIndex].toggle()
         infoVisible[sortedIndex].toggle()
-        UserDefaultsRepository.infoVisible.value = infoVisible
+        Storage.shared.infoVisible.value = infoVisible
     }
     }
 
 
     func move(from source: IndexSet, to destination: Int) {
     func move(from source: IndexSet, to destination: Int) {
         infoSort.move(fromOffsets: source, toOffset: destination)
         infoSort.move(fromOffsets: source, toOffset: destination)
-        UserDefaultsRepository.infoSort.value = infoSort
+        Storage.shared.infoSort.value = infoSort
     }
     }
 
 
     func getName(for index: Int) -> String {
     func getName(for index: Int) -> String {

+ 2 - 2
LoopFollow/InfoTable/InfoManager.swift

@@ -67,11 +67,11 @@ class InfoManager {
     }
     }
 
 
     func numberOfRows() -> Int {
     func numberOfRows() -> Int {
-        return UserDefaultsRepository.infoSort.value.filter { UserDefaultsRepository.infoVisible.value[$0] }.count
+        return Storage.shared.infoSort.value.filter { Storage.shared.infoVisible.value[$0] }.count
     }
     }
 
 
     func dataForIndexPath(_ indexPath: IndexPath) -> InfoData? {
     func dataForIndexPath(_ indexPath: IndexPath) -> InfoData? {
-        let sortedAndVisibleIndexes = UserDefaultsRepository.infoSort.value.filter { UserDefaultsRepository.infoVisible.value[$0] }
+        let sortedAndVisibleIndexes = Storage.shared.infoSort.value.filter { Storage.shared.infoVisible.value[$0] }
 
 
         guard indexPath.row < sortedAndVisibleIndexes.count else {
         guard indexPath.row < sortedAndVisibleIndexes.count else {
             return nil
             return nil

+ 3 - 3
LoopFollow/Nightscout/NightscoutSettingsViewModel.swift

@@ -24,10 +24,10 @@ class NightscoutSettingsViewModel: ObservableObject {
         }
         }
     }
     }
 
 
-    @Published var nightscoutToken: String = UserDefaultsRepository.token.value {
+    @Published var nightscoutToken: String = Storage.shared.token.value {
         willSet {
         willSet {
             if newValue != nightscoutToken {
             if newValue != nightscoutToken {
-                UserDefaultsRepository.token.value = newValue
+                Storage.shared.token.value = newValue
                 triggerCheckStatus()
                 triggerCheckStatus()
             }
             }
         }
         }
@@ -41,7 +41,7 @@ class NightscoutSettingsViewModel: ObservableObject {
 
 
     init() {
     init() {
         initialURL = ObservableUserDefaults.shared.url.value
         initialURL = ObservableUserDefaults.shared.url.value
-        initialToken = UserDefaultsRepository.token.value
+        initialToken = Storage.shared.token.value
 
 
         setupDebounce()
         setupDebounce()
         checkNightscoutStatus()
         checkNightscoutStatus()

+ 1 - 1
LoopFollow/Remote/Loop/LoopOverrideView.swift

@@ -99,7 +99,7 @@ struct LoopOverrideView: View {
                                                     .foregroundColor(.secondary)
                                                     .foregroundColor(.secondary)
                                                 if !override.targetRange.isEmpty {
                                                 if !override.targetRange.isEmpty {
                                                     let range = override.targetRange.map { Localizer.formatQuantity($0) }.joined(separator: " - ")
                                                     let range = override.targetRange.map { Localizer.formatQuantity($0) }.joined(separator: " - ")
-                                                    Text("Target Range: \(range) \(UserDefaultsRepository.getPreferredUnit().localizedShortUnitString)")
+                                                    Text("Target Range: \(range) \(Localizer.getPreferredUnit().localizedShortUnitString)")
                                                         .font(.subheadline)
                                                         .font(.subheadline)
                                                         .foregroundColor(.secondary)
                                                         .foregroundColor(.secondary)
                                                 }
                                                 }

+ 7 - 7
LoopFollow/Remote/Nightscout/TrioNightscoutRemoteView.swift

@@ -58,7 +58,7 @@ struct TrioNightscoutRemoteView: View {
                                     Text("Current Target")
                                     Text("Current Target")
                                     Spacer()
                                     Spacer()
                                     Text(Localizer.formatQuantity(tempTargetValue))
                                     Text(Localizer.formatQuantity(tempTargetValue))
-                                    Text(UserDefaultsRepository.getPreferredUnit().localizedShortUnitString).foregroundColor(.secondary)
+                                    Text(Localizer.getPreferredUnit().localizedShortUnitString).foregroundColor(.secondary)
                                 }
                                 }
                                 Button {
                                 Button {
                                     alertType = .confirmCancellation
                                     alertType = .confirmCancellation
@@ -81,7 +81,7 @@ struct TrioNightscoutRemoteView: View {
                                 TextFieldWithToolBar(
                                 TextFieldWithToolBar(
                                     quantity: $newHKTarget,
                                     quantity: $newHKTarget,
                                     maxLength: 4,
                                     maxLength: 4,
-                                    unit: UserDefaultsRepository.getPreferredUnit(),
+                                    unit: Localizer.getPreferredUnit(),
                                     minValue: HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 80),
                                     minValue: HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 80),
                                     maxValue: HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 200),
                                     maxValue: HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 200),
                                     onValidationError: { message in
                                     onValidationError: { message in
@@ -89,7 +89,7 @@ struct TrioNightscoutRemoteView: View {
                                     }
                                     }
                                 )
                                 )
                                 .focused($targetFieldIsFocused)
                                 .focused($targetFieldIsFocused)
-                                Text(UserDefaultsRepository.getPreferredUnit().localizedShortUnitString).foregroundColor(.secondary)
+                                Text(Localizer.getPreferredUnit().localizedShortUnitString).foregroundColor(.secondary)
                             }
                             }
                             HStack {
                             HStack {
                                 Text("Duration")
                                 Text("Duration")
@@ -181,7 +181,7 @@ struct TrioNightscoutRemoteView: View {
                 case .confirmCommand:
                 case .confirmCommand:
                     return Alert(
                     return Alert(
                         title: Text("Confirm Command"),
                         title: Text("Confirm Command"),
-                        message: Text("New Target: \(Localizer.formatQuantity(newHKTarget)) \(UserDefaultsRepository.getPreferredUnit().localizedShortUnitString)\nDuration: \(Int(duration.doubleValue(for: HKUnit.minute()))) minutes"),
+                        message: Text("New Target: \(Localizer.formatQuantity(newHKTarget)) \(Localizer.getPreferredUnit().localizedShortUnitString)\nDuration: \(Int(duration.doubleValue(for: HKUnit.minute()))) minutes"),
                         primaryButton: .default(Text("Confirm"), action: {
                         primaryButton: .default(Text("Confirm"), action: {
                             enactTempTarget()
                             enactTempTarget()
                         }),
                         }),
@@ -244,7 +244,7 @@ struct TrioNightscoutRemoteView: View {
     }
     }
 
 
     private var isButtonDisabled: Bool {
     private var isButtonDisabled: Bool {
-        return newHKTarget.doubleValue(for: UserDefaultsRepository.getPreferredUnit()) == 0 ||
+        return newHKTarget.doubleValue(for: Localizer.getPreferredUnit()) == 0 ||
             duration.doubleValue(for: HKUnit.minute()) == 0 || isLoading
             duration.doubleValue(for: HKUnit.minute()) == 0 || isLoading
     }
     }
 
 
@@ -257,13 +257,13 @@ struct TrioNightscoutRemoteView: View {
                     self.statusMessage = "Command successfully sent to Nightscout."
                     self.statusMessage = "Command successfully sent to Nightscout."
                     LogManager.shared.log(
                     LogManager.shared.log(
                         category: .nightscout,
                         category: .nightscout,
-                        message: "sendTempTarget succeeded - New Target: \(Localizer.formatQuantity(newHKTarget)) \(UserDefaultsRepository.getPreferredUnit().localizedShortUnitString), Duration: \(Int(duration.doubleValue(for: HKUnit.minute()))) minutes"
+                        message: "sendTempTarget succeeded - New Target: \(Localizer.formatQuantity(newHKTarget)) \(Localizer.getPreferredUnit().localizedShortUnitString), Duration: \(Int(duration.doubleValue(for: HKUnit.minute()))) minutes"
                     )
                     )
                 } else {
                 } else {
                     self.statusMessage = "Failed to enact target."
                     self.statusMessage = "Failed to enact target."
                     LogManager.shared.log(
                     LogManager.shared.log(
                         category: .nightscout,
                         category: .nightscout,
-                        message: "sendTempTarget failed - New Target: \(Localizer.formatQuantity(newHKTarget)) \(UserDefaultsRepository.getPreferredUnit().localizedShortUnitString), Duration: \(Int(duration.doubleValue(for: HKUnit.minute()))) minutes"
+                        message: "sendTempTarget failed - New Target: \(Localizer.formatQuantity(newHKTarget)) \(Localizer.getPreferredUnit().localizedShortUnitString), Duration: \(Int(duration.doubleValue(for: HKUnit.minute()))) minutes"
                     )
                     )
                 }
                 }
                 self.alertType = .status
                 self.alertType = .status

+ 1 - 1
LoopFollow/Remote/TRC/OverrideView.swift

@@ -92,7 +92,7 @@ struct OverrideView: View {
                                                 }
                                                 }
 
 
                                                 if let target = override.target {
                                                 if let target = override.target {
-                                                    Text("Target: \(Localizer.formatQuantity(target)) \(UserDefaultsRepository.getPreferredUnit().localizedShortUnitString)")
+                                                    Text("Target: \(Localizer.formatQuantity(target)) \(Localizer.getPreferredUnit().localizedShortUnitString)")
                                                         .font(.subheadline)
                                                         .font(.subheadline)
                                                         .foregroundColor(.secondary)
                                                         .foregroundColor(.secondary)
                                                 }
                                                 }

+ 5 - 5
LoopFollow/Remote/TRC/TempTargetView.swift

@@ -50,7 +50,7 @@ struct TempTargetView: View {
                                     Text("Current Target")
                                     Text("Current Target")
                                     Spacer()
                                     Spacer()
                                     Text(Localizer.formatQuantity(tempTargetValue))
                                     Text(Localizer.formatQuantity(tempTargetValue))
-                                    Text(UserDefaultsRepository.getPreferredUnit().localizedShortUnitString).foregroundColor(.secondary)
+                                    Text(Localizer.getPreferredUnit().localizedShortUnitString).foregroundColor(.secondary)
                                 }
                                 }
                                 Button {
                                 Button {
                                     alertType = .confirmCancellation
                                     alertType = .confirmCancellation
@@ -73,7 +73,7 @@ struct TempTargetView: View {
                                 TextFieldWithToolBar(
                                 TextFieldWithToolBar(
                                     quantity: $newHKTarget,
                                     quantity: $newHKTarget,
                                     maxLength: 4,
                                     maxLength: 4,
-                                    unit: UserDefaultsRepository.getPreferredUnit(),
+                                    unit: Localizer.getPreferredUnit(),
                                     minValue: HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 80),
                                     minValue: HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 80),
                                     maxValue: HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 200),
                                     maxValue: HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 200),
                                     onValidationError: { message in
                                     onValidationError: { message in
@@ -81,7 +81,7 @@ struct TempTargetView: View {
                                     }
                                     }
                                 )
                                 )
                                 .focused($targetFieldIsFocused)
                                 .focused($targetFieldIsFocused)
-                                Text(UserDefaultsRepository.getPreferredUnit().localizedShortUnitString).foregroundColor(.secondary)
+                                Text(Localizer.getPreferredUnit().localizedShortUnitString).foregroundColor(.secondary)
                             }
                             }
                             HStack {
                             HStack {
                                 Text("Duration")
                                 Text("Duration")
@@ -173,7 +173,7 @@ struct TempTargetView: View {
                 case .confirmCommand:
                 case .confirmCommand:
                     return Alert(
                     return Alert(
                         title: Text("Confirm Command"),
                         title: Text("Confirm Command"),
-                        message: Text("New Target: \(Localizer.formatQuantity(newHKTarget)) \(UserDefaultsRepository.getPreferredUnit().localizedShortUnitString)\nDuration: \(Int(duration.doubleValue(for: HKUnit.minute()))) minutes"),
+                        message: Text("New Target: \(Localizer.formatQuantity(newHKTarget)) \(Localizer.getPreferredUnit().localizedShortUnitString)\nDuration: \(Int(duration.doubleValue(for: HKUnit.minute()))) minutes"),
                         primaryButton: .default(Text("Confirm"), action: {
                         primaryButton: .default(Text("Confirm"), action: {
                             enactTempTarget()
                             enactTempTarget()
                         }),
                         }),
@@ -242,7 +242,7 @@ struct TempTargetView: View {
     }
     }
 
 
     private var isButtonDisabled: Bool {
     private var isButtonDisabled: Bool {
-        return newHKTarget.doubleValue(for: UserDefaultsRepository.getPreferredUnit()) == 0 ||
+        return newHKTarget.doubleValue(for: Localizer.getPreferredUnit()) == 0 ||
             duration.doubleValue(for: HKUnit.minute()) == 0 || isLoading
             duration.doubleValue(for: HKUnit.minute()) == 0 || isLoading
     }
     }
 
 

+ 14 - 14
LoopFollow/Settings/AdvancedSettingsViewModel.swift

@@ -7,43 +7,43 @@ import Foundation
 class AdvancedSettingsViewModel: ObservableObject {
 class AdvancedSettingsViewModel: ObservableObject {
     @Published var downloadTreatments: Bool {
     @Published var downloadTreatments: Bool {
         didSet {
         didSet {
-            UserDefaultsRepository.downloadTreatments.value = downloadTreatments
+            Storage.shared.downloadTreatments.value = downloadTreatments
         }
         }
     }
     }
 
 
     @Published var downloadPrediction: Bool {
     @Published var downloadPrediction: Bool {
         didSet {
         didSet {
-            UserDefaultsRepository.downloadPrediction.value = downloadPrediction
+            Storage.shared.downloadPrediction.value = downloadPrediction
         }
         }
     }
     }
 
 
     @Published var graphBasal: Bool {
     @Published var graphBasal: Bool {
         didSet {
         didSet {
-            UserDefaultsRepository.graphBasal.value = graphBasal
+            Storage.shared.graphBasal.value = graphBasal
         }
         }
     }
     }
 
 
     @Published var graphBolus: Bool {
     @Published var graphBolus: Bool {
         didSet {
         didSet {
-            UserDefaultsRepository.graphBolus.value = graphBolus
+            Storage.shared.graphBolus.value = graphBolus
         }
         }
     }
     }
 
 
     @Published var graphCarbs: Bool {
     @Published var graphCarbs: Bool {
         didSet {
         didSet {
-            UserDefaultsRepository.graphCarbs.value = graphCarbs
+            Storage.shared.graphCarbs.value = graphCarbs
         }
         }
     }
     }
 
 
     @Published var graphOtherTreatments: Bool {
     @Published var graphOtherTreatments: Bool {
         didSet {
         didSet {
-            UserDefaultsRepository.graphOtherTreatments.value = graphOtherTreatments
+            Storage.shared.graphOtherTreatments.value = graphOtherTreatments
         }
         }
     }
     }
 
 
     @Published var bgUpdateDelay: Int {
     @Published var bgUpdateDelay: Int {
         didSet {
         didSet {
-            UserDefaultsRepository.bgUpdateDelay.value = bgUpdateDelay
+            Storage.shared.bgUpdateDelay.value = bgUpdateDelay
         }
         }
     }
     }
 
 
@@ -54,13 +54,13 @@ class AdvancedSettingsViewModel: ObservableObject {
     }
     }
 
 
     init() {
     init() {
-        downloadTreatments = UserDefaultsRepository.downloadTreatments.value
-        downloadPrediction = UserDefaultsRepository.downloadPrediction.value
-        graphBasal = UserDefaultsRepository.graphBasal.value
-        graphBolus = UserDefaultsRepository.graphBolus.value
-        graphCarbs = UserDefaultsRepository.graphCarbs.value
-        graphOtherTreatments = UserDefaultsRepository.graphOtherTreatments.value
-        bgUpdateDelay = UserDefaultsRepository.bgUpdateDelay.value
+        downloadTreatments = Storage.shared.downloadTreatments.value
+        downloadPrediction = Storage.shared.downloadPrediction.value
+        graphBasal = Storage.shared.graphBasal.value
+        graphBolus = Storage.shared.graphBolus.value
+        graphCarbs = Storage.shared.graphCarbs.value
+        graphOtherTreatments = Storage.shared.graphOtherTreatments.value
+        bgUpdateDelay = Storage.shared.bgUpdateDelay.value
         debugLogLevel = Storage.shared.debugLogLevel.value
         debugLogLevel = Storage.shared.debugLogLevel.value
     }
     }
 }
 }

+ 6 - 6
LoopFollow/Settings/DexcomSettingsViewModel.swift

@@ -6,26 +6,26 @@ import Combine
 import Foundation
 import Foundation
 
 
 class DexcomSettingsViewModel: ObservableObject {
 class DexcomSettingsViewModel: ObservableObject {
-    @Published var userName: String = UserDefaultsRepository.shareUserName.value {
+    @Published var userName: String = Storage.shared.shareUserName.value {
         willSet {
         willSet {
             if newValue != userName {
             if newValue != userName {
-                UserDefaultsRepository.shareUserName.value = newValue
+                Storage.shared.shareUserName.value = newValue
             }
             }
         }
         }
     }
     }
 
 
-    @Published var password: String = UserDefaultsRepository.sharePassword.value {
+    @Published var password: String = Storage.shared.sharePassword.value {
         willSet {
         willSet {
             if newValue != password {
             if newValue != password {
-                UserDefaultsRepository.sharePassword.value = newValue
+                Storage.shared.sharePassword.value = newValue
             }
             }
         }
         }
     }
     }
 
 
-    @Published var server: String = UserDefaultsRepository.shareServer.value {
+    @Published var server: String = Storage.shared.shareServer.value {
         willSet {
         willSet {
             if newValue != server {
             if newValue != server {
-                UserDefaultsRepository.shareServer.value = newValue
+                Storage.shared.shareServer.value = newValue
             }
             }
         }
         }
     }
     }

+ 2 - 2
LoopFollow/Settings/SettingsMenuView.swift

@@ -134,8 +134,8 @@ struct SettingsMenuView: View {
         Section("Data Settings") {
         Section("Data Settings") {
             Picker("Units",
             Picker("Units",
                    selection: Binding(
                    selection: Binding(
-                       get: { UserDefaultsRepository.units.value },
-                       set: { UserDefaultsRepository.units.value = $0 }
+                       get: { Storage.shared.units.value },
+                       set: { Storage.shared.units.value = $0 }
                    )) {
                    )) {
                 Text("mg/dL").tag("mg/dL")
                 Text("mg/dL").tag("mg/dL")
                 Text("mmol/L").tag("mmol/L")
                 Text("mmol/L").tag("mmol/L")

+ 2 - 2
LoopFollow/Snoozer/SnoozerView.swift

@@ -154,11 +154,11 @@ struct SnoozerView: View {
             return "🤷"
             return "🤷"
         }
         }
 
 
-        if UserDefaultsRepository.getPreferredUnit() == .millimolesPerLiter, Localizer.removePeriodAndCommaForBadge(bgText.value) == "55" {
+        if Localizer.getPreferredUnit() == .millimolesPerLiter, Localizer.removePeriodAndCommaForBadge(bgText.value) == "55" {
             return "🦄"
             return "🦄"
         }
         }
 
 
-        if UserDefaultsRepository.getPreferredUnit() == .milligramsPerDeciliter, bg == 100 {
+        if Localizer.getPreferredUnit() == .milligramsPerDeciliter, bg == 100 {
             return "🦄"
             return "🦄"
         }
         }
 
 

+ 66 - 5
LoopFollow/Storage/Storage+Migrate.swift

@@ -28,11 +28,6 @@ extension Storage {
             item.setNil(key: item.key)
             item.setNil(key: item.key)
         }
         }
 
 
-        if !UserDefaultsRepository.backgroundRefresh.value {
-            Storage.shared.backgroundRefreshType.value = .none
-            UserDefaultsRepository.backgroundRefresh.value = true
-        }
-
         // Remove this in a year later than the release of the new Alarms [BEGIN]
         // Remove this in a year later than the release of the new Alarms [BEGIN]
         let legacyColorBGText = UserDefaultsValue<Bool>(key: "colorBGText", default: true)
         let legacyColorBGText = UserDefaultsValue<Bool>(key: "colorBGText", default: true)
         if legacyColorBGText.exists {
         if legacyColorBGText.exists {
@@ -283,6 +278,72 @@ extension Storage {
             Storage.shared.alarmConfiguration.value = cfg
             Storage.shared.alarmConfiguration.value = cfg
         }
         }
 
 
+        // ── Dexcom Share --------------------------------------------------------
+        move(UserDefaultsValue<String>(key: "shareUserName", default: ""),
+             into: Storage.shared.shareUserName)
+
+        move(UserDefaultsValue<String>(key: "sharePassword", default: ""),
+             into: Storage.shared.sharePassword)
+
+        move(UserDefaultsValue<String>(key: "shareServer", default: "US"),
+             into: Storage.shared.shareServer)
+
+        // ── Graph ---------------------------------------------------------------
+        moveFloatToDouble(
+            UserDefaultsValue<Float>(key: "chartScaleX", default: 18.0),
+            into: Storage.shared.chartScaleX
+        )
+
+        // ── Advanced settings ---------------------------------------------------
+        move(UserDefaultsValue<Bool>(key: "downloadTreatments", default: true),
+             into: Storage.shared.downloadTreatments)
+        move(UserDefaultsValue<Bool>(key: "downloadPrediction", default: true),
+             into: Storage.shared.downloadPrediction)
+        move(UserDefaultsValue<Bool>(key: "graphOtherTreatments", default: true),
+             into: Storage.shared.graphOtherTreatments)
+        move(UserDefaultsValue<Bool>(key: "graphBasal", default: true),
+             into: Storage.shared.graphBasal)
+        move(UserDefaultsValue<Bool>(key: "graphBolus", default: true),
+             into: Storage.shared.graphBolus)
+        move(UserDefaultsValue<Bool>(key: "graphCarbs", default: true),
+             into: Storage.shared.graphCarbs)
+        move(UserDefaultsValue<Int>(key: "bgUpdateDelay", default: 10),
+             into: Storage.shared.bgUpdateDelay)
+
+        // ── Insert times --------------------------------------------------------
+        move(UserDefaultsValue<TimeInterval>(key: "alertCageInsertTime", default: 0),
+             into: Storage.shared.cageInsertTime)
+        move(UserDefaultsValue<TimeInterval>(key: "alertSageInsertTime", default: 0),
+             into: Storage.shared.sageInsertTime)
+
+        // ── Version-cache / notification bookkeeping ---------------------------
+        move(UserDefaultsValue<String?>(key: "cachedForVersion", default: nil),
+             into: Storage.shared.cachedForVersion)
+        move(UserDefaultsValue<String?>(key: "latestVersion", default: nil),
+             into: Storage.shared.latestVersion)
+        move(UserDefaultsValue<Date?>(key: "latestVersionChecked", default: nil),
+             into: Storage.shared.latestVersionChecked)
+        move(UserDefaultsValue<Bool>(key: "currentVersionBlackListed", default: false),
+             into: Storage.shared.currentVersionBlackListed)
+        move(UserDefaultsValue<Date?>(key: "lastBlacklistNotificationShown", default: nil),
+             into: Storage.shared.lastBlacklistNotificationShown)
+        move(UserDefaultsValue<Date?>(key: "lastVersionUpdateNotificationShown", default: nil),
+             into: Storage.shared.lastVersionUpdateNotificationShown)
+        move(UserDefaultsValue<Date?>(key: "lastExpirationNotificationShown", default: nil),
+             into: Storage.shared.lastExpirationNotificationShown)
+
+        move(UserDefaultsValue<Bool>(key: "hideInfoTable", default: false), into: Storage.shared.hideInfoTable)
+        move(UserDefaultsValue<String>(key: "token", default: ""), into: Storage.shared.token)
+        move(UserDefaultsValue<String>(key: "units", default: "mg/dL"), into: Storage.shared.units)
+
+        move(UserDefaultsValue<[Int]>(key: "infoSort",
+                                      default: InfoType.allCases.map { $0.sortOrder }),
+             into: Storage.shared.infoSort)
+
+        move(UserDefaultsValue<[Bool]>(key: "infoVisible",
+                                       default: InfoType.allCases.map { $0.defaultVisible }),
+             into: Storage.shared.infoVisible)
+
         migrateUrgentLowAlarm()
         migrateUrgentLowAlarm()
         migrateLowAlarm()
         migrateLowAlarm()
         migrateHighAlarm()
         migrateHighAlarm()

+ 42 - 0
LoopFollow/Storage/Storage.swift

@@ -107,6 +107,48 @@ class Storage {
     var watchLine2 = StorageValue<String>(key: "watchLine2", defaultValue: "C:%COB% I:%IOB% B:%BASAL%")
     var watchLine2 = StorageValue<String>(key: "watchLine2", defaultValue: "C:%COB% I:%IOB% B:%BASAL%")
     // Calendar entries [END]
     // Calendar entries [END]
 
 
+    // MARK: - Dexcom Share --------------------------------------------------------
+
+    var shareUserName = StorageValue<String>(key: "shareUserName", defaultValue: "")
+    var sharePassword = StorageValue<String>(key: "sharePassword", defaultValue: "")
+    var shareServer = StorageValue<String>(key: "shareServer", defaultValue: "US")
+
+    // MARK: - Graph ---------------------------------------------------------------
+
+    var chartScaleX = StorageValue<Double>(key: "chartScaleX", defaultValue: 18.0)
+
+    // MARK: - Advanced settings ---------------------------------------------------
+
+    var downloadTreatments = StorageValue<Bool>(key: "downloadTreatments", defaultValue: true)
+    var downloadPrediction = StorageValue<Bool>(key: "downloadPrediction", defaultValue: true)
+    var graphOtherTreatments = StorageValue<Bool>(key: "graphOtherTreatments", defaultValue: true)
+    var graphBasal = StorageValue<Bool>(key: "graphBasal", defaultValue: true)
+    var graphBolus = StorageValue<Bool>(key: "graphBolus", defaultValue: true)
+    var graphCarbs = StorageValue<Bool>(key: "graphCarbs", defaultValue: true)
+    var bgUpdateDelay = StorageValue<Int>(key: "bgUpdateDelay", defaultValue: 10)
+
+    // MARK: - Insert times (sensor / pump) ---------------------------------------
+
+    var cageInsertTime = StorageValue<TimeInterval>(key: "cageInsertTime", defaultValue: 0)
+    var sageInsertTime = StorageValue<TimeInterval>(key: "sageInsertTime", defaultValue: 0)
+
+    // MARK: - Version-info ---------------------------
+
+    var cachedForVersion = StorageValue<String?>(key: "cachedForVersion", defaultValue: nil)
+    var latestVersion = StorageValue<String?>(key: "latestVersion", defaultValue: nil)
+    var latestVersionChecked = StorageValue<Date?>(key: "latestVersionChecked", defaultValue: nil)
+    var currentVersionBlackListed = StorageValue<Bool>(key: "currentVersionBlackListed", defaultValue: false)
+    var lastBlacklistNotificationShown = StorageValue<Date?>(key: "lastBlacklistNotificationShown", defaultValue: nil)
+    var lastVersionUpdateNotificationShown = StorageValue<Date?>(key: "lastVersionUpdateNotificationShown", defaultValue: nil)
+    var lastExpirationNotificationShown = StorageValue<Date?>(key: "lastExpirationNotificationShown", defaultValue: nil)
+
+    var hideInfoTable = StorageValue<Bool>(key: "hideInfoTable", defaultValue: false)
+    var token = StorageValue<String>(key: "token", defaultValue: "")
+    var units = StorageValue<String>(key: "units", defaultValue: "mg/dL")
+
+    var infoSort = StorageValue<[Int]>(key: "infoSort", defaultValue: InfoType.allCases.map { $0.sortOrder })
+    var infoVisible = StorageValue<[Bool]>(key: "infoVisible", defaultValue: InfoType.allCases.map { $0.defaultVisible })
+
     static let shared = Storage()
     static let shared = Storage()
     private init() {}
     private init() {}
 }
 }

+ 0 - 116
LoopFollow/Storage/UserDefaults.swift

@@ -1,116 +0,0 @@
-// LoopFollow
-// UserDefaults.swift
-// Created by Jon Fawcett on 2020-06-05.
-
-import Foundation
-import HealthKit
-import UIKit
-
-/*
- Legacy storage, we are moving away from this
- */
-
-class UserDefaultsRepository {
-    static let infoSort = UserDefaultsValue<[Int]>(key: "infoSort", default: InfoType.allCases.map { $0.sortOrder })
-    static let infoVisible = UserDefaultsValue<[Bool]>(key: "infoVisible", default: InfoType.allCases.map { $0.defaultVisible })
-
-    static func synchronizeInfoTypes() {
-        var sortArray = infoSort.value
-        var visibleArray = infoVisible.value
-
-        // Current valid indices based on InfoType
-        let currentValidIndices = InfoType.allCases.map { $0.rawValue }
-
-        // Add missing indices to sortArray
-        for index in currentValidIndices {
-            if !sortArray.contains(index) {
-                sortArray.append(index)
-                // print("Added missing index \(index) to sortArray")
-            }
-        }
-
-        // Remove deprecated indices
-        sortArray = sortArray.filter { currentValidIndices.contains($0) }
-
-        // Ensure visibleArray is updated with new entries
-        if visibleArray.count < currentValidIndices.count {
-            for i in visibleArray.count ..< currentValidIndices.count {
-                visibleArray.append(InfoType(rawValue: i)?.defaultVisible ?? false)
-                // print("Added default visibility for new index \(i)")
-            }
-        }
-
-        // Trim excess elements if there are more than needed
-        if visibleArray.count > currentValidIndices.count {
-            visibleArray = Array(visibleArray.prefix(currentValidIndices.count))
-            // print("Trimmed visibleArray to match current valid indices")
-        }
-
-        infoSort.value = sortArray
-        infoVisible.value = visibleArray
-    }
-
-    static let hideInfoTable = UserDefaultsValue<Bool>(key: "hideInfoTable", default: false)
-
-    // Nightscout Settings
-    static let token = UserDefaultsValue<String>(key: "token", default: "")
-    static let units = UserDefaultsValue<String>(key: "units", default: "mg/dL")
-
-    static func getPreferredUnit() -> HKUnit {
-        let unitString = units.value
-        switch unitString {
-        case "mmol/L":
-            return .millimolesPerLiter
-        default:
-            return .milligramsPerDeciliter
-        }
-    }
-
-    static func setPreferredUnit(_ unit: HKUnit) {
-        var unitString = "mg/dL"
-        if unit == .millimolesPerLiter {
-            unitString = "mmol/L"
-        }
-        units.value = unitString
-    }
-
-    // Dexcom Share Settings
-    static let shareUserName = UserDefaultsValue<String>(key: "shareUserName", default: "")
-    static let sharePassword = UserDefaultsValue<String>(key: "sharePassword", default: "")
-    static let shareServer = UserDefaultsValue<String>(key: "shareServer", default: "US")
-
-    // Graph Settings
-    static let chartScaleX = UserDefaultsValue<Float>(key: "chartScaleX", default: 18.0)
-
-    // Deprecated, used to detect if backgroundRefresh was set to off. TODO: Remove in the beginning of 2026
-    static let backgroundRefresh = UserDefaultsValue<Bool>(key: "backgroundRefresh", default: true)
-
-    // Advanced Settings
-    static let downloadTreatments = UserDefaultsValue<Bool>(key: "downloadTreatments", default: true)
-    static let downloadPrediction = UserDefaultsValue<Bool>(key: "downloadPrediction", default: true)
-    static let graphOtherTreatments = UserDefaultsValue<Bool>(key: "graphOtherTreatments", default: true)
-    static let graphBasal = UserDefaultsValue<Bool>(key: "graphBasal", default: true)
-    static let graphBolus = UserDefaultsValue<Bool>(key: "graphBolus", default: true)
-    static let graphCarbs = UserDefaultsValue<Bool>(key: "graphCarbs", default: true)
-    static let bgUpdateDelay = UserDefaultsValue<Int>(key: "bgUpdateDelay", default: 10)
-
-    static let alertCageInsertTime = UserDefaultsValue<TimeInterval>(key: "alertCageInsertTime", default: 0)
-    static let alertSageInsertTime = UserDefaultsValue<TimeInterval>(key: "alertSageInsertTime", default: 0)
-
-    // What version is the cache valid for
-    static let cachedForVersion = UserDefaultsValue<String?>(key: "cachedForVersion", default: nil)
-
-    // Caching of latest version
-    static let latestVersion = UserDefaultsValue<String?>(key: "latestVersion", default: nil)
-    static let latestVersionChecked = UserDefaultsValue<Date?>(key: "latestVersionChecked", default: nil)
-
-    // Caching of blacklisted version
-    static let currentVersionBlackListed = UserDefaultsValue<Bool>(key: "currentVersionBlackListed", default: false)
-
-    // Tracking notifications to manage frequency
-    static let lastBlacklistNotificationShown = UserDefaultsValue<Date?>(key: "lastBlacklistNotificationShown", default: nil)
-    static let lastVersionUpdateNotificationShown = UserDefaultsValue<Date?>(key: "lastVersionUpdateNotificationShown", default: nil)
-
-    // Tracking the last time the expiration notification was shown
-    static let lastExpirationNotificationShown = UserDefaultsValue<Date?>(key: "lastExpirationNotificationShown", default: nil)
-}

+ 2 - 2
LoopFollow/Task/AlarmTask.swift

@@ -22,8 +22,8 @@ extension MainViewController {
             let latestTempTargetEnd = self.tempTargetGraphData.last { $0.endDate <= now }?.endDate
             let latestTempTargetEnd = self.tempTargetGraphData.last { $0.endDate <= now }?.endDate
             let recBolus = Observable.shared.deviceRecBolus.value
             let recBolus = Observable.shared.deviceRecBolus.value
             let COB = self.latestCOB?.value
             let COB = self.latestCOB?.value
-            let sensorInsertedAt = UserDefaultsRepository.alertSageInsertTime.value
-            let pumpInsertTime = UserDefaultsRepository.alertCageInsertTime.value
+            let sensorInsertedAt = Storage.shared.sageInsertTime.value
+            let pumpInsertTime = Storage.shared.cageInsertTime.value
             let latestPumpVol = self.latestPumpVolume
             let latestPumpVol = self.latestPumpVolume
             let bolusEntries = self.bolusData.map { BolusEntry(units: $0.value, date: Date(timeIntervalSince1970: $0.date)) }
             let bolusEntries = self.bolusData.map { BolusEntry(units: $0.value, date: Date(timeIntervalSince1970: $0.date)) }
             let latestBattery = Observable.shared.deviceBatteryLevel.value
             let latestBattery = Observable.shared.deviceBatteryLevel.value

+ 4 - 4
LoopFollow/Task/BGTask.swift

@@ -21,16 +21,16 @@ extension MainViewController {
         )
         )
 
 
         // If no Dexcom credentials and no Nightscout, schedule a retry in 60 seconds.
         // If no Dexcom credentials and no Nightscout, schedule a retry in 60 seconds.
-        if UserDefaultsRepository.shareUserName.value == "",
-           UserDefaultsRepository.sharePassword.value == "",
+        if Storage.shared.shareUserName.value == "",
+           Storage.shared.sharePassword.value == "",
            !IsNightscoutEnabled()
            !IsNightscoutEnabled()
         {
         {
             return
             return
         }
         }
 
 
         // If Dexcom credentials exist, fetch from DexShare
         // If Dexcom credentials exist, fetch from DexShare
-        if UserDefaultsRepository.shareUserName.value != "",
-           UserDefaultsRepository.sharePassword.value != ""
+        if Storage.shared.shareUserName.value != "",
+           Storage.shared.sharePassword.value != ""
         {
         {
             webLoadDexShare()
             webLoadDexShare()
         } else {
         } else {

+ 1 - 1
LoopFollow/Task/TreatmentsTask.swift

@@ -15,7 +15,7 @@ extension MainViewController {
 
 
     func treatmentsTaskAction() {
     func treatmentsTaskAction() {
         // If Nightscout not enabled, wait 60s and try again
         // If Nightscout not enabled, wait 60s and try again
-        guard IsNightscoutEnabled(), UserDefaultsRepository.downloadTreatments.value else {
+        guard IsNightscoutEnabled(), Storage.shared.downloadTreatments.value else {
             TaskScheduler.shared.rescheduleTask(id: .treatments, to: Date().addingTimeInterval(60))
             TaskScheduler.shared.rescheduleTask(id: .treatments, to: Date().addingTimeInterval(60))
             return
             return
         }
         }

+ 49 - 13
LoopFollow/ViewControllers/MainViewController.swift

@@ -126,7 +126,7 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
         Storage.shared.migrate()
         Storage.shared.migrate()
 
 
         // Synchronize info types to ensure arrays are the correct size
         // Synchronize info types to ensure arrays are the correct size
-        UserDefaultsRepository.synchronizeInfoTypes()
+        synchronizeInfoTypes()
 
 
         infoTable.rowHeight = 21
         infoTable.rowHeight = 21
         infoTable.dataSource = self
         infoTable.dataSource = self
@@ -139,9 +139,9 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
         smallGraphHeightConstraint.constant = CGFloat(Storage.shared.smallGraphHeight.value)
         smallGraphHeightConstraint.constant = CGFloat(Storage.shared.smallGraphHeight.value)
         view.layoutIfNeeded()
         view.layoutIfNeeded()
 
 
-        let shareUserName = UserDefaultsRepository.shareUserName.value
-        let sharePassword = UserDefaultsRepository.sharePassword.value
-        let shareServer = UserDefaultsRepository.shareServer.value == "US" ?KnownShareServers.US.rawValue : KnownShareServers.NON_US.rawValue
+        let shareUserName = Storage.shared.shareUserName.value
+        let sharePassword = Storage.shared.sharePassword.value
+        let shareServer = Storage.shared.shareServer.value == "US" ?KnownShareServers.US.rawValue : KnownShareServers.NON_US.rawValue
         dexShare = ShareClient(username: shareUserName, password: sharePassword, shareServer: shareServer)
         dexShare = ShareClient(username: shareUserName, password: sharePassword, shareServer: shareServer)
 
 
         // setup show/hide small graph and stats
         // setup show/hide small graph and stats
@@ -420,17 +420,17 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
 
 
             // Check if the current version is blacklisted, or if there is a newer version available
             // Check if the current version is blacklisted, or if there is a newer version available
             if isBlacklisted {
             if isBlacklisted {
-                let lastBlacklistShown = UserDefaultsRepository.lastBlacklistNotificationShown.value ?? Date.distantPast
+                let lastBlacklistShown = Storage.shared.lastBlacklistNotificationShown.value ?? Date.distantPast
                 if now.timeIntervalSince(lastBlacklistShown) > 86400 { // 24 hours
                 if now.timeIntervalSince(lastBlacklistShown) > 86400 { // 24 hours
                     self.versionAlert(message: "The current version has a critical issue and should be updated as soon as possible.")
                     self.versionAlert(message: "The current version has a critical issue and should be updated as soon as possible.")
-                    UserDefaultsRepository.lastBlacklistNotificationShown.value = now
-                    UserDefaultsRepository.lastVersionUpdateNotificationShown.value = now
+                    Storage.shared.lastBlacklistNotificationShown.value = now
+                    Storage.shared.lastVersionUpdateNotificationShown.value = now
                 }
                 }
             } else if isNewer {
             } else if isNewer {
-                let lastVersionUpdateShown = UserDefaultsRepository.lastVersionUpdateNotificationShown.value ?? Date.distantPast
+                let lastVersionUpdateShown = Storage.shared.lastVersionUpdateNotificationShown.value ?? Date.distantPast
                 if now.timeIntervalSince(lastVersionUpdateShown) > 1_209_600 { // 2 weeks
                 if now.timeIntervalSince(lastVersionUpdateShown) > 1_209_600 { // 2 weeks
                     self.versionAlert(message: "A new version is available: \(latestVersion ?? "Unknown"). It is recommended to update.")
                     self.versionAlert(message: "A new version is available: \(latestVersion ?? "Unknown"). It is recommended to update.")
-                    UserDefaultsRepository.lastVersionUpdateNotificationShown.value = now
+                    Storage.shared.lastVersionUpdateNotificationShown.value = now
                 }
                 }
             }
             }
         }
         }
@@ -450,10 +450,10 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
         let weekBeforeExpiration = Calendar.current.date(byAdding: .day, value: -7, to: expirationDate)!
         let weekBeforeExpiration = Calendar.current.date(byAdding: .day, value: -7, to: expirationDate)!
 
 
         if now >= weekBeforeExpiration {
         if now >= weekBeforeExpiration {
-            let lastExpirationShown = UserDefaultsRepository.lastExpirationNotificationShown.value ?? Date.distantPast
+            let lastExpirationShown = Storage.shared.lastExpirationNotificationShown.value ?? Date.distantPast
             if now.timeIntervalSince(lastExpirationShown) > 86400 { // 24 hours
             if now.timeIntervalSince(lastExpirationShown) > 86400 { // 24 hours
                 expirationAlert()
                 expirationAlert()
-                UserDefaultsRepository.lastExpirationNotificationShown.value = now
+                Storage.shared.lastExpirationNotificationShown.value = now
             }
             }
         }
         }
     }
     }
@@ -493,7 +493,7 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
         }
         }
         infoTable.isHidden = isHidden
         infoTable.isHidden = isHidden
 
 
-        if UserDefaultsRepository.hideInfoTable.value {
+        if Storage.shared.hideInfoTable.value {
             infoTable.isHidden = true
             infoTable.isHidden = true
         }
         }
 
 
@@ -706,11 +706,47 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
                 }
                 }
 
 
                 if let token = debugData.token {
                 if let token = debugData.token {
-                    UserDefaultsRepository.token.value = token
+                    Storage.shared.token.value = token
                 }
                 }
             } catch {
             } catch {
                 LogManager.shared.log(category: .alarm, message: "Failed to load DebugData: \(error)", isDebug: true)
                 LogManager.shared.log(category: .alarm, message: "Failed to load DebugData: \(error)", isDebug: true)
             }
             }
         }
         }
     }
     }
+
+    private func synchronizeInfoTypes() {
+        var sortArray = Storage.shared.infoSort.value
+        var visibleArray = Storage.shared.infoVisible.value
+
+        // Current valid indices based on InfoType
+        let currentValidIndices = InfoType.allCases.map { $0.rawValue }
+
+        // Add missing indices to sortArray
+        for index in currentValidIndices {
+            if !sortArray.contains(index) {
+                sortArray.append(index)
+                // print("Added missing index \(index) to sortArray")
+            }
+        }
+
+        // Remove deprecated indices
+        sortArray = sortArray.filter { currentValidIndices.contains($0) }
+
+        // Ensure visibleArray is updated with new entries
+        if visibleArray.count < currentValidIndices.count {
+            for i in visibleArray.count ..< currentValidIndices.count {
+                visibleArray.append(InfoType(rawValue: i)?.defaultVisible ?? false)
+                // print("Added default visibility for new index \(i)")
+            }
+        }
+
+        // Trim excess elements if there are more than needed
+        if visibleArray.count > currentValidIndices.count {
+            visibleArray = Array(visibleArray.prefix(currentValidIndices.count))
+            // print("Trimmed visibleArray to match current valid indices")
+        }
+
+        Storage.shared.infoSort.value = sortArray
+        Storage.shared.infoVisible.value = visibleArray
+    }
 }
 }

+ 1 - 1
LoopFollow/ViewControllers/NightScoutViewController.swift

@@ -15,7 +15,7 @@ class NightscoutViewController: UIViewController {
         }
         }
 
 
         var url = ObservableUserDefaults.shared.url.value
         var url = ObservableUserDefaults.shared.url.value
-        let token = UserDefaultsRepository.token.value
+        let token = Storage.shared.token.value
 
 
         if token != "" {
         if token != "" {
             url = url + "?token=" + token
             url = url + "?token=" + token