Prechádzať zdrojové kódy

Merge branch 'Crowdin' into XPM

Jon B.M 4 rokov pred
rodič
commit
a10a5c4e45
100 zmenil súbory, kde vykonal 682 pridanie a 1060 odobranie
  1. 5 0
      Dependencies/LoopKit/LoopKit/Insulin/ExponentialInsulinModelPreset.swift
  2. 6 0
      Dependencies/LoopKit/LoopKit/Insulin/InsulinModelSettings.swift
  3. 5 0
      Dependencies/LoopKit/LoopKit/InsulinKit/HKQuantitySample+InsulinKit.swift
  4. 7 0
      Dependencies/LoopKit/LoopKit/InsulinKit/InsulinType.swift
  5. 1 0
      Dependencies/LoopKit/LoopKit/StoredInsulinModel.swift
  6. 38 0
      Dependencies/LoopKit/LoopKitUI/Assets.xcassets/Lyumjev.colorset/Contents.json
  7. 4 0
      Dependencies/LoopKit/LoopKitUI/InsulinModelSettings+LoopKitUI.swift
  8. 1 1
      Dependencies/LoopKit/LoopKitUI/View Controllers/OverrideSelectionViewController.swift
  9. 3 3
      Dependencies/LoopKit/LoopKitUI/Views/OverrideSelectionHistory.swift
  10. 10 10
      Dependencies/rileylink_ios/OmniKitUI/nl.lproj/Localizable.strings
  11. 8 8
      Dependencies/rileylink_ios/RileyLinkKitUI/nl.lproj/Localizable.strings
  12. 117 359
      FreeAPS.xcodeproj/project.pbxproj
  13. 1 1
      FreeAPS/Resources/Config.xcconfig
  14. 2 2
      FreeAPS/Sources/APS/Extensions/PumpManagerExtensions.swift
  15. 26 38
      FreeAPS/Sources/Application/FreeAPSApp.swift
  16. 12 0
      FreeAPS/Sources/Assemblies/APSAssembly.swift
  17. 13 0
      FreeAPS/Sources/Assemblies/NetworkAssembly.swift
  18. 8 0
      FreeAPS/Sources/Assemblies/SecurityAssembly.swift
  19. 2 4
      FreeAPS/Sources/Containers/ServiceContainer.swift
  20. 18 0
      FreeAPS/Sources/Assemblies/StorageAssembly.swift
  21. 9 0
      FreeAPS/Sources/Assemblies/UIAssembly.swift
  22. 0 14
      FreeAPS/Sources/Containers/APSContainer.swift
  23. 0 10
      FreeAPS/Sources/Containers/DependeciesContainer.swift
  24. 0 16
      FreeAPS/Sources/Containers/NetworkContainer.swift
  25. 0 9
      FreeAPS/Sources/Containers/SecurityContainer.swift
  26. 0 21
      FreeAPS/Sources/Containers/StorageContainer.swift
  27. 0 10
      FreeAPS/Sources/Containers/UIContainer.swift
  28. 1 1
      FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings
  29. 1 1
      FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings
  30. 1 1
      FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings
  31. 1 1
      FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings
  32. 1 1
      FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings
  33. 1 1
      FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings
  34. 1 1
      FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings
  35. 1 1
      FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings
  36. 1 1
      FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings
  37. 1 1
      FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings
  38. 1 1
      FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings
  39. 1 1
      FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings
  40. 1 1
      FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings
  41. 1 1
      FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings
  42. 1 1
      FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings
  43. 1 1
      FreeAPS/Sources/Localizations/Main/pt.lproj/Localizable.strings
  44. 25 25
      FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings
  45. 3 3
      FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings
  46. 1 1
      FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings
  47. 1 1
      FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings
  48. 2 2
      FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings
  49. 1 1
      FreeAPS/Sources/Models/Preferences.swift
  50. 0 3
      FreeAPS/Sources/Modules/AddCarbs/AddCarbsBuilder.swift
  51. 1 1
      FreeAPS/Sources/Modules/AddCarbs/AddCarbsViewModel.swift
  52. 10 7
      FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift
  53. 0 3
      FreeAPS/Sources/Modules/AddTempTarget/AddTempTargetBuilder.swift
  54. 1 1
      FreeAPS/Sources/Modules/AddTempTarget/AddTempTargetViewModel.swift
  55. 21 18
      FreeAPS/Sources/Modules/AddTempTarget/View/AddTempTargetRootView.swift
  56. 0 3
      FreeAPS/Sources/Modules/AuthorizedRoot/AuthorizedRootBuilder.swift
  57. 0 7
      FreeAPS/Sources/Modules/AuthorizedRoot/AuthorizedRootDataFlow.swift
  58. 0 3
      FreeAPS/Sources/Modules/AuthorizedRoot/AuthorizedRootProvider.swift
  59. 0 10
      FreeAPS/Sources/Modules/AuthorizedRoot/AuthorizedRootViewModel.swift
  60. 0 13
      FreeAPS/Sources/Modules/AuthorizedRoot/View/AuthotizedRootRootView.swift
  61. 0 3
      FreeAPS/Sources/Modules/AutotuneConfig/AutotuneConfigBuilder.swift
  62. 11 5
      FreeAPS/Sources/Modules/AutotuneConfig/AutotuneConfigViewModel.swift
  63. 11 8
      FreeAPS/Sources/Modules/AutotuneConfig/View/AutotuneConfigRootView.swift
  64. 0 3
      FreeAPS/Sources/Modules/BasalProfileEditor/BasalProfileEditorBuilder.swift
  65. 1 1
      FreeAPS/Sources/Modules/BasalProfileEditor/BasalProfileEditorViewModel.swift
  66. 22 19
      FreeAPS/Sources/Modules/BasalProfileEditor/View/BasalProfileEditorRootView.swift
  67. 0 26
      FreeAPS/Sources/Modules/Base/BaseModuleBuilder.swift
  68. 0 1
      FreeAPS/Sources/Modules/Base/BaseProvider.swift
  69. 43 0
      FreeAPS/Sources/Modules/Base/BaseStateModel.swift
  70. 22 4
      FreeAPS/Sources/Modules/Base/BaseView.swift
  71. 0 56
      FreeAPS/Sources/Modules/Base/BaseViewModel.swift
  72. 0 15
      FreeAPS/Sources/Modules/Bolus/BolusBuilder.swift
  73. 6 15
      FreeAPS/Sources/Modules/Bolus/BolusViewModel.swift
  74. 25 16
      FreeAPS/Sources/Modules/Bolus/View/BolusRootView.swift
  75. 0 3
      FreeAPS/Sources/Modules/CGM/CGMBuilder.swift
  76. 1 1
      FreeAPS/Sources/Modules/CGM/CGMViewModel.swift
  77. 10 7
      FreeAPS/Sources/Modules/CGM/View/CGMRootView.swift
  78. 0 3
      FreeAPS/Sources/Modules/CREditor/CREditorBuilder.swift
  79. 1 1
      FreeAPS/Sources/Modules/CREditor/CREditorViewModel.swift
  80. 21 18
      FreeAPS/Sources/Modules/CREditor/View/CREditorRootView.swift
  81. 0 16
      FreeAPS/Sources/Modules/ConfigEditor/ConfigEditorBuilder.swift
  82. 17 0
      FreeAPS/Sources/Modules/ConfigEditor/ConfigEditorStateModel.swift
  83. 0 26
      FreeAPS/Sources/Modules/ConfigEditor/ConfigEditorViewModel.swift
  84. 13 5
      FreeAPS/Sources/Modules/ConfigEditor/View/ConfigEditorRootView.swift
  85. 0 3
      FreeAPS/Sources/Modules/DataTable/DataTableBuilder.swift
  86. 2 2
      FreeAPS/Sources/Modules/DataTable/DataTableViewModel.swift
  87. 7 4
      FreeAPS/Sources/Modules/DataTable/View/DataTableRootView.swift
  88. 0 3
      FreeAPS/Sources/Modules/Home/HomeBuilder.swift
  89. 3 3
      FreeAPS/Sources/Modules/Home/HomeViewModel.swift
  90. 60 56
      FreeAPS/Sources/Modules/Home/View/HomeRootView.swift
  91. 0 3
      FreeAPS/Sources/Modules/ISFEditor/ISFEditorBuilder.swift
  92. 1 1
      FreeAPS/Sources/Modules/ISFEditor/ISFEditorViewModel.swift
  93. 27 24
      FreeAPS/Sources/Modules/ISFEditor/View/ISFEditorRootView.swift
  94. 0 3
      FreeAPS/Sources/Modules/Login/LoginBuilder.swift
  95. 0 11
      FreeAPS/Sources/Modules/Login/LoginDataFlow.swift
  96. 0 19
      FreeAPS/Sources/Modules/Login/LoginProvider.swift
  97. 0 23
      FreeAPS/Sources/Modules/Login/LoginViewModel.swift
  98. 0 24
      FreeAPS/Sources/Modules/Login/View/LoginRootView.swift
  99. 0 5
      FreeAPS/Sources/Modules/Main/MainBuilder.swift
  100. 0 0
      FreeAPS/Sources/Modules/Main/MainDataFlow.swift

+ 5 - 0
Dependencies/LoopKit/LoopKit/Insulin/ExponentialInsulinModelPreset.swift

@@ -9,6 +9,7 @@ public enum ExponentialInsulinModelPreset: String, Codable {
     case rapidActingAdult
     case rapidActingChild
     case fiasp
+    case lyumjev
 }
 
 
@@ -22,6 +23,8 @@ extension ExponentialInsulinModelPreset {
             return .minutes(360)
         case .fiasp:
             return .minutes(360)
+        case .lyumjev:
+            return .minutes(330)
         }
     }
 
@@ -33,6 +36,8 @@ extension ExponentialInsulinModelPreset {
             return .minutes(65)
         case .fiasp:
             return .minutes(55)
+        case .lyumjev:
+            return .minutes(45)
         }
     }
 

+ 6 - 0
Dependencies/LoopKit/LoopKit/Insulin/InsulinModelSettings.swift

@@ -28,6 +28,8 @@ public enum InsulinModelSettings: Equatable {
         switch type {
         case .fiasp:
             return ExponentialInsulinModelPreset.fiasp
+        case .lyumjev:
+            return ExponentialInsulinModelPreset.lyumjev
         default:
             switch self {
             case .exponentialPreset(let model):
@@ -150,6 +152,8 @@ public extension InsulinModelSettings {
         switch storedSettingsInsulinModel.modelType {
         case .fiasp:
             self = .exponentialPreset(.fiasp)
+        case .lyumjev:
+            self = .exponentialPreset(.lyumjev)
         case .rapidAdult:
             self = .exponentialPreset(.rapidActingAdult)
         case .rapidChild:
@@ -175,6 +179,8 @@ public extension StoredInsulinModel {
                 modelType = .rapidChild
             case .fiasp:
                 modelType = .fiasp
+            case .lyumjev:
+                modelType = .lyumjev
             }
             actionDuration = preset.actionDuration
             peakActivity = preset.peakActivity

+ 5 - 0
Dependencies/LoopKit/LoopKit/InsulinKit/HKQuantitySample+InsulinKit.swift

@@ -189,6 +189,7 @@ enum InsulinTypeHealthKitRepresentation: String {
     case humalog = "Humalog"
     case apidra = "Apidra"
     case fiasp = "Fiasp"
+    case lyumjev = "Lyumjev"
 }
 
 extension InsulinType {
@@ -202,6 +203,8 @@ extension InsulinType {
             return InsulinTypeHealthKitRepresentation.apidra.rawValue
         case .fiasp:
             return InsulinTypeHealthKitRepresentation.fiasp.rawValue
+        case .lyumjev:
+            return InsulinTypeHealthKitRepresentation.lyumjev.rawValue
         }
     }
     
@@ -215,6 +218,8 @@ extension InsulinType {
             self = .apidra
         case InsulinTypeHealthKitRepresentation.fiasp.rawValue:
             self = .fiasp
+        case InsulinTypeHealthKitRepresentation.lyumjev.rawValue:
+            self = .lyumjev
         default:
             return nil
         }

+ 7 - 0
Dependencies/LoopKit/LoopKit/InsulinKit/InsulinType.swift

@@ -13,6 +13,7 @@ public enum InsulinType: Int, Codable, CaseIterable {
     case humalog
     case apidra
     case fiasp
+    case lyumjev
     
     public var title: String {
         switch self {
@@ -24,6 +25,8 @@ public enum InsulinType: Int, Codable, CaseIterable {
             return LocalizedString("Apidra (insulin glulisine)", comment: "Title for Apidra insulin type")
         case .fiasp:
             return LocalizedString("Fiasp", comment: "Title for Fiasp insulin type")
+        case .lyumjev:
+            return LocalizedString("Lyumjev", comment: "Title for Lyumjev insulin type")
         }
     }
     
@@ -37,6 +40,8 @@ public enum InsulinType: Int, Codable, CaseIterable {
             return LocalizedString("Apidra", comment: "Brand name for apidra insulin type")
         case .fiasp:
             return LocalizedString("Fiasp", comment: "Brand name for fiasp insulin type")
+        case .lyumjev:
+            return LocalizedString("Lyumjev", comment: "Brand name for lyumjev insulin type")
         }
     }
     
@@ -50,6 +55,8 @@ public enum InsulinType: Int, Codable, CaseIterable {
             return LocalizedString("Apidra (insulin glulisine) is a fast-acting insulin made by Sanofi-aventis ", comment: "Description for apidra insulin type")
         case .fiasp:
             return LocalizedString("Fiasp is a mealtime insulin aspart formulation with the addition of nicotinamide (vitamin B3) made by Novo Nordisk", comment: "Description for fiasp insulin type")
+        case .lyumjev:
+            return LocalizedString("Lyumjev (ultra rapid lispro) is a mealtime insulin lispro formulation with the addition of Citrat and Treprostinil made by Eli Lilly", comment: "Description for lyumjev insulin type")
         }
     }
 }

+ 1 - 0
Dependencies/LoopKit/LoopKit/StoredInsulinModel.swift

@@ -11,6 +11,7 @@ import Foundation
 public struct StoredInsulinModel: Codable, Equatable {
     public enum ModelType: String, Codable {
         case fiasp
+        case lyumjev
         case rapidAdult
         case rapidChild
         case walsh

+ 38 - 0
Dependencies/LoopKit/LoopKitUI/Assets.xcassets/Lyumjev.colorset/Contents.json

@@ -0,0 +1,38 @@
+{
+  "colors" : [
+    {
+      "color" : {
+        "color-space" : "display-p3",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "0.385",
+          "green" : "0.179",
+          "red" : "0.866"
+        }
+      },
+      "idiom" : "universal"
+    },
+    {
+      "appearances" : [
+        {
+          "appearance" : "luminosity",
+          "value" : "dark"
+        }
+      ],
+      "color" : {
+        "color-space" : "display-p3",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "0.314",
+          "green" : "0.149",
+          "red" : "0.693"
+        }
+      },
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 4 - 0
Dependencies/LoopKit/LoopKitUI/InsulinModelSettings+LoopKitUI.swift

@@ -37,6 +37,8 @@ public extension ExponentialInsulinModelPreset {
             return LocalizedString("Rapid-Acting – Children", comment: "Title of insulin model preset - rapid acting children")
         case .fiasp:
             return LocalizedString("Fiasp", comment: "Title of insulin model preset - fiasp")
+        case .lyumjev:
+            return LocalizedString("Lyumjev", comment: "Title of insulin model preset - lyumjev")
         }
     }
 
@@ -48,6 +50,8 @@ public extension ExponentialInsulinModelPreset {
             return LocalizedString("This model assumes peak insulin activity at 65 minutes.", comment: "Subtitle of Rapid-Acting – Children preset")
         case .fiasp:
             return LocalizedString("This model assumes peak insulin activity at 55 minutes.", comment: "Subtitle of Fiasp preset")
+        case .lyumjev:
+            return LocalizedString("This model assumes peak insulin activity at 45 minutes.", comment: "Subtitle of Lyumjev preset")
         }
     }
 }

+ 1 - 1
Dependencies/LoopKit/LoopKitUI/View Controllers/OverrideSelectionViewController.swift

@@ -281,7 +281,7 @@ public final class OverrideSelectionViewController: UICollectionViewController,
                 customOverrideVC.delegate = self
                 show(customOverrideVC, sender: collectionView.cellForItem(at: indexPath))
             case .history:
-                let model = OverrideHistoryViewModel(
+                let model = OverrideHistorystate(
                     overrides: overrideHistory,
                     glucoseUnit: glucoseUnit
                 )

+ 3 - 3
Dependencies/LoopKit/LoopKitUI/Views/OverrideSelectionHistory.swift

@@ -10,7 +10,7 @@ import SwiftUI
 import LoopKit
 import HealthKit
 
-public class OverrideHistoryViewModel: ObservableObject {
+public class OverrideHistorystate: ObservableObject {
     var overrides: [TemporaryScheduleOverride]
     var glucoseUnit: HKUnit
     var didEditOverride: ((TemporaryScheduleOverride) -> Void)?
@@ -26,12 +26,12 @@ public class OverrideHistoryViewModel: ObservableObject {
 }
 
 public struct OverrideSelectionHistory: View {
-    @ObservedObject var model: OverrideHistoryViewModel
+    @ObservedObject var model: OverrideHistorystate
     private var quantityFormatter: QuantityFormatter
     private var glucoseNumberFormatter: NumberFormatter
     private var durationFormatter: DateComponentsFormatter
     
-    public init(model: OverrideHistoryViewModel) {
+    public init(model: OverrideHistorystate) {
         self.model = model
         self.quantityFormatter = {
             let quantityFormatter = QuantityFormatter()

+ 10 - 10
Dependencies/rileylink_ios/OmniKitUI/nl.lproj/Localizable.strings

@@ -1,5 +1,5 @@
 /* Accessibility format string for (1: localized volume)(2: time) */
-"%1$@ units remaining at %2$@" = "%1$@ eenheden aanwezig op %2$@";
+"%1$@ units remaining at %2$@" = "%1$@ eenheden aanwezig om %2$@";
 
 /* The format string for displaying an offset from a time zone: (1: GMT)(2: -)(3: 4:00) */
 "%1$@%2$@%3$@" = "%1$@%2$@%3$@";
@@ -72,7 +72,7 @@
 "Configuration" = "Configuratie";
 
 /* The title of the Insulin Type */
-"Insulin Type" = "Insulin Type";
+"Insulin Type" = "Insuline soort";
 
 /* The title of the continue action in an action sheet */
 "Continue" = "Vervolg";
@@ -94,7 +94,7 @@
 "Device Information" = "Apparaat informatie";
 
 /* Title text for button to disable bolus beeps */
-"Disable Bolus Beeps" = "Stop gebruik Bolus Piepjes";
+"Disable Bolus Beeps" = "Bolus piepjes uitschakelen";
 
 /* Title text for button to enable bolus beeps */
 "Enable Bolus Beeps" = "Pas Bolus Piepjes gebruik toe";
@@ -106,16 +106,16 @@
 "Error enabling bolus beeps" = "Fout in toepassen gebruik bolus piepjes";
 
 /* The alert title for enable Confirmation Beeps */
-"Enable Confirmation Beeps" = "Enable Confirmation Beeps";
+"Enable Confirmation Beeps" = "Schakel bevestigingspiepjes in";
 
 /* The alert title for Disable Confirmation Beep */
-"Disable Confirmation Beeps" = "Disable Confirmation Beeps";
+"Disable Confirmation Beeps" = "Schakel bevestigingspiepjes uit";
 
 /* The alert title for enable Automatic Bolus Beeps */
-"Enable Automatic Bolus Beeps" = "Enable Automatic Bolus Beeps";
+"Enable Automatic Bolus Beeps" = "Automatische boluspiepen inschakelen";
 
 /* The alert title for Disable Automatic Bolus Beeps */
-"Disable Automatic Bolus Beeps" = "Disable Automatic Bolus Beeps";
+"Disable Automatic Bolus Beeps" = "Automatische boluspiepen uitschakelen";
 
 /* The alert title for a resume error */
 "Error Resuming" = "Fout met hervatten";
@@ -249,13 +249,13 @@
 "Unknown" = "Onbekend";
 
 /* The title of the diagnostics section in settings */
-"Diagnostics" = "Diagnostics";
+"Diagnostics" = "Diagnostische gegevens";
 
 /* The diagnostic command Read Pod Status */
-"Read Pod Status" = "Read Pod Status";
+"Read Pod Status" = "Lees pompstatus";
 
 /* The diagnostic command Read Pulse Log */
-"Read Pulse Log" = "Read Pulse Log";
+"Read Pulse Log" = "Pulslog uitlezen";
 
 /* The title of the pod details section in settings */
 "Pod Details" = "Pod Details";

+ 8 - 8
Dependencies/rileylink_ios/RileyLinkKitUI/nl.lproj/Localizable.strings

@@ -26,7 +26,7 @@
 "Firmware" = "Firmware";
 
 /* The title of the cell showing current rileylink frequency */
-"Frequency" = "Frequency";
+"Frequency" = "Frequentie";
 
 /* The title of the cell showing device name */
 "Name" = "Naam";
@@ -38,28 +38,28 @@
 "Signal Strength" = "Signaalsterkte";
 
 /* The header of the cells showing connection monitoring */
-"Connection Monitoring" = "Connection Monitoring";
+"Connection Monitoring" = "Verbindingen monitoren";
 
 /* The title of the cell showing uptime */
-"Uptime" = "Uptime";
+"Uptime" = "Tijd actief";
 
 /* The title of the cell showing battery level */
-"Battery level" = "Battery level";
+"Battery level" = "Batterijniveau";
 
 /* The title of the cell showing Voltage */
 "Voltage" = "Voltage";
 
 /* "The title of the section for alerts" */
-"Alert" = "Alert";
+"Alert" = "Waarschuwing";
 
 /* The title of the cell showing Low Battery Alert */
-"Low Battery Alert" = "Low Battery Alert";
+"Low Battery Alert" = "'Lege batterij'-waarschuwing";
 
 /* Header of list showing battery level alert options */
-"Battery level Alert" = "Battery level Alert";
+"Battery level Alert" = "Batterijniveau waarschuwing";
 
 /* Battery level alert OFF in list of options */
-"OFF" = "OFF";
+"OFF" = "UIT";
 
 /* The title of the command to update diagnostic LEDs */
 "Diagnostic LEDs" = "Diagnostic LEDs";

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 117 - 359
FreeAPS.xcodeproj/project.pbxproj


+ 1 - 1
FreeAPS/Resources/Config.xcconfig

@@ -1 +1 @@
-BUILD_VERSION = 0.2.2
+BUILD_VERSION = 0.2.3

+ 2 - 2
FreeAPS/Sources/APS/Extensions/PumpManagerExtensions.swift

@@ -15,7 +15,7 @@ extension PumpManagerUI {
         setupViewController(
             insulinTintColor: .accentColor,
             guidanceColors: GuidanceColors(acceptable: .green, warning: .orange, critical: .red),
-            allowedInsulinTypes: [.apidra, .humalog, .novolog, .fiasp]
+            allowedInsulinTypes: [.apidra, .humalog, .novolog, .fiasp, .lyumjev]
         )
     }
 
@@ -23,7 +23,7 @@ extension PumpManagerUI {
         settingsViewController(
             insulinTintColor: .accentColor,
             guidanceColors: GuidanceColors(acceptable: .green, warning: .orange, critical: .red),
-            allowedInsulinTypes: [.apidra, .humalog, .novolog, .fiasp]
+            allowedInsulinTypes: [.apidra, .humalog, .novolog, .fiasp, .lyumjev]
         )
     }
 }

+ 26 - 38
FreeAPS/Sources/Application/FreeAPSApp.swift

@@ -1,33 +1,33 @@
 import SwiftUI
 import Swinject
 
-private let dependencies: [DependeciesContainer.Type] = [
-    StorageContainer.self,
-    ServiceContainer.self,
-    APSContainer.self,
-    UIContainer.self,
-    NetworkContainer.self,
-    SecurityContainer.self
-]
-
-private extension Swinject.Resolver {
-    func setup() {
-        for dep in dependencies {
-            dep.setup()
-        }
-    }
-}
-
 @main struct FreeAPSApp: App {
     @Environment(\.scenePhase) var scenePhase
 
-    static let resolver = Container(defaultObjectScope: .container) { container in
-        for dep in dependencies {
-            dep.register(container: container)
-        }
-    }.synchronize()
+    // Dependencies Assembler
+    // contain all dependencies Assemblies
+    // TODO: Remove static key after update "Use Dependencies" logic
+    private static var assembler = Assembler([
+        StorageAssembly(),
+        ServiceAssembly(),
+        APSAssembly(),
+        NetworkAssembly(),
+        UIAssembly(),
+        SecurityAssembly()
+    ], parent: nil, defaultObjectScope: .container)
 
-    private static func loadServices() {
+    var resolver: Resolver {
+        FreeAPSApp.assembler.resolver
+    }
+
+    // Temp static var
+    // Use to backward compatibility with old Dependencies logic on Logger
+    // TODO: Remove var after update "Use Dependencies" logic in Logger
+    static var resolver: Resolver {
+        FreeAPSApp.assembler.resolver
+    }
+
+    private func loadServices() {
         resolver.resolve(AppearanceManager.self)!.setupGlobalAppearance()
         _ = resolver.resolve(DeviceDataManager.self)!
         _ = resolver.resolve(APSManager.self)!
@@ -37,27 +37,15 @@ private extension Swinject.Resolver {
     }
 
     init() {
-        FreeAPSApp.resolver.setup()
-        FreeAPSApp.loadServices()
+        loadServices()
     }
 
-    private let mainView = Main.Builder(resolver: FreeAPSApp.resolver).buildView()
-
     var body: some Scene {
         WindowGroup {
-            mainView
+            Main.RootView(resolver: resolver)
         }
         .onChange(of: scenePhase) { newScenePhase in
-            switch newScenePhase {
-            case .active:
-                debug(.default, "APPLICATION is active")
-            case .inactive:
-                debug(.default, "APPLICATION is inactive")
-            case .background:
-                debug(.default, "APPLICATION is in background")
-            @unknown default:
-                debug(.default, "APPLICATION: Received an unexpected scenePhase.")
-            }
+            debug(.default, "APPLICATION PHASE: \(newScenePhase)")
         }
     }
 }

+ 12 - 0
FreeAPS/Sources/Assemblies/APSAssembly.swift

@@ -0,0 +1,12 @@
+import Foundation
+import Swinject
+
+final class APSAssembly: Assembly {
+    func assemble(container: Container) {
+        container.register(DeviceDataManager.self) { r in BaseDeviceDataManager(resolver: r) }
+        container.register(APSManager.self) { r in BaseAPSManager(resolver: r) }
+        container.register(FetchGlucoseManager.self) { r in BaseFetchGlucoseManager(resolver: r) }
+        container.register(FetchTreatmentsManager.self) { r in BaseFetchTreatmentsManager(resolver: r) }
+        container.register(FetchAnnouncementsManager.self) { r in BaseFetchAnnouncementsManager(resolver: r) }
+    }
+}

+ 13 - 0
FreeAPS/Sources/Assemblies/NetworkAssembly.swift

@@ -0,0 +1,13 @@
+import Alamofire
+import Foundation
+import Swinject
+
+final class NetworkAssembly: Assembly {
+    func assemble(container: Container) {
+        container.register(ReachabilityManager.self) { _ in
+            NetworkReachabilityManager()!
+        }
+
+        container.register(NightscoutManager.self) { r in BaseNightscoutManager(resolver: r) }
+    }
+}

+ 8 - 0
FreeAPS/Sources/Assemblies/SecurityAssembly.swift

@@ -0,0 +1,8 @@
+import Foundation
+import Swinject
+
+final class SecurityAssembly: Assembly {
+    func assemble(container: Container) {
+        container.register(UnlockManager.self) { _ in BaseUnlockManager() }
+    }
+}

+ 2 - 4
FreeAPS/Sources/Containers/ServiceContainer.swift

@@ -1,10 +1,8 @@
 import Foundation
 import Swinject
 
-private let resolver = FreeAPSApp.resolver
-
-enum ServiceContainer: DependeciesContainer {
-    static func register(container: Container) {
+final class ServiceAssembly: Assembly {
+    func assemble(container: Container) {
         container.register(NotificationCenter.self) { _ in Foundation.NotificationCenter.default }
         container.register(Broadcaster.self) { _ in BaseBroadcaster() }
         container.register(GroupedIssueReporter.self) { _ in

+ 18 - 0
FreeAPS/Sources/Assemblies/StorageAssembly.swift

@@ -0,0 +1,18 @@
+import Foundation
+import Swinject
+
+final class StorageAssembly: Assembly {
+    func assemble(container: Container) {
+        container.register(FileManager.self) { _ in
+            Foundation.FileManager.default
+        }
+        container.register(FileStorage.self) { _ in BaseFileStorage() }
+        container.register(PumpHistoryStorage.self) { r in BasePumpHistoryStorage(resolver: r) }
+        container.register(GlucoseStorage.self) { r in BaseGlucoseStorage(resolver: r) }
+        container.register(TempTargetsStorage.self) { r in BaseTempTargetsStorage(resolver: r) }
+        container.register(CarbsStorage.self) { r in BaseCarbsStorage(resolver: r) }
+        container.register(AnnouncementsStorage.self) { r in BaseAnnouncementsStorage(resolver: r) }
+        container.register(SettingsManager.self) { r in BaseSettingsManager(resolver: r) }
+        container.register(Keychain.self) { _ in BaseKeychain() }
+    }
+}

+ 9 - 0
FreeAPS/Sources/Assemblies/UIAssembly.swift

@@ -0,0 +1,9 @@
+import Foundation
+import Swinject
+
+final class UIAssembly: Assembly {
+    func assemble(container: Container) {
+        container.register(AppearanceManager.self) { _ in BaseAppearanceManager() }
+        container.register(Router.self) { r in BaseRouter(resolver: r) }
+    }
+}

+ 0 - 14
FreeAPS/Sources/Containers/APSContainer.swift

@@ -1,14 +0,0 @@
-import Foundation
-import Swinject
-
-private let resolver = FreeAPSApp.resolver
-
-enum APSContainer: DependeciesContainer {
-    static func register(container: Container) {
-        container.register(DeviceDataManager.self) { _ in BaseDeviceDataManager(resolver: resolver) }
-        container.register(APSManager.self) { _ in BaseAPSManager(resolver: resolver) }
-        container.register(FetchGlucoseManager.self) { _ in BaseFetchGlucoseManager(resolver: resolver) }
-        container.register(FetchTreatmentsManager.self) { _ in BaseFetchTreatmentsManager(resolver: resolver) }
-        container.register(FetchAnnouncementsManager.self) { _ in BaseFetchAnnouncementsManager(resolver: resolver) }
-    }
-}

+ 0 - 10
FreeAPS/Sources/Containers/DependeciesContainer.swift

@@ -1,10 +0,0 @@
-import Swinject
-
-protocol DependeciesContainer {
-    static func register(container: Container)
-    static func setup()
-}
-
-extension DependeciesContainer {
-    static func setup() {}
-}

+ 0 - 16
FreeAPS/Sources/Containers/NetworkContainer.swift

@@ -1,16 +0,0 @@
-import Alamofire
-import Swinject
-import UIKit
-
-private let resolver = FreeAPSApp.resolver
-
-enum NetworkContainer: DependeciesContainer {
-    static func register(container: Container) {
-        container.register(ReachabilityManager.self) { _ in
-            NetworkReachabilityManager()!
-        }.inObjectScope(.transient)
-
-        container.register(NightscoutManager.self) { _ in BaseNightscoutManager(resolver: resolver) }
-        container.register(AuthorizationManager.self) { _ in BaseAuthorizationManager(resolver: resolver) }
-    }
-}

+ 0 - 9
FreeAPS/Sources/Containers/SecurityContainer.swift

@@ -1,9 +0,0 @@
-import Swinject
-
-private let resolver = FreeAPSApp.resolver
-
-enum SecurityContainer: DependeciesContainer {
-    static func register(container: Container) {
-        container.register(UnlockManager.self) { _ in BaseUnlockManager() }
-    }
-}

+ 0 - 21
FreeAPS/Sources/Containers/StorageContainer.swift

@@ -1,21 +0,0 @@
-import Foundation
-import Swinject
-
-private let resolver = FreeAPSApp.resolver
-
-enum StorageContainer: DependeciesContainer {
-    static func register(container: Container) {
-        container.register(FileManager.self) { _ in
-            Foundation.FileManager.default
-        }
-        container.register(FileStorage.self) { _ in BaseFileStorage() }
-        container.register(PumpHistoryStorage.self) { _ in BasePumpHistoryStorage(resolver: resolver) }
-        container.register(GlucoseStorage.self) { _ in BaseGlucoseStorage(resolver: resolver) }
-        container.register(TempTargetsStorage.self) { _ in BaseTempTargetsStorage(resolver: resolver) }
-        container.register(CarbsStorage.self) { _ in BaseCarbsStorage(resolver: resolver) }
-        container.register(AnnouncementsStorage.self) { _ in BaseAnnouncementsStorage(resolver: resolver) }
-        container.register(SettingsManager.self) { _ in BaseSettingsManager(resolver: resolver) }
-
-        container.register(Keychain.self) { _ in BaseKeychain() }
-    }
-}

+ 0 - 10
FreeAPS/Sources/Containers/UIContainer.swift

@@ -1,10 +0,0 @@
-import Swinject
-
-private let resolver = FreeAPSApp.resolver
-
-enum UIContainer: DependeciesContainer {
-    static func register(container: Container) {
-        container.register(AppearanceManager.self) { _ in BaseAppearanceManager() }
-        container.register(Router.self) { _ in BaseRouter(resolver: resolver) }
-    }
-}

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
FreeAPS/Sources/Localizations/Main/pt.lproj/Localizable.strings


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 25 - 25
FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 3 - 3
FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 2 - 2
FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings


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

@@ -22,7 +22,7 @@ struct Preferences: JSON {
     var maxCOB: Decimal = 120
     var wideBGTargetRange: Bool = false
     var skipNeutralTemps: Bool = false
-    var unsuspendIfNoTemp: Bool = true
+    var unsuspendIfNoTemp: Bool = false
     var bolusSnoozeDIADivisor: Decimal = 2
     var min5mCarbimpact: Decimal = 8
     var autotuneISFAdjustmentFraction: Decimal = 1.0

+ 0 - 3
FreeAPS/Sources/Modules/AddCarbs/AddCarbsBuilder.swift

@@ -1,3 +0,0 @@
-extension AddCarbs {
-    final class Builder: BaseModuleBuilder<RootView, ViewModel<Provider>, Provider> {}
-}

+ 1 - 1
FreeAPS/Sources/Modules/AddCarbs/AddCarbsViewModel.swift

@@ -1,7 +1,7 @@
 import SwiftUI
 
 extension AddCarbs {
-    class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: AddCarbsProvider {
+    final class StateModel: BaseStateModel<Provider> {
         @Injected() var carbsStorage: CarbsStorage!
         @Injected() var settingsManager: SettingsManager!
         @Injected() var apsManager: APSManager!

+ 10 - 7
FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift

@@ -1,8 +1,10 @@
 import SwiftUI
+import Swinject
 
 extension AddCarbs {
     struct RootView: BaseView {
-        @EnvironmentObject var viewModel: ViewModel<Provider>
+        let resolver: Resolver
+        @StateObject var state = StateModel()
 
         private var formatter: NumberFormatter {
             let formatter = NumberFormatter()
@@ -13,7 +15,7 @@ extension AddCarbs {
 
         var body: some View {
             Form {
-                if let carbsReq = viewModel.carbsRequired {
+                if let carbsReq = state.carbsRequired {
                     Section {
                         HStack {
                             Text("Carbs required")
@@ -26,21 +28,22 @@ extension AddCarbs {
                     HStack {
                         Text("Amount")
                         Spacer()
-                        DecimalTextField("0", value: $viewModel.carbs, formatter: formatter, autofocus: true, cleanInput: true)
+                        DecimalTextField("0", value: $state.carbs, formatter: formatter, autofocus: true, cleanInput: true)
                         Text("grams").foregroundColor(.secondary)
                     }
-                    DatePicker("Date", selection: $viewModel.date)
+                    DatePicker("Date", selection: $state.date)
                 }
 
                 Section {
-                    Button { viewModel.add() }
+                    Button { state.add() }
                     label: { Text("Add") }
-                        .disabled(viewModel.carbs <= 0)
+                        .disabled(state.carbs <= 0)
                 }
             }
+            .onAppear(perform: configureView)
             .navigationTitle("Add Carbs")
             .navigationBarTitleDisplayMode(.automatic)
-            .navigationBarItems(leading: Button("Close", action: viewModel.hideModal))
+            .navigationBarItems(leading: Button("Close", action: state.hideModal))
         }
     }
 }

+ 0 - 3
FreeAPS/Sources/Modules/AddTempTarget/AddTempTargetBuilder.swift

@@ -1,3 +0,0 @@
-extension AddTempTarget {
-    final class Builder: BaseModuleBuilder<RootView, ViewModel<Provider>, Provider> {}
-}

+ 1 - 1
FreeAPS/Sources/Modules/AddTempTarget/AddTempTargetViewModel.swift

@@ -1,7 +1,7 @@
 import SwiftUI
 
 extension AddTempTarget {
-    class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: AddTempTargetProvider {
+    final class StateModel: BaseStateModel<Provider> {
         @Injected() private var storage: TempTargetsStorage!
         @Injected() private var settingsManager: SettingsManager!
         @Injected() var apsManager: APSManager!

+ 21 - 18
FreeAPS/Sources/Modules/AddTempTarget/View/AddTempTargetRootView.swift

@@ -1,8 +1,10 @@
 import SwiftUI
+import Swinject
 
 extension AddTempTarget {
     struct RootView: BaseView {
-        @EnvironmentObject var viewModel: ViewModel<Provider>
+        let resolver: Resolver
+        @StateObject var state = StateModel()
         @State private var isPromtPresented = false
         @State private var isRemoveAlertPresented = false
         @State private var removeAlert: Alert?
@@ -16,9 +18,9 @@ extension AddTempTarget {
 
         var body: some View {
             Form {
-                if !viewModel.presets.isEmpty {
+                if !state.presets.isEmpty {
                     Section(header: Text("Presets")) {
-                        ForEach(viewModel.presets) { preset in
+                        ForEach(state.presets) { preset in
                             presetView(for: preset)
                         }
                     }
@@ -28,39 +30,39 @@ extension AddTempTarget {
                     HStack {
                         Text("Bottom target")
                         Spacer()
-                        DecimalTextField("0", value: $viewModel.low, formatter: formatter, cleanInput: true)
-                        Text(viewModel.units.rawValue).foregroundColor(.secondary)
+                        DecimalTextField("0", value: $state.low, formatter: formatter, cleanInput: true)
+                        Text(state.units.rawValue).foregroundColor(.secondary)
                     }
                     HStack {
                         Text("Top target")
                         Spacer()
-                        DecimalTextField("0", value: $viewModel.high, formatter: formatter, cleanInput: true)
-                        Text(viewModel.units.rawValue).foregroundColor(.secondary)
+                        DecimalTextField("0", value: $state.high, formatter: formatter, cleanInput: true)
+                        Text(state.units.rawValue).foregroundColor(.secondary)
                     }
                     HStack {
                         Text("Duration")
                         Spacer()
-                        DecimalTextField("0", value: $viewModel.duration, formatter: formatter, cleanInput: true)
+                        DecimalTextField("0", value: $state.duration, formatter: formatter, cleanInput: true)
                         Text("minutes").foregroundColor(.secondary)
                     }
-                    DatePicker("Date", selection: $viewModel.date)
+                    DatePicker("Date", selection: $state.date)
                     Button { isPromtPresented = true }
                     label: { Text("Save as preset") }
                 }
 
                 Section {
-                    Button { viewModel.enact() }
+                    Button { state.enact() }
                     label: { Text("Enact") }
-                    Button { viewModel.cancel() }
+                    Button { state.cancel() }
                     label: { Text("Cancel Temp Target") }
                 }
             }
             .popover(isPresented: $isPromtPresented) {
                 Form {
                     Section(header: Text("Enter preset name")) {
-                        TextField("Name", text: $viewModel.newPresetName)
+                        TextField("Name", text: $state.newPresetName)
                         Button {
-                            viewModel.save()
+                            state.save()
                             isPromtPresented = false
                         }
                         label: { Text("Save") }
@@ -69,15 +71,16 @@ extension AddTempTarget {
                     }
                 }
             }
+            .onAppear(perform: configureView)
             .navigationTitle("Enact Temp Target")
             .navigationBarTitleDisplayMode(.automatic)
-            .navigationBarItems(leading: Button("Close", action: viewModel.hideModal))
+            .navigationBarItems(leading: Button("Close", action: state.hideModal))
         }
 
         private func presetView(for preset: TempTarget) -> some View {
             var low = preset.targetBottom
             var high = preset.targetTop
-            if viewModel.units == .mmolL {
+            if state.units == .mmolL {
                 low = low?.asMmolL
                 high = high?.asMmolL
             }
@@ -94,7 +97,7 @@ extension AddTempTarget {
                         .foregroundColor(.secondary)
                         .font(.caption)
 
-                        Text(viewModel.units.rawValue)
+                        Text(state.units.rawValue)
                             .foregroundColor(.secondary)
                             .font(.caption)
                         Text("for \(formatter.string(from: preset.duration as NSNumber)!) min")
@@ -105,7 +108,7 @@ extension AddTempTarget {
                 }
                 .contentShape(Rectangle())
                 .onTapGesture {
-                    viewModel.enactPreset(id: preset.id)
+                    state.enactPreset(id: preset.id)
                 }
 
                 Image(systemName: "xmark.circle").foregroundColor(.secondary)
@@ -115,7 +118,7 @@ extension AddTempTarget {
                         removeAlert = Alert(
                             title: Text("Are you sure?"),
                             message: Text("Delete preset \"\(preset.displayName)\""),
-                            primaryButton: .destructive(Text("Delete"), action: { viewModel.removePreset(id: preset.id) }),
+                            primaryButton: .destructive(Text("Delete"), action: { state.removePreset(id: preset.id) }),
                             secondaryButton: .cancel()
                         )
                         isRemoveAlertPresented = true

+ 0 - 3
FreeAPS/Sources/Modules/AuthorizedRoot/AuthorizedRootBuilder.swift

@@ -1,3 +0,0 @@
-extension AuthorizedRoot {
-    final class Builder: BaseModuleBuilder<RootView, ViewModel<Provider>, Provider> {}
-}

+ 0 - 7
FreeAPS/Sources/Modules/AuthorizedRoot/AuthorizedRootDataFlow.swift

@@ -1,7 +0,0 @@
-import SwiftUI
-
-enum AuthorizedRoot {
-    enum Config {}
-}
-
-protocol AuthorizedRootProvider: Provider {}

+ 0 - 3
FreeAPS/Sources/Modules/AuthorizedRoot/AuthorizedRootProvider.swift

@@ -1,3 +0,0 @@
-extension AuthorizedRoot {
-    final class Provider: BaseProvider, AuthorizedRootProvider {}
-}

+ 0 - 10
FreeAPS/Sources/Modules/AuthorizedRoot/AuthorizedRootViewModel.swift

@@ -1,10 +0,0 @@
-import SwiftUI
-import Swinject
-
-extension AuthorizedRoot {
-    class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: AuthorizedRootProvider {
-        override func subscribe() {}
-
-        lazy var rootView: some View = { router.view(for: .home) }()
-    }
-}

+ 0 - 13
FreeAPS/Sources/Modules/AuthorizedRoot/View/AuthotizedRootRootView.swift

@@ -1,13 +0,0 @@
-import SwiftUI
-
-extension AuthorizedRoot {
-    struct RootView: BaseView {
-        @EnvironmentObject var viewModel: ViewModel<Provider>
-
-        var body: some View {
-            NavigationView {
-                viewModel.rootView
-            }
-        }
-    }
-}

+ 0 - 3
FreeAPS/Sources/Modules/AutotuneConfig/AutotuneConfigBuilder.swift

@@ -1,3 +0,0 @@
-extension AutotuneConfig {
-    final class Builder: BaseModuleBuilder<RootView, ViewModel<Provider>, Provider> {}
-}

+ 11 - 5
FreeAPS/Sources/Modules/AutotuneConfig/AutotuneConfigViewModel.swift

@@ -2,7 +2,7 @@ import Combine
 import SwiftUI
 
 extension AutotuneConfig {
-    class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: AutotuneConfigProvider {
+    final class StateModel: BaseStateModel<Provider> {
         @Injected() var settingsManager: SettingsManager!
         @Injected() var apsManager: APSManager!
         @Published var useAutotune = false
@@ -25,7 +25,10 @@ extension AutotuneConfig {
 
             $useAutotune
                 .removeDuplicates()
-                .flatMap { use -> AnyPublisher<Bool, Never> in
+                .flatMap { [weak self] use -> AnyPublisher<Bool, Never> in
+                    guard let self = self else {
+                        return Just(false).eraseToAnyPublisher()
+                    }
                     self.settingsManager.settings.useAutotune = use
                     return self.apsManager.makeProfiles()
                 }
@@ -36,12 +39,15 @@ extension AutotuneConfig {
         func run() {
             provider.runAutotune()
                 .receive(on: DispatchQueue.main)
-                .flatMap { result -> AnyPublisher<Bool, Never> in
+                .flatMap { [weak self] result -> AnyPublisher<Bool, Never> in
+                    guard let self = self else {
+                        return Just(false).eraseToAnyPublisher()
+                    }
                     self.autotune = result
                     return self.apsManager.makeProfiles()
                 }
-                .sink { _ in
-                    self.lastAutotuneDate = Date()
+                .sink { [weak self] _ in
+                    self?.lastAutotuneDate = Date()
                 }.store(in: &lifetime)
         }
 

+ 11 - 8
FreeAPS/Sources/Modules/AutotuneConfig/View/AutotuneConfigRootView.swift

@@ -1,8 +1,10 @@
 import SwiftUI
+import Swinject
 
 extension AutotuneConfig {
     struct RootView: BaseView {
-        @EnvironmentObject var viewModel: ViewModel<Provider>
+        let resolver: Resolver
+        @StateObject var state = StateModel()
 
         private var isfFormatter: NumberFormatter {
             let formatter = NumberFormatter()
@@ -28,20 +30,20 @@ extension AutotuneConfig {
         var body: some View {
             Form {
                 Section {
-                    Toggle("Use Autotune", isOn: $viewModel.useAutotune)
+                    Toggle("Use Autotune", isOn: $state.useAutotune)
                 }
 
                 Section {
                     HStack {
                         Text("Last run")
                         Spacer()
-                        Text(dateFormatter.string(from: viewModel.publishedDate))
+                        Text(dateFormatter.string(from: state.publishedDate))
                     }
-                    Button { viewModel.run() }
+                    Button { state.run() }
                     label: { Text("Run now") }
                 }
 
-                if let autotune = viewModel.autotune {
+                if let autotune = state.autotune {
                     Section {
                         HStack {
                             Text("Carb ratio")
@@ -52,12 +54,12 @@ extension AutotuneConfig {
                         HStack {
                             Text("Sensitivity")
                             Spacer()
-                            if viewModel.units == .mmolL {
+                            if state.units == .mmolL {
                                 Text(isfFormatter.string(from: autotune.sensitivity.asMmolL as NSNumber) ?? "0")
                             } else {
                                 Text(isfFormatter.string(from: autotune.sensitivity as NSNumber) ?? "0")
                             }
-                            Text(viewModel.units.rawValue + "/U").foregroundColor(.secondary)
+                            Text(state.units.rawValue + "/U").foregroundColor(.secondary)
                         }
                     }
 
@@ -73,12 +75,13 @@ extension AutotuneConfig {
                     }
 
                     Section {
-                        Button { viewModel.delete() }
+                        Button { state.delete() }
                         label: { Text("Delete autotune data") }
                             .foregroundColor(.red)
                     }
                 }
             }
+            .onAppear(perform: configureView)
             .navigationTitle("Autotune")
             .navigationBarTitleDisplayMode(.automatic)
         }

+ 0 - 3
FreeAPS/Sources/Modules/BasalProfileEditor/BasalProfileEditorBuilder.swift

@@ -1,3 +0,0 @@
-extension BasalProfileEditor {
-    final class Builder: BaseModuleBuilder<RootView, ViewModel<Provider>, Provider> {}
-}

+ 1 - 1
FreeAPS/Sources/Modules/BasalProfileEditor/BasalProfileEditorViewModel.swift

@@ -1,7 +1,7 @@
 import SwiftUI
 
 extension BasalProfileEditor {
-    class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: BasalProfileEditorProvider {
+    final class StateModel: BaseStateModel<Provider> {
         @Published var syncInProgress = false
         @Published var items: [Item] = []
 

+ 22 - 19
FreeAPS/Sources/Modules/BasalProfileEditor/View/BasalProfileEditorRootView.swift

@@ -1,8 +1,10 @@
 import SwiftUI
+import Swinject
 
 extension BasalProfileEditor {
     struct RootView: BaseView {
-        @EnvironmentObject var viewModel: ViewModel<Provider>
+        let resolver: Resolver
+        @StateObject var state = StateModel()
         @State private var editMode = EditMode.inactive
 
         private var dateFormatter: DateFormatter {
@@ -26,17 +28,18 @@ extension BasalProfileEditor {
                 }
                 Section {
                     HStack {
-                        if viewModel.syncInProgress {
+                        if state.syncInProgress {
                             ProgressView().padding(.trailing, 10)
                         }
-                        Button { viewModel.save() }
+                        Button { state.save() }
                         label: {
-                            Text(viewModel.syncInProgress ? "Saving..." : "Save on Pump")
+                            Text(state.syncInProgress ? "Saving..." : "Save on Pump")
                         }
-                        .disabled(viewModel.syncInProgress || viewModel.items.isEmpty)
+                        .disabled(state.syncInProgress || state.items.isEmpty)
                     }
                 }
             }
+            .onAppear(perform: configureView)
             .navigationTitle("Basal Profile")
             .navigationBarTitleDisplayMode(.automatic)
             .navigationBarItems(
@@ -44,7 +47,7 @@ extension BasalProfileEditor {
             )
             .environment(\.editMode, $editMode)
             .onAppear {
-                viewModel.validate()
+                state.validate()
             }
         }
 
@@ -56,12 +59,12 @@ extension BasalProfileEditor {
                         Text("Time").frame(width: geometry.size.width / 2)
                     }
                     HStack(spacing: 0) {
-                        Picker(selection: $viewModel.items[index].rateIndex, label: EmptyView()) {
-                            ForEach(0 ..< viewModel.rateValues.count, id: \.self) { i in
+                        Picker(selection: $state.items[index].rateIndex, label: EmptyView()) {
+                            ForEach(0 ..< state.rateValues.count, id: \.self) { i in
                                 Text(
                                     (
                                         self.rateFormatter
-                                            .string(from: viewModel.rateValues[i] as NSNumber) ?? ""
+                                            .string(from: state.rateValues[i] as NSNumber) ?? ""
                                     ) + " U/hr"
                                 ).tag(i)
                             }
@@ -69,12 +72,12 @@ extension BasalProfileEditor {
                         .frame(maxWidth: geometry.size.width / 2)
                         .clipped()
 
-                        Picker(selection: $viewModel.items[index].timeIndex, label: EmptyView()) {
-                            ForEach(0 ..< viewModel.timeValues.count, id: \.self) { i in
+                        Picker(selection: $state.items[index].timeIndex, label: EmptyView()) {
+                            ForEach(0 ..< state.timeValues.count, id: \.self) { i in
                                 Text(
                                     self.dateFormatter
                                         .string(from: Date(
-                                            timeIntervalSince1970: viewModel
+                                            timeIntervalSince1970: state
                                                 .timeValues[i]
                                         ))
                                 ).tag(i)
@@ -89,17 +92,17 @@ extension BasalProfileEditor {
 
         private var list: some View {
             List {
-                ForEach(viewModel.items.indexed(), id: \.1.id) { index, item in
+                ForEach(state.items.indexed(), id: \.1.id) { index, item in
                     NavigationLink(destination: pickers(for: index)) {
                         HStack {
                             Text("Rate").foregroundColor(.secondary)
                             Text(
-                                "\(rateFormatter.string(from: viewModel.rateValues[item.rateIndex] as NSNumber) ?? "0") U/hr"
+                                "\(rateFormatter.string(from: state.rateValues[item.rateIndex] as NSNumber) ?? "0") U/hr"
                             )
                             Spacer()
                             Text("starts at").foregroundColor(.secondary)
                             Text(
-                                "\(dateFormatter.string(from: Date(timeIntervalSince1970: viewModel.timeValues[item.timeIndex])))"
+                                "\(dateFormatter.string(from: Date(timeIntervalSince1970: state.timeValues[item.timeIndex])))"
                             )
                         }
                     }
@@ -110,7 +113,7 @@ extension BasalProfileEditor {
         }
 
         private var addButton: some View {
-            guard viewModel.canAdd else {
+            guard state.canAdd else {
                 return AnyView(EmptyView())
             }
 
@@ -123,12 +126,12 @@ extension BasalProfileEditor {
         }
 
         func onAdd() {
-            viewModel.add()
+            state.add()
         }
 
         private func onDelete(offsets: IndexSet) {
-            viewModel.items.remove(atOffsets: offsets)
-            viewModel.validate()
+            state.items.remove(atOffsets: offsets)
+            state.validate()
         }
     }
 }

+ 0 - 26
FreeAPS/Sources/Modules/Base/BaseModuleBuilder.swift

@@ -1,26 +0,0 @@
-import SwiftUI
-import Swinject
-
-protocol ModuleBuilder {
-    associatedtype View: SwiftUI.View
-    func buildView() -> AnyView
-}
-
-class BaseModuleBuilder<View: BaseView, ViewModel: ObservableObject, Provider: FreeAPS.Provider>: ModuleBuilder
-    where ViewModel: BaseViewModel<Provider>, View.ViewModel == ViewModel
-{
-    let resolver: Resolver
-    lazy var viewModel: ViewModel = { buildViewModel() }()
-
-    init(resolver: Resolver) {
-        self.resolver = resolver
-    }
-
-    func buildViewModel() -> ViewModel {
-        ViewModel(provider: Provider(resolver: resolver), resolver: resolver)
-    }
-
-    func buildView() -> AnyView {
-        View().environmentObject(viewModel).asAny()
-    }
-}

+ 0 - 1
FreeAPS/Sources/Modules/Base/BaseProvider.swift

@@ -8,7 +8,6 @@ protocol Provider {
 
 class BaseProvider: Provider, Injectable {
     var lifetime = Lifetime()
-    @Injected() var authorizationManager: AuthorizationManager!
     @Injected() var deviceManager: DeviceDataManager!
     @Injected() var storage: FileStorage!
 

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

@@ -0,0 +1,43 @@
+import SwiftUI
+import Swinject
+
+protocol StateModel: ObservableObject {
+    var resolver: Resolver? { get set }
+    var isInitial: Bool { get set }
+    func subscribe()
+    func showModal(for screen: Screen?)
+    func hideModal()
+    func view(for screen: Screen) -> AnyView
+}
+
+class BaseStateModel<Provider>: StateModel, Injectable where Provider: FreeAPS.Provider {
+    @Injected() var router: Router!
+    var isInitial: Bool = true
+    private(set) var provider: Provider!
+
+    var resolver: Resolver? {
+        didSet {
+            if let resolver = resolver {
+                injectServices(resolver)
+                provider = Provider(resolver: resolver)
+                subscribe()
+            }
+        }
+    }
+
+    var lifetime = Lifetime()
+
+    func subscribe() {}
+
+    func showModal(for screen: Screen?) {
+        router.mainModalScreen.send(screen)
+    }
+
+    func hideModal() {
+        router.mainModalScreen.send(nil)
+    }
+
+    func view(for screen: Screen) -> AnyView {
+        router.view(for: screen)
+    }
+}

+ 22 - 4
FreeAPS/Sources/Modules/Base/BaseView.swift

@@ -1,11 +1,29 @@
 import SwiftUI
+import Swinject
 
 protocol BaseView: View {
-    associatedtype ViewModel: FreeAPS.ViewModel
-    var viewModel: ViewModel { get }
-    init()
+    associatedtype StateModelType: StateModel
+    var resolver: Resolver { get }
+    var state: StateModelType { get }
+    var router: Router { get }
+    func configureView()
+    func configureView(_ configure: (() -> Void)?)
 }
 
 extension BaseView {
-    init() { self.init() }
+    var router: Router { resolver.resolve(Router.self)! }
+}
+
+extension BaseView {
+    func configureView() {
+        configureView(nil)
+    }
+
+    func configureView(_ configure: (() -> Void)?) {
+        if state.isInitial {
+            configure?()
+            state.resolver = resolver
+            state.isInitial = false
+        }
+    }
 }

+ 0 - 56
FreeAPS/Sources/Modules/Base/BaseViewModel.swift

@@ -1,56 +0,0 @@
-import Combine
-import SwiftUI
-import Swinject
-
-protocol ViewModel {
-    func subscribe()
-    func view(for screen: Screen) -> AnyView
-    func cachedView(for screen: Screen) -> AnyView
-    func showModal(for screen: Screen?)
-    func hideModal()
-    func cleanViewCache()
-}
-
-class BaseViewModel<Provider>: ViewModel, Injectable where Provider: FreeAPS.Provider {
-    let resolver: Resolver
-    let provider: Provider
-    var lifetime = Lifetime()
-    @Injected() var router: Router!
-
-    private var viewCache: [Screen: AnyView] = [:]
-
-    required init(provider: Provider, resolver: Resolver) {
-        self.provider = provider
-        self.resolver = resolver
-        injectServices(resolver)
-        subscribe()
-    }
-
-    func subscribe() {}
-
-    func view(for screen: Screen) -> AnyView {
-        router.view(for: screen)
-    }
-
-    func cachedView(for screen: Screen) -> AnyView {
-        if let view = viewCache[screen] {
-            return view
-        }
-
-        let view = view(for: screen)
-        viewCache[screen] = view
-        return view
-    }
-
-    func cleanViewCache() {
-        viewCache.removeAll()
-    }
-
-    func showModal(for screen: Screen?) {
-        router.mainModalScreen.send(screen)
-    }
-
-    func hideModal() {
-        router.mainModalScreen.send(nil)
-    }
-}

+ 0 - 15
FreeAPS/Sources/Modules/Bolus/BolusBuilder.swift

@@ -1,15 +0,0 @@
-import Swinject
-
-extension Bolus {
-    final class Builder: BaseModuleBuilder<RootView, ViewModel<Provider>, Provider> {
-        private let waitForSuggestion: Bool
-        init(resolver: Resolver, waitForSuggestion: Bool) {
-            self.waitForSuggestion = waitForSuggestion
-            super.init(resolver: resolver)
-        }
-
-        override func buildViewModel() -> Bolus.ViewModel<Bolus.Provider> {
-            ViewModel(provider: Provider(resolver: resolver), resolver: resolver, waitForSuggestion: waitForSuggestion)
-        }
-    }
-}

+ 6 - 15
FreeAPS/Sources/Modules/Bolus/BolusViewModel.swift

@@ -2,7 +2,7 @@ import SwiftUI
 import Swinject
 
 extension Bolus {
-    class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: BolusProvider {
+    final class StateModel: BaseStateModel<Provider> {
         @Injected() var unlockmanager: UnlockManager!
         @Injected() var apsManager: APSManager!
         @Injected() var broadcaster: Broadcaster!
@@ -11,18 +11,8 @@ extension Bolus {
         @Published var amount: Decimal = 0
         @Published var inslinRecommended: Decimal = 0
         @Published var inslinRequired: Decimal = 0
-        @Published var waitForSuggestion: Bool
-        let waitForSuggestionInitial: Bool
-
-        init(provider: Provider, resolver: Resolver, waitForSuggestion: Bool) {
-            self.waitForSuggestion = waitForSuggestion
-            waitForSuggestionInitial = waitForSuggestion
-            super.init(provider: provider, resolver: resolver)
-        }
-
-        required init(provider _: Provider, resolver _: Resolver) {
-            error(.default, "init(provider:resolver:) has not been implemented")
-        }
+        @Published var waitForSuggestion: Bool = false
+        var waitForSuggestionInitial: Bool = false
 
         override func subscribe() {
             setupInsulinRequired()
@@ -51,7 +41,8 @@ extension Bolus {
             let maxAmount = Double(min(amount, provider.pumpSettings().maxBolus))
 
             unlockmanager.unlock()
-                .sink { _ in } receiveValue: {
+                .sink { _ in } receiveValue: { [weak self] _ in
+                    guard let self = self else { return }
                     self.apsManager.enactBolus(amount: maxAmount, isSMB: false)
                     self.showModal(for: nil)
                 }
@@ -92,7 +83,7 @@ extension Bolus {
     }
 }
 
-extension Bolus.ViewModel: SuggestionObserver {
+extension Bolus.StateModel: SuggestionObserver {
     func suggestionDidUpdate(_: Suggestion) {
         DispatchQueue.main.async {
             self.waitForSuggestion = false

+ 25 - 16
FreeAPS/Sources/Modules/Bolus/View/BolusRootView.swift

@@ -1,8 +1,11 @@
 import SwiftUI
+import Swinject
 
 extension Bolus {
     struct RootView: BaseView {
-        @EnvironmentObject var viewModel: ViewModel<Provider>
+        let resolver: Resolver
+        let waitForSuggestion: Bool
+        @StateObject var state = StateModel()
         @State private var isAddInsulinAlertPresented = false
 
         private var formatter: NumberFormatter {
@@ -15,7 +18,7 @@ extension Bolus {
         var body: some View {
             Form {
                 Section(header: Text("Recommendation")) {
-                    if viewModel.waitForSuggestion {
+                    if state.waitForSuggestion {
                         HStack {
                             Text("Wait please").foregroundColor(.secondary)
                             Spacer()
@@ -27,36 +30,36 @@ extension Bolus {
                             Spacer()
                             Text(
                                 formatter
-                                    .string(from: viewModel.inslinRequired as NSNumber)! +
+                                    .string(from: state.inslinRequired as NSNumber)! +
                                     NSLocalizedString(" U", comment: "Insulin unit")
                             ).foregroundColor(.secondary)
                         }.contentShape(Rectangle())
                             .onTapGesture {
-                                viewModel.amount = viewModel.inslinRecommended
+                                state.amount = state.inslinRecommended
                             }
                         HStack {
                             Text("Insulin recommended")
                             Spacer()
                             Text(
                                 formatter
-                                    .string(from: viewModel.inslinRecommended as NSNumber)! +
+                                    .string(from: state.inslinRecommended as NSNumber)! +
                                     NSLocalizedString(" U", comment: "Insulin unit")
                             ).foregroundColor(.secondary)
                         }.contentShape(Rectangle())
                             .onTapGesture {
-                                viewModel.amount = viewModel.inslinRecommended
+                                state.amount = state.inslinRecommended
                             }
                     }
                 }
 
-                if !viewModel.waitForSuggestion {
+                if !state.waitForSuggestion {
                     Section(header: Text("Bolus")) {
                         HStack {
                             Text("Amount")
                             Spacer()
                             DecimalTextField(
                                 "0",
-                                value: $viewModel.amount,
+                                value: $state.amount,
                                 formatter: formatter,
                                 autofocus: true,
                                 cleanInput: true
@@ -66,39 +69,45 @@ extension Bolus {
                     }
 
                     Section {
-                        Button { viewModel.add() }
+                        Button { state.add() }
                         label: { Text("Enact bolus") }
-                            .disabled(viewModel.amount <= 0)
+                            .disabled(state.amount <= 0)
                     }
 
                     Section {
-                        if viewModel.waitForSuggestionInitial {
-                            Button { viewModel.showModal(for: nil) }
+                        if waitForSuggestion {
+                            Button { state.showModal(for: nil) }
                             label: { Text("Continue without bolus") }
                         } else {
                             Button { isAddInsulinAlertPresented = true }
                             label: { Text("Add insulin without actually bolusing") }
-                                .disabled(viewModel.amount <= 0)
+                                .disabled(state.amount <= 0)
                         }
                     }
                 }
             }
             .alert(isPresented: $isAddInsulinAlertPresented) {
                 let amount = formatter
-                    .string(from: viewModel.amount as NSNumber)! + NSLocalizedString(" U", comment: "Insulin unit")
+                    .string(from: state.amount as NSNumber)! + NSLocalizedString(" U", comment: "Insulin unit")
                 return Alert(
                     title: Text("Are you sure?"),
                     message: Text("Add \(amount) without bolusing"),
                     primaryButton: .destructive(
                         Text("Add"),
-                        action: { viewModel.addWithoutBolus() }
+                        action: { state.addWithoutBolus() }
                     ),
                     secondaryButton: .cancel()
                 )
             }
+            .onAppear {
+                configureView {
+                    state.waitForSuggestionInitial = waitForSuggestion
+                    state.waitForSuggestion = waitForSuggestion
+                }
+            }
             .navigationTitle("Enact Bolus")
             .navigationBarTitleDisplayMode(.automatic)
-            .navigationBarItems(leading: Button("Close", action: viewModel.hideModal))
+            .navigationBarItems(leading: Button("Close", action: state.hideModal))
         }
     }
 }

+ 0 - 3
FreeAPS/Sources/Modules/CGM/CGMBuilder.swift

@@ -1,3 +0,0 @@
-extension CGM {
-    final class Builder: BaseModuleBuilder<RootView, ViewModel<Provider>, Provider> {}
-}

+ 1 - 1
FreeAPS/Sources/Modules/CGM/CGMViewModel.swift

@@ -1,7 +1,7 @@
 import SwiftUI
 
 extension CGM {
-    class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: CGMProvider {
+    final class StateModel: BaseStateModel<Provider> {
         @Injected() var settingsManager: SettingsManager!
 
         @Published var cgm: CGMType = .nightscout

+ 10 - 7
FreeAPS/Sources/Modules/CGM/View/CGMRootView.swift

@@ -1,37 +1,40 @@
 import SwiftUI
+import Swinject
 
 extension CGM {
     struct RootView: BaseView {
-        @EnvironmentObject var viewModel: ViewModel<Provider>
+        let resolver: Resolver
+        @StateObject var state = StateModel()
 
         var body: some View {
             Form {
                 Section {
-                    Picker("Type", selection: $viewModel.cgm) {
+                    Picker("Type", selection: $state.cgm) {
                         ForEach(CGMType.allCases) {
                             Text($0.displayName).tag($0)
                         }
                     }
                 }
-                if [.dexcomG5, .dexcomG6].contains(viewModel.cgm) {
+                if [.dexcomG5, .dexcomG6].contains(state.cgm) {
                     Section(header: Text("Transmitter ID")) {
-                        TextField("XXXXXX", text: $viewModel.transmitterID, onCommit: {
+                        TextField("XXXXXX", text: $state.transmitterID, onCommit: {
                             UIApplication.shared.endEditing()
-                            viewModel.onChangeID()
+                            state.onChangeID()
                         })
                             .disableAutocorrection(true)
                             .autocapitalization(.allCharacters)
                             .keyboardType(.asciiCapable)
                     }
                     .onDisappear {
-                        viewModel.onChangeID()
+                        state.onChangeID()
                     }
                 }
 
                 Section(header: Text("Other")) {
-                    Toggle("Upload glucose to Nightscout", isOn: $viewModel.uploadGlucose)
+                    Toggle("Upload glucose to Nightscout", isOn: $state.uploadGlucose)
                 }
             }
+            .onAppear(perform: configureView)
             .navigationTitle("CGM")
             .navigationBarTitleDisplayMode(.automatic)
         }

+ 0 - 3
FreeAPS/Sources/Modules/CREditor/CREditorBuilder.swift

@@ -1,3 +0,0 @@
-extension CREditor {
-    final class Builder: BaseModuleBuilder<RootView, ViewModel<Provider>, Provider> {}
-}

+ 1 - 1
FreeAPS/Sources/Modules/CREditor/CREditorViewModel.swift

@@ -1,7 +1,7 @@
 import SwiftUI
 
 extension CREditor {
-    class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: CREditorProvider {
+    final class StateModel: BaseStateModel<Provider> {
         @Published var items: [Item] = []
         @Published var autotune: Autotune?
 

+ 21 - 18
FreeAPS/Sources/Modules/CREditor/View/CREditorRootView.swift

@@ -1,8 +1,10 @@
 import SwiftUI
+import Swinject
 
 extension CREditor {
     struct RootView: BaseView {
-        @EnvironmentObject var viewModel: ViewModel<Provider>
+        let resolver: Resolver
+        @StateObject var state = StateModel()
         @State private var editMode = EditMode.inactive
 
         private var dateFormatter: DateFormatter {
@@ -20,7 +22,7 @@ extension CREditor {
 
         var body: some View {
             Form {
-                if let autotune = viewModel.autotune {
+                if let autotune = state.autotune {
                     Section(header: Text("Autotune")) {
                         HStack {
                             Text("Calculated Ratio")
@@ -35,13 +37,14 @@ extension CREditor {
                     addButton
                 }
                 Section {
-                    Button { viewModel.save() }
+                    Button { state.save() }
                     label: {
                         Text("Save")
                     }
-                    .disabled(viewModel.items.isEmpty)
+                    .disabled(state.items.isEmpty)
                 }
             }
+            .onAppear(perform: configureView)
             .navigationTitle("Carb Ratios")
             .navigationBarTitleDisplayMode(.automatic)
             .navigationBarItems(
@@ -49,7 +52,7 @@ extension CREditor {
             )
             .environment(\.editMode, $editMode)
             .onAppear {
-                viewModel.validate()
+                state.validate()
             }
         }
 
@@ -61,12 +64,12 @@ extension CREditor {
                         Text("Time").frame(width: geometry.size.width / 2)
                     }
                     HStack(spacing: 0) {
-                        Picker(selection: $viewModel.items[index].rateIndex, label: EmptyView()) {
-                            ForEach(0 ..< viewModel.rateValues.count, id: \.self) { i in
+                        Picker(selection: $state.items[index].rateIndex, label: EmptyView()) {
+                            ForEach(0 ..< state.rateValues.count, id: \.self) { i in
                                 Text(
                                     (
                                         self.rateFormatter
-                                            .string(from: viewModel.rateValues[i] as NSNumber) ?? ""
+                                            .string(from: state.rateValues[i] as NSNumber) ?? ""
                                     ) + " g/U"
                                 ).tag(i)
                             }
@@ -74,12 +77,12 @@ extension CREditor {
                         .frame(maxWidth: geometry.size.width / 2)
                         .clipped()
 
-                        Picker(selection: $viewModel.items[index].timeIndex, label: EmptyView()) {
-                            ForEach(0 ..< viewModel.timeValues.count, id: \.self) { i in
+                        Picker(selection: $state.items[index].timeIndex, label: EmptyView()) {
+                            ForEach(0 ..< state.timeValues.count, id: \.self) { i in
                                 Text(
                                     self.dateFormatter
                                         .string(from: Date(
-                                            timeIntervalSince1970: viewModel
+                                            timeIntervalSince1970: state
                                                 .timeValues[i]
                                         ))
                                 ).tag(i)
@@ -94,17 +97,17 @@ extension CREditor {
 
         private var list: some View {
             List {
-                ForEach(viewModel.items.indexed(), id: \.1.id) { index, item in
+                ForEach(state.items.indexed(), id: \.1.id) { index, item in
                     NavigationLink(destination: pickers(for: index)) {
                         HStack {
                             Text("Ratio").foregroundColor(.secondary)
                             Text(
-                                "\(rateFormatter.string(from: viewModel.rateValues[item.rateIndex] as NSNumber) ?? "0") g/U"
+                                "\(rateFormatter.string(from: state.rateValues[item.rateIndex] as NSNumber) ?? "0") g/U"
                             )
                             Spacer()
                             Text("starts at").foregroundColor(.secondary)
                             Text(
-                                "\(dateFormatter.string(from: Date(timeIntervalSince1970: viewModel.timeValues[item.timeIndex])))"
+                                "\(dateFormatter.string(from: Date(timeIntervalSince1970: state.timeValues[item.timeIndex])))"
                             )
                         }
                     }
@@ -115,7 +118,7 @@ extension CREditor {
         }
 
         private var addButton: some View {
-            guard viewModel.canAdd else {
+            guard state.canAdd else {
                 return AnyView(EmptyView())
             }
 
@@ -128,12 +131,12 @@ extension CREditor {
         }
 
         func onAdd() {
-            viewModel.add()
+            state.add()
         }
 
         private func onDelete(offsets: IndexSet) {
-            viewModel.items.remove(atOffsets: offsets)
-            viewModel.validate()
+            state.items.remove(atOffsets: offsets)
+            state.validate()
         }
     }
 }

+ 0 - 16
FreeAPS/Sources/Modules/ConfigEditor/ConfigEditorBuilder.swift

@@ -1,16 +0,0 @@
-import Swinject
-
-extension ConfigEditor {
-    final class Builder: BaseModuleBuilder<RootView, ViewModel<Provider>, Provider> {
-        private let file: String
-
-        init(resolver: Resolver, file: String) {
-            self.file = file
-            super.init(resolver: resolver)
-        }
-
-        override func buildViewModel() -> ConfigEditor.ViewModel<ConfigEditor.Provider> {
-            ViewModel(provider: Provider(resolver: resolver), resolver: resolver, file: file)
-        }
-    }
-}

+ 17 - 0
FreeAPS/Sources/Modules/ConfigEditor/ConfigEditorStateModel.swift

@@ -0,0 +1,17 @@
+import SwiftUI
+import Swinject
+
+extension ConfigEditor {
+    final class StateModel: BaseStateModel<Provider> {
+        var file: String = ""
+        @Published var configText = ""
+
+        override func subscribe() {
+            configText = provider.load(file: file)
+        }
+
+        func save() {
+            provider.save(configText, as: file)
+        }
+    }
+}

+ 0 - 26
FreeAPS/Sources/Modules/ConfigEditor/ConfigEditorViewModel.swift

@@ -1,26 +0,0 @@
-import SwiftUI
-import Swinject
-
-extension ConfigEditor {
-    class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: ConfigEditorProvider {
-        let file: String
-        @Published var configText = ""
-
-        init(provider: Provider, resolver: Resolver, file: String) {
-            self.file = file
-            super.init(provider: provider, resolver: resolver)
-        }
-
-        required init(provider _: Provider, resolver _: Resolver) {
-            error(.default, "init(provider:resolver:) has not been implemented")
-        }
-
-        override func subscribe() {
-            configText = provider.load(file: file)
-        }
-
-        func save() {
-            provider.save(configText, as: file)
-        }
-    }
-}

+ 13 - 5
FreeAPS/Sources/Modules/ConfigEditor/View/ConfigEditorRootView.swift

@@ -1,12 +1,15 @@
 import SwiftUI
+import Swinject
 
 extension ConfigEditor {
     struct RootView: BaseView {
-        @EnvironmentObject var viewModel: ViewModel<Provider>
+        let resolver: Resolver
+        let file: String
+        @StateObject var state = StateModel()
         @State private var showShareSheet = false
 
         var body: some View {
-            TextEditor(text: $viewModel.configText)
+            TextEditor(text: $state.configText)
                 .keyboardType(.asciiCapable)
                 .font(.system(.subheadline, design: .monospaced))
                 .allowsTightening(true)
@@ -22,12 +25,17 @@ extension ConfigEditor {
                     }
                 }
                 .navigationBarItems(
-                    trailing: Button("Save", action: viewModel.save)
+                    trailing: Button("Save", action: state.save)
                 )
                 .sheet(isPresented: $showShareSheet) {
-                    ShareSheet(activityItems: [viewModel.provider.urlFor(file: viewModel.file)!])
+                    ShareSheet(activityItems: [state.provider.urlFor(file: state.file)!])
                 }
-                .navigationTitle(viewModel.file)
+                .onAppear {
+                    configureView {
+                        state.file = file
+                    }
+                }
+                .navigationTitle(file)
                 .navigationBarTitleDisplayMode(.inline)
                 .padding()
         }

+ 0 - 3
FreeAPS/Sources/Modules/DataTable/DataTableBuilder.swift

@@ -1,3 +0,0 @@
-extension DataTable {
-    final class Builder: BaseModuleBuilder<RootView, ViewModel<Provider>, Provider> {}
-}

+ 2 - 2
FreeAPS/Sources/Modules/DataTable/DataTableViewModel.swift

@@ -1,7 +1,7 @@
 import SwiftUI
 
 extension DataTable {
-    class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: DataTableProvider {
+    final class StateModel: BaseStateModel<Provider> {
         @Injected() var broadcaster: Broadcaster!
         @Injected() var settingsManager: SettingsManager!
         @Published var items: [Item] = []
@@ -83,7 +83,7 @@ extension DataTable {
     }
 }
 
-extension DataTable.ViewModel:
+extension DataTable.StateModel:
     SettingsObserver,
     PumpHistoryObserver,
     TempTargetsObserver,

+ 7 - 4
FreeAPS/Sources/Modules/DataTable/View/DataTableRootView.swift

@@ -1,8 +1,10 @@
 import SwiftUI
+import Swinject
 
 extension DataTable {
     struct RootView: BaseView {
-        @EnvironmentObject var viewModel: ViewModel<Provider>
+        let resolver: Resolver
+        @StateObject var state = StateModel()
         @State private var isRemoveCarbsAlertPresented = false
         @State private var removeCarbsAlert: Alert?
 
@@ -16,16 +18,17 @@ extension DataTable {
             Form {
                 list
             }
+            .onAppear(perform: configureView)
             .navigationTitle("History")
             .navigationBarTitleDisplayMode(.automatic)
             .navigationBarItems(
-                leading: Button("Close", action: viewModel.hideModal)
+                leading: Button("Close", action: state.hideModal)
             )
         }
 
         private var list: some View {
             List {
-                ForEach(viewModel.items.indexed(), id: \.1.id) { _, item in
+                ForEach(state.items.indexed(), id: \.1.id) { _, item in
                     HStack {
                         Image(systemName: "circle.fill").foregroundColor(item.color)
                         Text(dateFormatter.string(from: item.date))
@@ -47,7 +50,7 @@ extension DataTable {
                                         message: Text(item.amountText),
                                         primaryButton: .destructive(
                                             Text("Delete"),
-                                            action: { viewModel.deleteCarbs(at: item.date) }
+                                            action: { state.deleteCarbs(at: item.date) }
                                         ),
                                         secondaryButton: .cancel()
                                     )

+ 0 - 3
FreeAPS/Sources/Modules/Home/HomeBuilder.swift

@@ -1,3 +0,0 @@
-extension Home {
-    final class Builder: BaseModuleBuilder<RootView, ViewModel<Provider>, Provider> {}
-}

+ 3 - 3
FreeAPS/Sources/Modules/Home/HomeViewModel.swift

@@ -3,7 +3,7 @@ import SwiftDate
 import SwiftUI
 
 extension Home {
-    class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: HomeProvider {
+    final class StateModel: BaseStateModel<Provider> {
         @Injected() var broadcaster: Broadcaster!
         @Injected() var settingsManager: SettingsManager!
         @Injected() var apsManager: APSManager!
@@ -281,7 +281,7 @@ extension Home {
     }
 }
 
-extension Home.ViewModel:
+extension Home.StateModel:
     GlucoseObserver,
     SuggestionObserver,
     SettingsObserver,
@@ -346,7 +346,7 @@ extension Home.ViewModel:
     }
 }
 
-extension Home.ViewModel: CompletionDelegate {
+extension Home.StateModel: CompletionDelegate {
     func completionNotifyingDidComplete(_: CompletionNotifying) {
         setupPump = false
     }

+ 60 - 56
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -1,9 +1,12 @@
 import SwiftDate
 import SwiftUI
+import Swinject
 
 extension Home {
     struct RootView: BaseView {
-        @EnvironmentObject var viewModel: ViewModel<Provider>
+        let resolver: Resolver
+
+        @StateObject var state = StateModel()
         @State var isStatusPopupPresented = false
 
         private var numberFormatter: NumberFormatter {
@@ -50,41 +53,41 @@ extension Home {
                 Spacer()
 
                 CurrentGlucoseView(
-                    recentGlucose: $viewModel.recentGlucose,
-                    delta: $viewModel.glucoseDelta,
-                    units: $viewModel.units
+                    recentGlucose: $state.recentGlucose,
+                    delta: $state.glucoseDelta,
+                    units: $state.units
                 )
                 .onTapGesture {
-                    viewModel.openCGM()
+                    state.openCGM()
                 }
                 Spacer()
                 PumpView(
-                    reservoir: $viewModel.reservoir,
-                    battery: $viewModel.battery,
-                    name: $viewModel.pumpName,
-                    expiresAtDate: $viewModel.pumpExpiresAtDate,
-                    timerDate: $viewModel.timerDate
+                    reservoir: $state.reservoir,
+                    battery: $state.battery,
+                    name: $state.pumpName,
+                    expiresAtDate: $state.pumpExpiresAtDate,
+                    timerDate: $state.timerDate
                 )
                 .onTapGesture {
-                    viewModel.setupPump = true
+                    state.setupPump = true
                 }
-                .popover(isPresented: $viewModel.setupPump) {
-                    if let pumpManager = viewModel.provider.apsManager.pumpManager {
-                        PumpConfig.PumpSettingsView(pumpManager: pumpManager, completionDelegate: viewModel)
+                .popover(isPresented: $state.setupPump) {
+                    if let pumpManager = state.provider.apsManager.pumpManager {
+                        PumpConfig.PumpSettingsView(pumpManager: pumpManager, completionDelegate: state)
                     }
                 }
                 Spacer()
                 LoopView(
-                    suggestion: $viewModel.suggestion,
-                    enactedSuggestion: $viewModel.enactedSuggestion,
-                    closedLoop: $viewModel.closedLoop,
-                    timerDate: $viewModel.timerDate,
-                    isLooping: $viewModel.isLooping,
-                    lastLoopDate: $viewModel.lastLoopDate
+                    suggestion: $state.suggestion,
+                    enactedSuggestion: $state.enactedSuggestion,
+                    closedLoop: $state.closedLoop,
+                    timerDate: $state.timerDate,
+                    isLooping: $state.isLooping,
+                    lastLoopDate: $state.lastLoopDate
                 ).onTapGesture {
                     isStatusPopupPresented = true
                 }.onLongPressGesture {
-                    viewModel.runLoop()
+                    state.runLoop()
                 }
                 Spacer()
             }.frame(maxWidth: .infinity)
@@ -92,11 +95,11 @@ extension Home {
 
         var infoPanal: some View {
             HStack(alignment: .center) {
-                if viewModel.pumpSuspended {
+                if state.pumpSuspended {
                     Text("Pump suspended")
                         .font(.system(size: 12, weight: .bold)).foregroundColor(.loopGray)
                         .padding(.leading, 8)
-                } else if let tempRate = viewModel.tempRate {
+                } else if let tempRate = state.tempRate {
                     Text(
                         (numberFormatter.string(from: tempRate as NSNumber) ?? "0") +
                             NSLocalizedString(" U/hr", comment: "Unit per hour with space")
@@ -105,9 +108,9 @@ extension Home {
                     .padding(.leading, 8)
                 }
 
-                if let tempTarget = viewModel.tempTarget {
+                if let tempTarget = state.tempTarget {
                     Text(tempTarget.displayName).font(.caption).foregroundColor(.secondary)
-                    if viewModel.units == .mmolL {
+                    if state.units == .mmolL {
                         Text(
                             targetFormatter
                                 .string(from: (tempTarget.targetBottom?.asMmolL ?? 0) as NSNumber)!
@@ -120,12 +123,12 @@ extension Home {
                             Text(
                                 targetFormatter
                                     .string(from: (tempTarget.targetTop?.asMmolL ?? 0) as NSNumber)! +
-                                    " \(viewModel.units.rawValue)"
+                                    " \(state.units.rawValue)"
                             )
                             .font(.caption)
                             .foregroundColor(.secondary)
                         } else {
-                            Text(viewModel.units.rawValue).font(.caption)
+                            Text(state.units.rawValue).font(.caption)
                                 .foregroundColor(.secondary)
                         }
 
@@ -138,25 +141,25 @@ extension Home {
                                 .foregroundColor(.secondary)
                             Text(
                                 targetFormatter
-                                    .string(from: (tempTarget.targetTop ?? 0) as NSNumber)! + " \(viewModel.units.rawValue)"
+                                    .string(from: (tempTarget.targetTop ?? 0) as NSNumber)! + " \(state.units.rawValue)"
                             )
                             .font(.caption)
                             .foregroundColor(.secondary)
                         } else {
-                            Text(viewModel.units.rawValue).font(.caption)
+                            Text(state.units.rawValue).font(.caption)
                                 .foregroundColor(.secondary)
                         }
                     }
                 }
                 Spacer()
-                if let progress = viewModel.bolusProgress {
+                if let progress = state.bolusProgress {
                     Text("Bolusing")
                         .font(.system(size: 12, weight: .bold)).foregroundColor(.insulin)
                     ProgressView(value: Double(progress))
                         .progressViewStyle(BolusProgressViewStyle())
                         .padding(.trailing, 8)
                         .onTapGesture {
-                            viewModel.cancelBolus()
+                            state.cancelBolus()
                         }
                 }
             }
@@ -195,10 +198,10 @@ extension Home {
                         .font(.system(size: 12, weight: .bold)).foregroundColor(.uam)
                 }
 
-                if let eventualBG = viewModel.eventualBG {
+                if let eventualBG = state.eventualBG {
                     Text(
                         "⇢ " + numberFormatter.string(
-                            from: (viewModel.units == .mmolL ? eventualBG.asMmolL : Decimal(eventualBG)) as NSNumber
+                            from: (state.units == .mmolL ? eventualBG.asMmolL : Decimal(eventualBG)) as NSNumber
                         )!
                     )
                     .font(.system(size: 12, weight: .bold)).foregroundColor(.secondary)
@@ -219,19 +222,19 @@ extension Home {
                     infoPanal
                         .background(Color.backgroundColor)
                     MainChartView(
-                        glucose: $viewModel.glucose,
-                        suggestion: $viewModel.suggestion,
-                        tempBasals: $viewModel.tempBasals,
-                        boluses: $viewModel.boluses,
-                        suspensions: $viewModel.suspensions,
-                        hours: .constant(viewModel.filteredHours),
-                        maxBasal: $viewModel.maxBasal,
-                        autotunedBasalProfile: $viewModel.autotunedBasalProfile,
-                        basalProfile: $viewModel.basalProfile,
-                        tempTargets: $viewModel.tempTargets,
-                        carbs: $viewModel.carbs,
-                        timerDate: $viewModel.timerDate,
-                        units: $viewModel.units
+                        glucose: $state.glucose,
+                        suggestion: $state.suggestion,
+                        tempBasals: $state.tempBasals,
+                        boluses: $state.boluses,
+                        suspensions: $state.suspensions,
+                        hours: .constant(state.filteredHours),
+                        maxBasal: $state.maxBasal,
+                        autotunedBasalProfile: $state.autotunedBasalProfile,
+                        basalProfile: $state.basalProfile,
+                        tempTargets: $state.tempTargets,
+                        carbs: $state.carbs,
+                        timerDate: $state.timerDate,
+                        units: $state.units
                     )
                     .background(Color.gray.opacity(0.05))
                     // .padding(.bottom)
@@ -243,7 +246,7 @@ extension Home {
                     ZStack {
                         Rectangle().fill(Color.gray.opacity(0.05)).frame(height: 50 + geo.safeAreaInsets.bottom)
                         HStack {
-                            Button { viewModel.showModal(for: .addCarbs) }
+                            Button { state.showModal(for: .addCarbs) }
                             label: {
                                 ZStack(alignment: Alignment(horizontal: .trailing, vertical: .bottom)) {
                                     Image("carbs")
@@ -252,7 +255,7 @@ extension Home {
                                         .frame(width: 24, height: 24)
                                         .foregroundColor(.loopGreen)
                                         .padding(8)
-                                    if let carbsReq = viewModel.carbsRequired {
+                                    if let carbsReq = state.carbsRequired {
                                         Text(numberFormatter.string(from: carbsReq as NSNumber)!)
                                             .font(.caption)
                                             .foregroundColor(.white)
@@ -262,7 +265,7 @@ extension Home {
                                 }
                             }
                             Spacer()
-                            Button { viewModel.showModal(for: .addTempTarget) }
+                            Button { state.showModal(for: .addTempTarget) }
                             label: {
                                 Image("target")
                                     .renderingMode(.template)
@@ -271,7 +274,7 @@ extension Home {
                                     .padding(8)
                             }.foregroundColor(.loopYellow)
                             Spacer()
-                            Button { viewModel.showModal(for: .bolus(waitForDuggestion: false)) }
+                            Button { state.showModal(for: .bolus(waitForDuggestion: false)) }
                             label: {
                                 Image("bolus")
                                     .renderingMode(.template)
@@ -280,8 +283,8 @@ extension Home {
                                     .padding(8)
                             }.foregroundColor(.insulin)
                             Spacer()
-                            if viewModel.allowManualTemp {
-                                Button { viewModel.showModal(for: .manualTempBasal) }
+                            if state.allowManualTemp {
+                                Button { state.showModal(for: .manualTempBasal) }
                                 label: {
                                     Image("bolus1")
                                         .renderingMode(.template)
@@ -291,7 +294,7 @@ extension Home {
                                 }.foregroundColor(.insulin)
                                 Spacer()
                             }
-                            Button { viewModel.showModal(for: .settings) }
+                            Button { state.showModal(for: .settings) }
                             label: {
                                 Image("settings1")
                                     .renderingMode(.template)
@@ -306,16 +309,17 @@ extension Home {
                 }
                 .edgesIgnoringSafeArea(.vertical)
             }
+            .onAppear(perform: configureView)
             .navigationTitle("Home")
             .navigationBarHidden(true)
             .ignoresSafeArea(.keyboard)
             .popup(isPresented: isStatusPopupPresented, alignment: .top, direction: .top) {
                 VStack(alignment: .leading) {
-                    Text(viewModel.statusTitle).foregroundColor(.white)
+                    Text(state.statusTitle).foregroundColor(.white)
                         .padding(.bottom, 4)
-                    Text(viewModel.suggestion?.reason ?? "No sugestion found").font(.caption).foregroundColor(.white)
+                    Text(state.suggestion?.reason ?? "No sugestion found").font(.caption).foregroundColor(.white)
 
-                    if let errorMessage = viewModel.errorMessage, let date = viewModel.errorDate {
+                    if let errorMessage = state.errorMessage, let date = state.errorDate {
                         Text("Error at \(dateFormatter.string(from: date))").foregroundColor(.white)
                             .padding(.bottom, 4)
                             .padding(.top, 8)

+ 0 - 3
FreeAPS/Sources/Modules/ISFEditor/ISFEditorBuilder.swift

@@ -1,3 +0,0 @@
-extension ISFEditor {
-    final class Builder: BaseModuleBuilder<RootView, ViewModel<Provider>, Provider> {}
-}

+ 1 - 1
FreeAPS/Sources/Modules/ISFEditor/ISFEditorViewModel.swift

@@ -1,7 +1,7 @@
 import SwiftUI
 
 extension ISFEditor {
-    class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: ISFEditorProvider {
+    final class StateModel: BaseStateModel<Provider> {
         @Injected() var settingsManager: SettingsManager!
         @Published var items: [Item] = []
         private(set) var autosensISF: Decimal?

+ 27 - 24
FreeAPS/Sources/Modules/ISFEditor/View/ISFEditorRootView.swift

@@ -1,8 +1,10 @@
 import SwiftUI
+import Swinject
 
 extension ISFEditor {
     struct RootView: BaseView {
-        @EnvironmentObject var viewModel: ViewModel<Provider>
+        let resolver: Resolver
+        @StateObject var state = StateModel()
         @State private var editMode = EditMode.inactive
 
         private var dateFormatter: DateFormatter {
@@ -21,32 +23,32 @@ extension ISFEditor {
 
         var body: some View {
             Form {
-                if let autotune = viewModel.autotune {
+                if let autotune = state.autotune {
                     Section(header: Text("Autotune")) {
                         HStack {
                             Text("Calculated Sensitivity")
                             Spacer()
-                            if viewModel.units == .mmolL {
+                            if state.units == .mmolL {
                                 Text(rateFormatter.string(from: autotune.sensitivity.asMmolL as NSNumber) ?? "0")
                             } else {
                                 Text(rateFormatter.string(from: autotune.sensitivity as NSNumber) ?? "0")
                             }
-                            Text(viewModel.units.rawValue + "/U").foregroundColor(.secondary)
+                            Text(state.units.rawValue + "/U").foregroundColor(.secondary)
                         }
                     }
                 }
-                if let newISF = viewModel.autosensISF {
+                if let newISF = state.autosensISF {
                     Section(header: Text("Autosens")) {
                         HStack {
                             Text("Sensitivity Ratio")
                             Spacer()
-                            Text(rateFormatter.string(from: viewModel.autosensRatio as NSNumber) ?? "1")
+                            Text(rateFormatter.string(from: state.autosensRatio as NSNumber) ?? "1")
                         }
                         HStack {
                             Text("Calculated Sensitivity")
                             Spacer()
                             Text(rateFormatter.string(from: newISF as NSNumber) ?? "0")
-                            Text(viewModel.units.rawValue + "/U").foregroundColor(.secondary)
+                            Text(state.units.rawValue + "/U").foregroundColor(.secondary)
                         }
                     }
                 }
@@ -55,13 +57,14 @@ extension ISFEditor {
                     addButton
                 }
                 Section {
-                    Button { viewModel.save() }
+                    Button { state.save() }
                     label: {
                         Text("Save")
                     }
-                    .disabled(viewModel.items.isEmpty)
+                    .disabled(state.items.isEmpty)
                 }
             }
+            .onAppear(perform: configureView)
             .navigationTitle("Insulin Sensitivities")
             .navigationBarTitleDisplayMode(.automatic)
             .navigationBarItems(
@@ -69,7 +72,7 @@ extension ISFEditor {
             )
             .environment(\.editMode, $editMode)
             .onAppear {
-                viewModel.validate()
+                state.validate()
             }
         }
 
@@ -81,25 +84,25 @@ extension ISFEditor {
                         Text("Time").frame(width: geometry.size.width / 2)
                     }
                     HStack(spacing: 0) {
-                        Picker(selection: $viewModel.items[index].rateIndex, label: EmptyView()) {
-                            ForEach(0 ..< viewModel.rateValues.count, id: \.self) { i in
+                        Picker(selection: $state.items[index].rateIndex, label: EmptyView()) {
+                            ForEach(0 ..< state.rateValues.count, id: \.self) { i in
                                 Text(
                                     (
                                         self.rateFormatter
-                                            .string(from: viewModel.rateValues[i] as NSNumber) ?? ""
-                                    ) + " \(viewModel.units.rawValue)/U"
+                                            .string(from: state.rateValues[i] as NSNumber) ?? ""
+                                    ) + " \(state.units.rawValue)/U"
                                 ).tag(i)
                             }
                         }
                         .frame(maxWidth: geometry.size.width / 2)
                         .clipped()
 
-                        Picker(selection: $viewModel.items[index].timeIndex, label: EmptyView()) {
-                            ForEach(0 ..< viewModel.timeValues.count, id: \.self) { i in
+                        Picker(selection: $state.items[index].timeIndex, label: EmptyView()) {
+                            ForEach(0 ..< state.timeValues.count, id: \.self) { i in
                                 Text(
                                     self.dateFormatter
                                         .string(from: Date(
-                                            timeIntervalSince1970: viewModel
+                                            timeIntervalSince1970: state
                                                 .timeValues[i]
                                         ))
                                 ).tag(i)
@@ -114,17 +117,17 @@ extension ISFEditor {
 
         private var list: some View {
             List {
-                ForEach(viewModel.items.indexed(), id: \.1.id) { index, item in
+                ForEach(state.items.indexed(), id: \.1.id) { index, item in
                     NavigationLink(destination: pickers(for: index)) {
                         HStack {
                             Text("Rate").foregroundColor(.secondary)
                             Text(
-                                "\(rateFormatter.string(from: viewModel.rateValues[item.rateIndex] as NSNumber) ?? "0") \(viewModel.units.rawValue)/U"
+                                "\(rateFormatter.string(from: state.rateValues[item.rateIndex] as NSNumber) ?? "0") \(state.units.rawValue)/U"
                             )
                             Spacer()
                             Text("starts at").foregroundColor(.secondary)
                             Text(
-                                "\(dateFormatter.string(from: Date(timeIntervalSince1970: viewModel.timeValues[item.timeIndex])))"
+                                "\(dateFormatter.string(from: Date(timeIntervalSince1970: state.timeValues[item.timeIndex])))"
                             )
                         }
                     }
@@ -135,7 +138,7 @@ extension ISFEditor {
         }
 
         private var addButton: some View {
-            guard viewModel.canAdd else {
+            guard state.canAdd else {
                 return AnyView(EmptyView())
             }
 
@@ -148,12 +151,12 @@ extension ISFEditor {
         }
 
         func onAdd() {
-            viewModel.add()
+            state.add()
         }
 
         private func onDelete(offsets: IndexSet) {
-            viewModel.items.remove(atOffsets: offsets)
-            viewModel.validate()
+            state.items.remove(atOffsets: offsets)
+            state.validate()
         }
     }
 }

+ 0 - 3
FreeAPS/Sources/Modules/Login/LoginBuilder.swift

@@ -1,3 +0,0 @@
-extension Login {
-    final class Builder: BaseModuleBuilder<RootView, ViewModel<Provider>, Provider> {}
-}

+ 0 - 11
FreeAPS/Sources/Modules/Login/LoginDataFlow.swift

@@ -1,11 +0,0 @@
-
-enum Login {
-    enum Config {
-        static let credentialsKey = "FreeAPS.Credentials"
-    }
-}
-
-protocol LoginProvider: Provider {
-    func authorize(credentials: Credentials)
-    var credentials: Credentials? { get }
-}

+ 0 - 19
FreeAPS/Sources/Modules/Login/LoginProvider.swift

@@ -1,19 +0,0 @@
-import AuthenticationServices
-
-extension Login {
-    final class Provider: BaseProvider, LoginProvider {
-        @Injected() private var keychain: Keychain!
-
-        func authorize(credentials: Credentials) {
-            authorizationManager.authorize(credentials: credentials)
-                .sink { _ in
-                    self.keychain.setValue(credentials, forKey: Config.credentialsKey)
-                }
-                .store(in: &lifetime)
-        }
-
-        var credentials: Credentials? {
-            keychain.getValue(Credentials.self, forKey: Config.credentialsKey)
-        }
-    }
-}

+ 0 - 23
FreeAPS/Sources/Modules/Login/LoginViewModel.swift

@@ -1,23 +0,0 @@
-import AuthenticationServices
-import SwiftUI
-
-extension Login {
-    class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: LoginProvider {
-        @Published var credentials: Credentials?
-
-        override func subscribe() {
-            credentials = provider.credentials
-
-            $credentials
-                .compactMap { $0 }
-                .sink { [weak self] in
-                    self?.provider.authorize(credentials: $0)
-                }
-                .store(in: &lifetime)
-        }
-
-        func login() {
-            credentials = Credentials()
-        }
-    }
-}

+ 0 - 24
FreeAPS/Sources/Modules/Login/View/LoginRootView.swift

@@ -1,24 +0,0 @@
-import SwiftUI
-
-extension Login {
-    struct RootView: BaseView {
-        @EnvironmentObject var viewModel: ViewModel<Provider>
-
-        var body: some View {
-            VStack {
-                Text("Disclaimer").font(.title)
-                Spacer()
-                Text(
-                    "FreeAPS X is in an active development state. We do not recommend to use the system for everyday control of blood glucose! Use it for testing purposes only at your own risk. We are not responsible for your decisions and actions."
-                )
-                Spacer()
-                Button(action: viewModel.login) {
-                    Text("Agree and continue")
-                        .frame(maxWidth: .infinity)
-                        .foregroundColor(.white)
-                        .buttonBackground()
-                }
-            }.padding()
-        }
-    }
-}

+ 0 - 5
FreeAPS/Sources/Modules/Main/MainBuilder.swift

@@ -1,5 +0,0 @@
-import Swinject
-
-extension Main {
-    final class Builder: BaseModuleBuilder<RootView, ViewModel<Provider>, Provider> {}
-}

+ 0 - 0
FreeAPS/Sources/Modules/Main/MainDataFlow.swift


Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov