Просмотр исходного кода

Merge branch 'Crowdin' into XPM

Jon B.M 4 лет назад
Родитель
Сommit
a10a5c4e45
100 измененных файлов с 682 добавлено и 1060 удалено
  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 rapidActingAdult
     case rapidActingChild
     case rapidActingChild
     case fiasp
     case fiasp
+    case lyumjev
 }
 }
 
 
 
 
@@ -22,6 +23,8 @@ extension ExponentialInsulinModelPreset {
             return .minutes(360)
             return .minutes(360)
         case .fiasp:
         case .fiasp:
             return .minutes(360)
             return .minutes(360)
+        case .lyumjev:
+            return .minutes(330)
         }
         }
     }
     }
 
 
@@ -33,6 +36,8 @@ extension ExponentialInsulinModelPreset {
             return .minutes(65)
             return .minutes(65)
         case .fiasp:
         case .fiasp:
             return .minutes(55)
             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 {
         switch type {
         case .fiasp:
         case .fiasp:
             return ExponentialInsulinModelPreset.fiasp
             return ExponentialInsulinModelPreset.fiasp
+        case .lyumjev:
+            return ExponentialInsulinModelPreset.lyumjev
         default:
         default:
             switch self {
             switch self {
             case .exponentialPreset(let model):
             case .exponentialPreset(let model):
@@ -150,6 +152,8 @@ public extension InsulinModelSettings {
         switch storedSettingsInsulinModel.modelType {
         switch storedSettingsInsulinModel.modelType {
         case .fiasp:
         case .fiasp:
             self = .exponentialPreset(.fiasp)
             self = .exponentialPreset(.fiasp)
+        case .lyumjev:
+            self = .exponentialPreset(.lyumjev)
         case .rapidAdult:
         case .rapidAdult:
             self = .exponentialPreset(.rapidActingAdult)
             self = .exponentialPreset(.rapidActingAdult)
         case .rapidChild:
         case .rapidChild:
@@ -175,6 +179,8 @@ public extension StoredInsulinModel {
                 modelType = .rapidChild
                 modelType = .rapidChild
             case .fiasp:
             case .fiasp:
                 modelType = .fiasp
                 modelType = .fiasp
+            case .lyumjev:
+                modelType = .lyumjev
             }
             }
             actionDuration = preset.actionDuration
             actionDuration = preset.actionDuration
             peakActivity = preset.peakActivity
             peakActivity = preset.peakActivity

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

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

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

@@ -13,6 +13,7 @@ public enum InsulinType: Int, Codable, CaseIterable {
     case humalog
     case humalog
     case apidra
     case apidra
     case fiasp
     case fiasp
+    case lyumjev
     
     
     public var title: String {
     public var title: String {
         switch self {
         switch self {
@@ -24,6 +25,8 @@ public enum InsulinType: Int, Codable, CaseIterable {
             return LocalizedString("Apidra (insulin glulisine)", comment: "Title for Apidra insulin type")
             return LocalizedString("Apidra (insulin glulisine)", comment: "Title for Apidra insulin type")
         case .fiasp:
         case .fiasp:
             return LocalizedString("Fiasp", comment: "Title for Fiasp insulin type")
             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")
             return LocalizedString("Apidra", comment: "Brand name for apidra insulin type")
         case .fiasp:
         case .fiasp:
             return LocalizedString("Fiasp", comment: "Brand name for fiasp insulin type")
             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")
             return LocalizedString("Apidra (insulin glulisine) is a fast-acting insulin made by Sanofi-aventis ", comment: "Description for apidra insulin type")
         case .fiasp:
         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")
             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 struct StoredInsulinModel: Codable, Equatable {
     public enum ModelType: String, Codable {
     public enum ModelType: String, Codable {
         case fiasp
         case fiasp
+        case lyumjev
         case rapidAdult
         case rapidAdult
         case rapidChild
         case rapidChild
         case walsh
         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")
             return LocalizedString("Rapid-Acting – Children", comment: "Title of insulin model preset - rapid acting children")
         case .fiasp:
         case .fiasp:
             return LocalizedString("Fiasp", comment: "Title of insulin model preset - 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")
             return LocalizedString("This model assumes peak insulin activity at 65 minutes.", comment: "Subtitle of Rapid-Acting – Children preset")
         case .fiasp:
         case .fiasp:
             return LocalizedString("This model assumes peak insulin activity at 55 minutes.", comment: "Subtitle of Fiasp preset")
             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
                 customOverrideVC.delegate = self
                 show(customOverrideVC, sender: collectionView.cellForItem(at: indexPath))
                 show(customOverrideVC, sender: collectionView.cellForItem(at: indexPath))
             case .history:
             case .history:
-                let model = OverrideHistoryViewModel(
+                let model = OverrideHistorystate(
                     overrides: overrideHistory,
                     overrides: overrideHistory,
                     glucoseUnit: glucoseUnit
                     glucoseUnit: glucoseUnit
                 )
                 )

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

@@ -10,7 +10,7 @@ import SwiftUI
 import LoopKit
 import LoopKit
 import HealthKit
 import HealthKit
 
 
-public class OverrideHistoryViewModel: ObservableObject {
+public class OverrideHistorystate: ObservableObject {
     var overrides: [TemporaryScheduleOverride]
     var overrides: [TemporaryScheduleOverride]
     var glucoseUnit: HKUnit
     var glucoseUnit: HKUnit
     var didEditOverride: ((TemporaryScheduleOverride) -> Void)?
     var didEditOverride: ((TemporaryScheduleOverride) -> Void)?
@@ -26,12 +26,12 @@ public class OverrideHistoryViewModel: ObservableObject {
 }
 }
 
 
 public struct OverrideSelectionHistory: View {
 public struct OverrideSelectionHistory: View {
-    @ObservedObject var model: OverrideHistoryViewModel
+    @ObservedObject var model: OverrideHistorystate
     private var quantityFormatter: QuantityFormatter
     private var quantityFormatter: QuantityFormatter
     private var glucoseNumberFormatter: NumberFormatter
     private var glucoseNumberFormatter: NumberFormatter
     private var durationFormatter: DateComponentsFormatter
     private var durationFormatter: DateComponentsFormatter
     
     
-    public init(model: OverrideHistoryViewModel) {
+    public init(model: OverrideHistorystate) {
         self.model = model
         self.model = model
         self.quantityFormatter = {
         self.quantityFormatter = {
             let quantityFormatter = 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) */
 /* 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) */
 /* The format string for displaying an offset from a time zone: (1: GMT)(2: -)(3: 4:00) */
 "%1$@%2$@%3$@" = "%1$@%2$@%3$@";
 "%1$@%2$@%3$@" = "%1$@%2$@%3$@";
@@ -72,7 +72,7 @@
 "Configuration" = "Configuratie";
 "Configuration" = "Configuratie";
 
 
 /* The title of the Insulin Type */
 /* The title of the Insulin Type */
-"Insulin Type" = "Insulin Type";
+"Insulin Type" = "Insuline soort";
 
 
 /* The title of the continue action in an action sheet */
 /* The title of the continue action in an action sheet */
 "Continue" = "Vervolg";
 "Continue" = "Vervolg";
@@ -94,7 +94,7 @@
 "Device Information" = "Apparaat informatie";
 "Device Information" = "Apparaat informatie";
 
 
 /* Title text for button to disable bolus beeps */
 /* 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 */
 /* Title text for button to enable bolus beeps */
 "Enable Bolus Beeps" = "Pas Bolus Piepjes gebruik toe";
 "Enable Bolus Beeps" = "Pas Bolus Piepjes gebruik toe";
@@ -106,16 +106,16 @@
 "Error enabling bolus beeps" = "Fout in toepassen gebruik bolus piepjes";
 "Error enabling bolus beeps" = "Fout in toepassen gebruik bolus piepjes";
 
 
 /* The alert title for enable Confirmation Beeps */
 /* 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 */
 /* 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 */
 /* 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 */
 /* 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 */
 /* The alert title for a resume error */
 "Error Resuming" = "Fout met hervatten";
 "Error Resuming" = "Fout met hervatten";
@@ -249,13 +249,13 @@
 "Unknown" = "Onbekend";
 "Unknown" = "Onbekend";
 
 
 /* The title of the diagnostics section in settings */
 /* The title of the diagnostics section in settings */
-"Diagnostics" = "Diagnostics";
+"Diagnostics" = "Diagnostische gegevens";
 
 
 /* The diagnostic command Read Pod Status */
 /* The diagnostic command Read Pod Status */
-"Read Pod Status" = "Read Pod Status";
+"Read Pod Status" = "Lees pompstatus";
 
 
 /* The diagnostic command Read Pulse Log */
 /* 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 */
 /* The title of the pod details section in settings */
 "Pod Details" = "Pod Details";
 "Pod Details" = "Pod Details";

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

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

Разница между файлами не показана из-за своего большого размера
+ 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(
         setupViewController(
             insulinTintColor: .accentColor,
             insulinTintColor: .accentColor,
             guidanceColors: GuidanceColors(acceptable: .green, warning: .orange, critical: .red),
             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(
         settingsViewController(
             insulinTintColor: .accentColor,
             insulinTintColor: .accentColor,
             guidanceColors: GuidanceColors(acceptable: .green, warning: .orange, critical: .red),
             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 SwiftUI
 import Swinject
 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 {
 @main struct FreeAPSApp: App {
     @Environment(\.scenePhase) var scenePhase
     @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(AppearanceManager.self)!.setupGlobalAppearance()
         _ = resolver.resolve(DeviceDataManager.self)!
         _ = resolver.resolve(DeviceDataManager.self)!
         _ = resolver.resolve(APSManager.self)!
         _ = resolver.resolve(APSManager.self)!
@@ -37,27 +37,15 @@ private extension Swinject.Resolver {
     }
     }
 
 
     init() {
     init() {
-        FreeAPSApp.resolver.setup()
-        FreeAPSApp.loadServices()
+        loadServices()
     }
     }
 
 
-    private let mainView = Main.Builder(resolver: FreeAPSApp.resolver).buildView()
-
     var body: some Scene {
     var body: some Scene {
         WindowGroup {
         WindowGroup {
-            mainView
+            Main.RootView(resolver: resolver)
         }
         }
         .onChange(of: scenePhase) { newScenePhase in
         .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 Foundation
 import Swinject
 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(NotificationCenter.self) { _ in Foundation.NotificationCenter.default }
         container.register(Broadcaster.self) { _ in BaseBroadcaster() }
         container.register(Broadcaster.self) { _ in BaseBroadcaster() }
         container.register(GroupedIssueReporter.self) { _ in
         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) }
-    }
-}

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
FreeAPS/Sources/Localizations/Main/pt.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 25 - 25
FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 3 - 3
FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings


Разница между файлами не показана из-за своего большого размера
+ 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 maxCOB: Decimal = 120
     var wideBGTargetRange: Bool = false
     var wideBGTargetRange: Bool = false
     var skipNeutralTemps: Bool = false
     var skipNeutralTemps: Bool = false
-    var unsuspendIfNoTemp: Bool = true
+    var unsuspendIfNoTemp: Bool = false
     var bolusSnoozeDIADivisor: Decimal = 2
     var bolusSnoozeDIADivisor: Decimal = 2
     var min5mCarbimpact: Decimal = 8
     var min5mCarbimpact: Decimal = 8
     var autotuneISFAdjustmentFraction: Decimal = 1.0
     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
 import SwiftUI
 
 
 extension AddCarbs {
 extension AddCarbs {
-    class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: AddCarbsProvider {
+    final class StateModel: BaseStateModel<Provider> {
         @Injected() var carbsStorage: CarbsStorage!
         @Injected() var carbsStorage: CarbsStorage!
         @Injected() var settingsManager: SettingsManager!
         @Injected() var settingsManager: SettingsManager!
         @Injected() var apsManager: APSManager!
         @Injected() var apsManager: APSManager!

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

@@ -1,8 +1,10 @@
 import SwiftUI
 import SwiftUI
+import Swinject
 
 
 extension AddCarbs {
 extension AddCarbs {
     struct RootView: BaseView {
     struct RootView: BaseView {
-        @EnvironmentObject var viewModel: ViewModel<Provider>
+        let resolver: Resolver
+        @StateObject var state = StateModel()
 
 
         private var formatter: NumberFormatter {
         private var formatter: NumberFormatter {
             let formatter = NumberFormatter()
             let formatter = NumberFormatter()
@@ -13,7 +15,7 @@ extension AddCarbs {
 
 
         var body: some View {
         var body: some View {
             Form {
             Form {
-                if let carbsReq = viewModel.carbsRequired {
+                if let carbsReq = state.carbsRequired {
                     Section {
                     Section {
                         HStack {
                         HStack {
                             Text("Carbs required")
                             Text("Carbs required")
@@ -26,21 +28,22 @@ extension AddCarbs {
                     HStack {
                     HStack {
                         Text("Amount")
                         Text("Amount")
                         Spacer()
                         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)
                         Text("grams").foregroundColor(.secondary)
                     }
                     }
-                    DatePicker("Date", selection: $viewModel.date)
+                    DatePicker("Date", selection: $state.date)
                 }
                 }
 
 
                 Section {
                 Section {
-                    Button { viewModel.add() }
+                    Button { state.add() }
                     label: { Text("Add") }
                     label: { Text("Add") }
-                        .disabled(viewModel.carbs <= 0)
+                        .disabled(state.carbs <= 0)
                 }
                 }
             }
             }
+            .onAppear(perform: configureView)
             .navigationTitle("Add Carbs")
             .navigationTitle("Add Carbs")
             .navigationBarTitleDisplayMode(.automatic)
             .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
 import SwiftUI
 
 
 extension AddTempTarget {
 extension AddTempTarget {
-    class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: AddTempTargetProvider {
+    final class StateModel: BaseStateModel<Provider> {
         @Injected() private var storage: TempTargetsStorage!
         @Injected() private var storage: TempTargetsStorage!
         @Injected() private var settingsManager: SettingsManager!
         @Injected() private var settingsManager: SettingsManager!
         @Injected() var apsManager: APSManager!
         @Injected() var apsManager: APSManager!

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

@@ -1,8 +1,10 @@
 import SwiftUI
 import SwiftUI
+import Swinject
 
 
 extension AddTempTarget {
 extension AddTempTarget {
     struct RootView: BaseView {
     struct RootView: BaseView {
-        @EnvironmentObject var viewModel: ViewModel<Provider>
+        let resolver: Resolver
+        @StateObject var state = StateModel()
         @State private var isPromtPresented = false
         @State private var isPromtPresented = false
         @State private var isRemoveAlertPresented = false
         @State private var isRemoveAlertPresented = false
         @State private var removeAlert: Alert?
         @State private var removeAlert: Alert?
@@ -16,9 +18,9 @@ extension AddTempTarget {
 
 
         var body: some View {
         var body: some View {
             Form {
             Form {
-                if !viewModel.presets.isEmpty {
+                if !state.presets.isEmpty {
                     Section(header: Text("Presets")) {
                     Section(header: Text("Presets")) {
-                        ForEach(viewModel.presets) { preset in
+                        ForEach(state.presets) { preset in
                             presetView(for: preset)
                             presetView(for: preset)
                         }
                         }
                     }
                     }
@@ -28,39 +30,39 @@ extension AddTempTarget {
                     HStack {
                     HStack {
                         Text("Bottom target")
                         Text("Bottom target")
                         Spacer()
                         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 {
                     HStack {
                         Text("Top target")
                         Text("Top target")
                         Spacer()
                         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 {
                     HStack {
                         Text("Duration")
                         Text("Duration")
                         Spacer()
                         Spacer()
-                        DecimalTextField("0", value: $viewModel.duration, formatter: formatter, cleanInput: true)
+                        DecimalTextField("0", value: $state.duration, formatter: formatter, cleanInput: true)
                         Text("minutes").foregroundColor(.secondary)
                         Text("minutes").foregroundColor(.secondary)
                     }
                     }
-                    DatePicker("Date", selection: $viewModel.date)
+                    DatePicker("Date", selection: $state.date)
                     Button { isPromtPresented = true }
                     Button { isPromtPresented = true }
                     label: { Text("Save as preset") }
                     label: { Text("Save as preset") }
                 }
                 }
 
 
                 Section {
                 Section {
-                    Button { viewModel.enact() }
+                    Button { state.enact() }
                     label: { Text("Enact") }
                     label: { Text("Enact") }
-                    Button { viewModel.cancel() }
+                    Button { state.cancel() }
                     label: { Text("Cancel Temp Target") }
                     label: { Text("Cancel Temp Target") }
                 }
                 }
             }
             }
             .popover(isPresented: $isPromtPresented) {
             .popover(isPresented: $isPromtPresented) {
                 Form {
                 Form {
                     Section(header: Text("Enter preset name")) {
                     Section(header: Text("Enter preset name")) {
-                        TextField("Name", text: $viewModel.newPresetName)
+                        TextField("Name", text: $state.newPresetName)
                         Button {
                         Button {
-                            viewModel.save()
+                            state.save()
                             isPromtPresented = false
                             isPromtPresented = false
                         }
                         }
                         label: { Text("Save") }
                         label: { Text("Save") }
@@ -69,15 +71,16 @@ extension AddTempTarget {
                     }
                     }
                 }
                 }
             }
             }
+            .onAppear(perform: configureView)
             .navigationTitle("Enact Temp Target")
             .navigationTitle("Enact Temp Target")
             .navigationBarTitleDisplayMode(.automatic)
             .navigationBarTitleDisplayMode(.automatic)
-            .navigationBarItems(leading: Button("Close", action: viewModel.hideModal))
+            .navigationBarItems(leading: Button("Close", action: state.hideModal))
         }
         }
 
 
         private func presetView(for preset: TempTarget) -> some View {
         private func presetView(for preset: TempTarget) -> some View {
             var low = preset.targetBottom
             var low = preset.targetBottom
             var high = preset.targetTop
             var high = preset.targetTop
-            if viewModel.units == .mmolL {
+            if state.units == .mmolL {
                 low = low?.asMmolL
                 low = low?.asMmolL
                 high = high?.asMmolL
                 high = high?.asMmolL
             }
             }
@@ -94,7 +97,7 @@ extension AddTempTarget {
                         .foregroundColor(.secondary)
                         .foregroundColor(.secondary)
                         .font(.caption)
                         .font(.caption)
 
 
-                        Text(viewModel.units.rawValue)
+                        Text(state.units.rawValue)
                             .foregroundColor(.secondary)
                             .foregroundColor(.secondary)
                             .font(.caption)
                             .font(.caption)
                         Text("for \(formatter.string(from: preset.duration as NSNumber)!) min")
                         Text("for \(formatter.string(from: preset.duration as NSNumber)!) min")
@@ -105,7 +108,7 @@ extension AddTempTarget {
                 }
                 }
                 .contentShape(Rectangle())
                 .contentShape(Rectangle())
                 .onTapGesture {
                 .onTapGesture {
-                    viewModel.enactPreset(id: preset.id)
+                    state.enactPreset(id: preset.id)
                 }
                 }
 
 
                 Image(systemName: "xmark.circle").foregroundColor(.secondary)
                 Image(systemName: "xmark.circle").foregroundColor(.secondary)
@@ -115,7 +118,7 @@ extension AddTempTarget {
                         removeAlert = Alert(
                         removeAlert = Alert(
                             title: Text("Are you sure?"),
                             title: Text("Are you sure?"),
                             message: Text("Delete preset \"\(preset.displayName)\""),
                             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()
                             secondaryButton: .cancel()
                         )
                         )
                         isRemoveAlertPresented = true
                         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
 import SwiftUI
 
 
 extension AutotuneConfig {
 extension AutotuneConfig {
-    class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: AutotuneConfigProvider {
+    final class StateModel: BaseStateModel<Provider> {
         @Injected() var settingsManager: SettingsManager!
         @Injected() var settingsManager: SettingsManager!
         @Injected() var apsManager: APSManager!
         @Injected() var apsManager: APSManager!
         @Published var useAutotune = false
         @Published var useAutotune = false
@@ -25,7 +25,10 @@ extension AutotuneConfig {
 
 
             $useAutotune
             $useAutotune
                 .removeDuplicates()
                 .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
                     self.settingsManager.settings.useAutotune = use
                     return self.apsManager.makeProfiles()
                     return self.apsManager.makeProfiles()
                 }
                 }
@@ -36,12 +39,15 @@ extension AutotuneConfig {
         func run() {
         func run() {
             provider.runAutotune()
             provider.runAutotune()
                 .receive(on: DispatchQueue.main)
                 .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
                     self.autotune = result
                     return self.apsManager.makeProfiles()
                     return self.apsManager.makeProfiles()
                 }
                 }
-                .sink { _ in
-                    self.lastAutotuneDate = Date()
+                .sink { [weak self] _ in
+                    self?.lastAutotuneDate = Date()
                 }.store(in: &lifetime)
                 }.store(in: &lifetime)
         }
         }
 
 

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

@@ -1,8 +1,10 @@
 import SwiftUI
 import SwiftUI
+import Swinject
 
 
 extension AutotuneConfig {
 extension AutotuneConfig {
     struct RootView: BaseView {
     struct RootView: BaseView {
-        @EnvironmentObject var viewModel: ViewModel<Provider>
+        let resolver: Resolver
+        @StateObject var state = StateModel()
 
 
         private var isfFormatter: NumberFormatter {
         private var isfFormatter: NumberFormatter {
             let formatter = NumberFormatter()
             let formatter = NumberFormatter()
@@ -28,20 +30,20 @@ extension AutotuneConfig {
         var body: some View {
         var body: some View {
             Form {
             Form {
                 Section {
                 Section {
-                    Toggle("Use Autotune", isOn: $viewModel.useAutotune)
+                    Toggle("Use Autotune", isOn: $state.useAutotune)
                 }
                 }
 
 
                 Section {
                 Section {
                     HStack {
                     HStack {
                         Text("Last run")
                         Text("Last run")
                         Spacer()
                         Spacer()
-                        Text(dateFormatter.string(from: viewModel.publishedDate))
+                        Text(dateFormatter.string(from: state.publishedDate))
                     }
                     }
-                    Button { viewModel.run() }
+                    Button { state.run() }
                     label: { Text("Run now") }
                     label: { Text("Run now") }
                 }
                 }
 
 
-                if let autotune = viewModel.autotune {
+                if let autotune = state.autotune {
                     Section {
                     Section {
                         HStack {
                         HStack {
                             Text("Carb ratio")
                             Text("Carb ratio")
@@ -52,12 +54,12 @@ extension AutotuneConfig {
                         HStack {
                         HStack {
                             Text("Sensitivity")
                             Text("Sensitivity")
                             Spacer()
                             Spacer()
-                            if viewModel.units == .mmolL {
+                            if state.units == .mmolL {
                                 Text(isfFormatter.string(from: autotune.sensitivity.asMmolL as NSNumber) ?? "0")
                                 Text(isfFormatter.string(from: autotune.sensitivity.asMmolL as NSNumber) ?? "0")
                             } else {
                             } else {
                                 Text(isfFormatter.string(from: autotune.sensitivity as NSNumber) ?? "0")
                                 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 {
                     Section {
-                        Button { viewModel.delete() }
+                        Button { state.delete() }
                         label: { Text("Delete autotune data") }
                         label: { Text("Delete autotune data") }
                             .foregroundColor(.red)
                             .foregroundColor(.red)
                     }
                     }
                 }
                 }
             }
             }
+            .onAppear(perform: configureView)
             .navigationTitle("Autotune")
             .navigationTitle("Autotune")
             .navigationBarTitleDisplayMode(.automatic)
             .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
 import SwiftUI
 
 
 extension BasalProfileEditor {
 extension BasalProfileEditor {
-    class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: BasalProfileEditorProvider {
+    final class StateModel: BaseStateModel<Provider> {
         @Published var syncInProgress = false
         @Published var syncInProgress = false
         @Published var items: [Item] = []
         @Published var items: [Item] = []
 
 

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

@@ -1,8 +1,10 @@
 import SwiftUI
 import SwiftUI
+import Swinject
 
 
 extension BasalProfileEditor {
 extension BasalProfileEditor {
     struct RootView: BaseView {
     struct RootView: BaseView {
-        @EnvironmentObject var viewModel: ViewModel<Provider>
+        let resolver: Resolver
+        @StateObject var state = StateModel()
         @State private var editMode = EditMode.inactive
         @State private var editMode = EditMode.inactive
 
 
         private var dateFormatter: DateFormatter {
         private var dateFormatter: DateFormatter {
@@ -26,17 +28,18 @@ extension BasalProfileEditor {
                 }
                 }
                 Section {
                 Section {
                     HStack {
                     HStack {
-                        if viewModel.syncInProgress {
+                        if state.syncInProgress {
                             ProgressView().padding(.trailing, 10)
                             ProgressView().padding(.trailing, 10)
                         }
                         }
-                        Button { viewModel.save() }
+                        Button { state.save() }
                         label: {
                         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")
             .navigationTitle("Basal Profile")
             .navigationBarTitleDisplayMode(.automatic)
             .navigationBarTitleDisplayMode(.automatic)
             .navigationBarItems(
             .navigationBarItems(
@@ -44,7 +47,7 @@ extension BasalProfileEditor {
             )
             )
             .environment(\.editMode, $editMode)
             .environment(\.editMode, $editMode)
             .onAppear {
             .onAppear {
-                viewModel.validate()
+                state.validate()
             }
             }
         }
         }
 
 
@@ -56,12 +59,12 @@ extension BasalProfileEditor {
                         Text("Time").frame(width: geometry.size.width / 2)
                         Text("Time").frame(width: geometry.size.width / 2)
                     }
                     }
                     HStack(spacing: 0) {
                     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(
                                 Text(
                                     (
                                     (
                                         self.rateFormatter
                                         self.rateFormatter
-                                            .string(from: viewModel.rateValues[i] as NSNumber) ?? ""
+                                            .string(from: state.rateValues[i] as NSNumber) ?? ""
                                     ) + " U/hr"
                                     ) + " U/hr"
                                 ).tag(i)
                                 ).tag(i)
                             }
                             }
@@ -69,12 +72,12 @@ extension BasalProfileEditor {
                         .frame(maxWidth: geometry.size.width / 2)
                         .frame(maxWidth: geometry.size.width / 2)
                         .clipped()
                         .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(
                                 Text(
                                     self.dateFormatter
                                     self.dateFormatter
                                         .string(from: Date(
                                         .string(from: Date(
-                                            timeIntervalSince1970: viewModel
+                                            timeIntervalSince1970: state
                                                 .timeValues[i]
                                                 .timeValues[i]
                                         ))
                                         ))
                                 ).tag(i)
                                 ).tag(i)
@@ -89,17 +92,17 @@ extension BasalProfileEditor {
 
 
         private var list: some View {
         private var list: some View {
             List {
             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)) {
                     NavigationLink(destination: pickers(for: index)) {
                         HStack {
                         HStack {
                             Text("Rate").foregroundColor(.secondary)
                             Text("Rate").foregroundColor(.secondary)
                             Text(
                             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()
                             Spacer()
                             Text("starts at").foregroundColor(.secondary)
                             Text("starts at").foregroundColor(.secondary)
                             Text(
                             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 {
         private var addButton: some View {
-            guard viewModel.canAdd else {
+            guard state.canAdd else {
                 return AnyView(EmptyView())
                 return AnyView(EmptyView())
             }
             }
 
 
@@ -123,12 +126,12 @@ extension BasalProfileEditor {
         }
         }
 
 
         func onAdd() {
         func onAdd() {
-            viewModel.add()
+            state.add()
         }
         }
 
 
         private func onDelete(offsets: IndexSet) {
         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 {
 class BaseProvider: Provider, Injectable {
     var lifetime = Lifetime()
     var lifetime = Lifetime()
-    @Injected() var authorizationManager: AuthorizationManager!
     @Injected() var deviceManager: DeviceDataManager!
     @Injected() var deviceManager: DeviceDataManager!
     @Injected() var storage: FileStorage!
     @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 SwiftUI
+import Swinject
 
 
 protocol BaseView: View {
 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 {
 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
 import Swinject
 
 
 extension Bolus {
 extension Bolus {
-    class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: BolusProvider {
+    final class StateModel: BaseStateModel<Provider> {
         @Injected() var unlockmanager: UnlockManager!
         @Injected() var unlockmanager: UnlockManager!
         @Injected() var apsManager: APSManager!
         @Injected() var apsManager: APSManager!
         @Injected() var broadcaster: Broadcaster!
         @Injected() var broadcaster: Broadcaster!
@@ -11,18 +11,8 @@ extension Bolus {
         @Published var amount: Decimal = 0
         @Published var amount: Decimal = 0
         @Published var inslinRecommended: Decimal = 0
         @Published var inslinRecommended: Decimal = 0
         @Published var inslinRequired: 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() {
         override func subscribe() {
             setupInsulinRequired()
             setupInsulinRequired()
@@ -51,7 +41,8 @@ extension Bolus {
             let maxAmount = Double(min(amount, provider.pumpSettings().maxBolus))
             let maxAmount = Double(min(amount, provider.pumpSettings().maxBolus))
 
 
             unlockmanager.unlock()
             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.apsManager.enactBolus(amount: maxAmount, isSMB: false)
                     self.showModal(for: nil)
                     self.showModal(for: nil)
                 }
                 }
@@ -92,7 +83,7 @@ extension Bolus {
     }
     }
 }
 }
 
 
-extension Bolus.ViewModel: SuggestionObserver {
+extension Bolus.StateModel: SuggestionObserver {
     func suggestionDidUpdate(_: Suggestion) {
     func suggestionDidUpdate(_: Suggestion) {
         DispatchQueue.main.async {
         DispatchQueue.main.async {
             self.waitForSuggestion = false
             self.waitForSuggestion = false

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

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

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

@@ -1,37 +1,40 @@
 import SwiftUI
 import SwiftUI
+import Swinject
 
 
 extension CGM {
 extension CGM {
     struct RootView: BaseView {
     struct RootView: BaseView {
-        @EnvironmentObject var viewModel: ViewModel<Provider>
+        let resolver: Resolver
+        @StateObject var state = StateModel()
 
 
         var body: some View {
         var body: some View {
             Form {
             Form {
                 Section {
                 Section {
-                    Picker("Type", selection: $viewModel.cgm) {
+                    Picker("Type", selection: $state.cgm) {
                         ForEach(CGMType.allCases) {
                         ForEach(CGMType.allCases) {
                             Text($0.displayName).tag($0)
                             Text($0.displayName).tag($0)
                         }
                         }
                     }
                     }
                 }
                 }
-                if [.dexcomG5, .dexcomG6].contains(viewModel.cgm) {
+                if [.dexcomG5, .dexcomG6].contains(state.cgm) {
                     Section(header: Text("Transmitter ID")) {
                     Section(header: Text("Transmitter ID")) {
-                        TextField("XXXXXX", text: $viewModel.transmitterID, onCommit: {
+                        TextField("XXXXXX", text: $state.transmitterID, onCommit: {
                             UIApplication.shared.endEditing()
                             UIApplication.shared.endEditing()
-                            viewModel.onChangeID()
+                            state.onChangeID()
                         })
                         })
                             .disableAutocorrection(true)
                             .disableAutocorrection(true)
                             .autocapitalization(.allCharacters)
                             .autocapitalization(.allCharacters)
                             .keyboardType(.asciiCapable)
                             .keyboardType(.asciiCapable)
                     }
                     }
                     .onDisappear {
                     .onDisappear {
-                        viewModel.onChangeID()
+                        state.onChangeID()
                     }
                     }
                 }
                 }
 
 
                 Section(header: Text("Other")) {
                 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")
             .navigationTitle("CGM")
             .navigationBarTitleDisplayMode(.automatic)
             .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
 import SwiftUI
 
 
 extension CREditor {
 extension CREditor {
-    class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: CREditorProvider {
+    final class StateModel: BaseStateModel<Provider> {
         @Published var items: [Item] = []
         @Published var items: [Item] = []
         @Published var autotune: Autotune?
         @Published var autotune: Autotune?
 
 

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

@@ -1,8 +1,10 @@
 import SwiftUI
 import SwiftUI
+import Swinject
 
 
 extension CREditor {
 extension CREditor {
     struct RootView: BaseView {
     struct RootView: BaseView {
-        @EnvironmentObject var viewModel: ViewModel<Provider>
+        let resolver: Resolver
+        @StateObject var state = StateModel()
         @State private var editMode = EditMode.inactive
         @State private var editMode = EditMode.inactive
 
 
         private var dateFormatter: DateFormatter {
         private var dateFormatter: DateFormatter {
@@ -20,7 +22,7 @@ extension CREditor {
 
 
         var body: some View {
         var body: some View {
             Form {
             Form {
-                if let autotune = viewModel.autotune {
+                if let autotune = state.autotune {
                     Section(header: Text("Autotune")) {
                     Section(header: Text("Autotune")) {
                         HStack {
                         HStack {
                             Text("Calculated Ratio")
                             Text("Calculated Ratio")
@@ -35,13 +37,14 @@ extension CREditor {
                     addButton
                     addButton
                 }
                 }
                 Section {
                 Section {
-                    Button { viewModel.save() }
+                    Button { state.save() }
                     label: {
                     label: {
                         Text("Save")
                         Text("Save")
                     }
                     }
-                    .disabled(viewModel.items.isEmpty)
+                    .disabled(state.items.isEmpty)
                 }
                 }
             }
             }
+            .onAppear(perform: configureView)
             .navigationTitle("Carb Ratios")
             .navigationTitle("Carb Ratios")
             .navigationBarTitleDisplayMode(.automatic)
             .navigationBarTitleDisplayMode(.automatic)
             .navigationBarItems(
             .navigationBarItems(
@@ -49,7 +52,7 @@ extension CREditor {
             )
             )
             .environment(\.editMode, $editMode)
             .environment(\.editMode, $editMode)
             .onAppear {
             .onAppear {
-                viewModel.validate()
+                state.validate()
             }
             }
         }
         }
 
 
@@ -61,12 +64,12 @@ extension CREditor {
                         Text("Time").frame(width: geometry.size.width / 2)
                         Text("Time").frame(width: geometry.size.width / 2)
                     }
                     }
                     HStack(spacing: 0) {
                     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(
                                 Text(
                                     (
                                     (
                                         self.rateFormatter
                                         self.rateFormatter
-                                            .string(from: viewModel.rateValues[i] as NSNumber) ?? ""
+                                            .string(from: state.rateValues[i] as NSNumber) ?? ""
                                     ) + " g/U"
                                     ) + " g/U"
                                 ).tag(i)
                                 ).tag(i)
                             }
                             }
@@ -74,12 +77,12 @@ extension CREditor {
                         .frame(maxWidth: geometry.size.width / 2)
                         .frame(maxWidth: geometry.size.width / 2)
                         .clipped()
                         .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(
                                 Text(
                                     self.dateFormatter
                                     self.dateFormatter
                                         .string(from: Date(
                                         .string(from: Date(
-                                            timeIntervalSince1970: viewModel
+                                            timeIntervalSince1970: state
                                                 .timeValues[i]
                                                 .timeValues[i]
                                         ))
                                         ))
                                 ).tag(i)
                                 ).tag(i)
@@ -94,17 +97,17 @@ extension CREditor {
 
 
         private var list: some View {
         private var list: some View {
             List {
             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)) {
                     NavigationLink(destination: pickers(for: index)) {
                         HStack {
                         HStack {
                             Text("Ratio").foregroundColor(.secondary)
                             Text("Ratio").foregroundColor(.secondary)
                             Text(
                             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()
                             Spacer()
                             Text("starts at").foregroundColor(.secondary)
                             Text("starts at").foregroundColor(.secondary)
                             Text(
                             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 {
         private var addButton: some View {
-            guard viewModel.canAdd else {
+            guard state.canAdd else {
                 return AnyView(EmptyView())
                 return AnyView(EmptyView())
             }
             }
 
 
@@ -128,12 +131,12 @@ extension CREditor {
         }
         }
 
 
         func onAdd() {
         func onAdd() {
-            viewModel.add()
+            state.add()
         }
         }
 
 
         private func onDelete(offsets: IndexSet) {
         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 SwiftUI
+import Swinject
 
 
 extension ConfigEditor {
 extension ConfigEditor {
     struct RootView: BaseView {
     struct RootView: BaseView {
-        @EnvironmentObject var viewModel: ViewModel<Provider>
+        let resolver: Resolver
+        let file: String
+        @StateObject var state = StateModel()
         @State private var showShareSheet = false
         @State private var showShareSheet = false
 
 
         var body: some View {
         var body: some View {
-            TextEditor(text: $viewModel.configText)
+            TextEditor(text: $state.configText)
                 .keyboardType(.asciiCapable)
                 .keyboardType(.asciiCapable)
                 .font(.system(.subheadline, design: .monospaced))
                 .font(.system(.subheadline, design: .monospaced))
                 .allowsTightening(true)
                 .allowsTightening(true)
@@ -22,12 +25,17 @@ extension ConfigEditor {
                     }
                     }
                 }
                 }
                 .navigationBarItems(
                 .navigationBarItems(
-                    trailing: Button("Save", action: viewModel.save)
+                    trailing: Button("Save", action: state.save)
                 )
                 )
                 .sheet(isPresented: $showShareSheet) {
                 .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)
                 .navigationBarTitleDisplayMode(.inline)
                 .padding()
                 .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
 import SwiftUI
 
 
 extension DataTable {
 extension DataTable {
-    class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: DataTableProvider {
+    final class StateModel: BaseStateModel<Provider> {
         @Injected() var broadcaster: Broadcaster!
         @Injected() var broadcaster: Broadcaster!
         @Injected() var settingsManager: SettingsManager!
         @Injected() var settingsManager: SettingsManager!
         @Published var items: [Item] = []
         @Published var items: [Item] = []
@@ -83,7 +83,7 @@ extension DataTable {
     }
     }
 }
 }
 
 
-extension DataTable.ViewModel:
+extension DataTable.StateModel:
     SettingsObserver,
     SettingsObserver,
     PumpHistoryObserver,
     PumpHistoryObserver,
     TempTargetsObserver,
     TempTargetsObserver,

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

@@ -1,8 +1,10 @@
 import SwiftUI
 import SwiftUI
+import Swinject
 
 
 extension DataTable {
 extension DataTable {
     struct RootView: BaseView {
     struct RootView: BaseView {
-        @EnvironmentObject var viewModel: ViewModel<Provider>
+        let resolver: Resolver
+        @StateObject var state = StateModel()
         @State private var isRemoveCarbsAlertPresented = false
         @State private var isRemoveCarbsAlertPresented = false
         @State private var removeCarbsAlert: Alert?
         @State private var removeCarbsAlert: Alert?
 
 
@@ -16,16 +18,17 @@ extension DataTable {
             Form {
             Form {
                 list
                 list
             }
             }
+            .onAppear(perform: configureView)
             .navigationTitle("History")
             .navigationTitle("History")
             .navigationBarTitleDisplayMode(.automatic)
             .navigationBarTitleDisplayMode(.automatic)
             .navigationBarItems(
             .navigationBarItems(
-                leading: Button("Close", action: viewModel.hideModal)
+                leading: Button("Close", action: state.hideModal)
             )
             )
         }
         }
 
 
         private var list: some View {
         private var list: some View {
             List {
             List {
-                ForEach(viewModel.items.indexed(), id: \.1.id) { _, item in
+                ForEach(state.items.indexed(), id: \.1.id) { _, item in
                     HStack {
                     HStack {
                         Image(systemName: "circle.fill").foregroundColor(item.color)
                         Image(systemName: "circle.fill").foregroundColor(item.color)
                         Text(dateFormatter.string(from: item.date))
                         Text(dateFormatter.string(from: item.date))
@@ -47,7 +50,7 @@ extension DataTable {
                                         message: Text(item.amountText),
                                         message: Text(item.amountText),
                                         primaryButton: .destructive(
                                         primaryButton: .destructive(
                                             Text("Delete"),
                                             Text("Delete"),
-                                            action: { viewModel.deleteCarbs(at: item.date) }
+                                            action: { state.deleteCarbs(at: item.date) }
                                         ),
                                         ),
                                         secondaryButton: .cancel()
                                         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
 import SwiftUI
 
 
 extension Home {
 extension Home {
-    class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: HomeProvider {
+    final class StateModel: BaseStateModel<Provider> {
         @Injected() var broadcaster: Broadcaster!
         @Injected() var broadcaster: Broadcaster!
         @Injected() var settingsManager: SettingsManager!
         @Injected() var settingsManager: SettingsManager!
         @Injected() var apsManager: APSManager!
         @Injected() var apsManager: APSManager!
@@ -281,7 +281,7 @@ extension Home {
     }
     }
 }
 }
 
 
-extension Home.ViewModel:
+extension Home.StateModel:
     GlucoseObserver,
     GlucoseObserver,
     SuggestionObserver,
     SuggestionObserver,
     SettingsObserver,
     SettingsObserver,
@@ -346,7 +346,7 @@ extension Home.ViewModel:
     }
     }
 }
 }
 
 
-extension Home.ViewModel: CompletionDelegate {
+extension Home.StateModel: CompletionDelegate {
     func completionNotifyingDidComplete(_: CompletionNotifying) {
     func completionNotifyingDidComplete(_: CompletionNotifying) {
         setupPump = false
         setupPump = false
     }
     }

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

@@ -1,9 +1,12 @@
 import SwiftDate
 import SwiftDate
 import SwiftUI
 import SwiftUI
+import Swinject
 
 
 extension Home {
 extension Home {
     struct RootView: BaseView {
     struct RootView: BaseView {
-        @EnvironmentObject var viewModel: ViewModel<Provider>
+        let resolver: Resolver
+
+        @StateObject var state = StateModel()
         @State var isStatusPopupPresented = false
         @State var isStatusPopupPresented = false
 
 
         private var numberFormatter: NumberFormatter {
         private var numberFormatter: NumberFormatter {
@@ -50,41 +53,41 @@ extension Home {
                 Spacer()
                 Spacer()
 
 
                 CurrentGlucoseView(
                 CurrentGlucoseView(
-                    recentGlucose: $viewModel.recentGlucose,
-                    delta: $viewModel.glucoseDelta,
-                    units: $viewModel.units
+                    recentGlucose: $state.recentGlucose,
+                    delta: $state.glucoseDelta,
+                    units: $state.units
                 )
                 )
                 .onTapGesture {
                 .onTapGesture {
-                    viewModel.openCGM()
+                    state.openCGM()
                 }
                 }
                 Spacer()
                 Spacer()
                 PumpView(
                 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 {
                 .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()
                 Spacer()
                 LoopView(
                 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 {
                 ).onTapGesture {
                     isStatusPopupPresented = true
                     isStatusPopupPresented = true
                 }.onLongPressGesture {
                 }.onLongPressGesture {
-                    viewModel.runLoop()
+                    state.runLoop()
                 }
                 }
                 Spacer()
                 Spacer()
             }.frame(maxWidth: .infinity)
             }.frame(maxWidth: .infinity)
@@ -92,11 +95,11 @@ extension Home {
 
 
         var infoPanal: some View {
         var infoPanal: some View {
             HStack(alignment: .center) {
             HStack(alignment: .center) {
-                if viewModel.pumpSuspended {
+                if state.pumpSuspended {
                     Text("Pump suspended")
                     Text("Pump suspended")
                         .font(.system(size: 12, weight: .bold)).foregroundColor(.loopGray)
                         .font(.system(size: 12, weight: .bold)).foregroundColor(.loopGray)
                         .padding(.leading, 8)
                         .padding(.leading, 8)
-                } else if let tempRate = viewModel.tempRate {
+                } else if let tempRate = state.tempRate {
                     Text(
                     Text(
                         (numberFormatter.string(from: tempRate as NSNumber) ?? "0") +
                         (numberFormatter.string(from: tempRate as NSNumber) ?? "0") +
                             NSLocalizedString(" U/hr", comment: "Unit per hour with space")
                             NSLocalizedString(" U/hr", comment: "Unit per hour with space")
@@ -105,9 +108,9 @@ extension Home {
                     .padding(.leading, 8)
                     .padding(.leading, 8)
                 }
                 }
 
 
-                if let tempTarget = viewModel.tempTarget {
+                if let tempTarget = state.tempTarget {
                     Text(tempTarget.displayName).font(.caption).foregroundColor(.secondary)
                     Text(tempTarget.displayName).font(.caption).foregroundColor(.secondary)
-                    if viewModel.units == .mmolL {
+                    if state.units == .mmolL {
                         Text(
                         Text(
                             targetFormatter
                             targetFormatter
                                 .string(from: (tempTarget.targetBottom?.asMmolL ?? 0) as NSNumber)!
                                 .string(from: (tempTarget.targetBottom?.asMmolL ?? 0) as NSNumber)!
@@ -120,12 +123,12 @@ extension Home {
                             Text(
                             Text(
                                 targetFormatter
                                 targetFormatter
                                     .string(from: (tempTarget.targetTop?.asMmolL ?? 0) as NSNumber)! +
                                     .string(from: (tempTarget.targetTop?.asMmolL ?? 0) as NSNumber)! +
-                                    " \(viewModel.units.rawValue)"
+                                    " \(state.units.rawValue)"
                             )
                             )
                             .font(.caption)
                             .font(.caption)
                             .foregroundColor(.secondary)
                             .foregroundColor(.secondary)
                         } else {
                         } else {
-                            Text(viewModel.units.rawValue).font(.caption)
+                            Text(state.units.rawValue).font(.caption)
                                 .foregroundColor(.secondary)
                                 .foregroundColor(.secondary)
                         }
                         }
 
 
@@ -138,25 +141,25 @@ extension Home {
                                 .foregroundColor(.secondary)
                                 .foregroundColor(.secondary)
                             Text(
                             Text(
                                 targetFormatter
                                 targetFormatter
-                                    .string(from: (tempTarget.targetTop ?? 0) as NSNumber)! + " \(viewModel.units.rawValue)"
+                                    .string(from: (tempTarget.targetTop ?? 0) as NSNumber)! + " \(state.units.rawValue)"
                             )
                             )
                             .font(.caption)
                             .font(.caption)
                             .foregroundColor(.secondary)
                             .foregroundColor(.secondary)
                         } else {
                         } else {
-                            Text(viewModel.units.rawValue).font(.caption)
+                            Text(state.units.rawValue).font(.caption)
                                 .foregroundColor(.secondary)
                                 .foregroundColor(.secondary)
                         }
                         }
                     }
                     }
                 }
                 }
                 Spacer()
                 Spacer()
-                if let progress = viewModel.bolusProgress {
+                if let progress = state.bolusProgress {
                     Text("Bolusing")
                     Text("Bolusing")
                         .font(.system(size: 12, weight: .bold)).foregroundColor(.insulin)
                         .font(.system(size: 12, weight: .bold)).foregroundColor(.insulin)
                     ProgressView(value: Double(progress))
                     ProgressView(value: Double(progress))
                         .progressViewStyle(BolusProgressViewStyle())
                         .progressViewStyle(BolusProgressViewStyle())
                         .padding(.trailing, 8)
                         .padding(.trailing, 8)
                         .onTapGesture {
                         .onTapGesture {
-                            viewModel.cancelBolus()
+                            state.cancelBolus()
                         }
                         }
                 }
                 }
             }
             }
@@ -195,10 +198,10 @@ extension Home {
                         .font(.system(size: 12, weight: .bold)).foregroundColor(.uam)
                         .font(.system(size: 12, weight: .bold)).foregroundColor(.uam)
                 }
                 }
 
 
-                if let eventualBG = viewModel.eventualBG {
+                if let eventualBG = state.eventualBG {
                     Text(
                     Text(
                         "⇢ " + numberFormatter.string(
                         "⇢ " + 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)
                     .font(.system(size: 12, weight: .bold)).foregroundColor(.secondary)
@@ -219,19 +222,19 @@ extension Home {
                     infoPanal
                     infoPanal
                         .background(Color.backgroundColor)
                         .background(Color.backgroundColor)
                     MainChartView(
                     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))
                     .background(Color.gray.opacity(0.05))
                     // .padding(.bottom)
                     // .padding(.bottom)
@@ -243,7 +246,7 @@ extension Home {
                     ZStack {
                     ZStack {
                         Rectangle().fill(Color.gray.opacity(0.05)).frame(height: 50 + geo.safeAreaInsets.bottom)
                         Rectangle().fill(Color.gray.opacity(0.05)).frame(height: 50 + geo.safeAreaInsets.bottom)
                         HStack {
                         HStack {
-                            Button { viewModel.showModal(for: .addCarbs) }
+                            Button { state.showModal(for: .addCarbs) }
                             label: {
                             label: {
                                 ZStack(alignment: Alignment(horizontal: .trailing, vertical: .bottom)) {
                                 ZStack(alignment: Alignment(horizontal: .trailing, vertical: .bottom)) {
                                     Image("carbs")
                                     Image("carbs")
@@ -252,7 +255,7 @@ extension Home {
                                         .frame(width: 24, height: 24)
                                         .frame(width: 24, height: 24)
                                         .foregroundColor(.loopGreen)
                                         .foregroundColor(.loopGreen)
                                         .padding(8)
                                         .padding(8)
-                                    if let carbsReq = viewModel.carbsRequired {
+                                    if let carbsReq = state.carbsRequired {
                                         Text(numberFormatter.string(from: carbsReq as NSNumber)!)
                                         Text(numberFormatter.string(from: carbsReq as NSNumber)!)
                                             .font(.caption)
                                             .font(.caption)
                                             .foregroundColor(.white)
                                             .foregroundColor(.white)
@@ -262,7 +265,7 @@ extension Home {
                                 }
                                 }
                             }
                             }
                             Spacer()
                             Spacer()
-                            Button { viewModel.showModal(for: .addTempTarget) }
+                            Button { state.showModal(for: .addTempTarget) }
                             label: {
                             label: {
                                 Image("target")
                                 Image("target")
                                     .renderingMode(.template)
                                     .renderingMode(.template)
@@ -271,7 +274,7 @@ extension Home {
                                     .padding(8)
                                     .padding(8)
                             }.foregroundColor(.loopYellow)
                             }.foregroundColor(.loopYellow)
                             Spacer()
                             Spacer()
-                            Button { viewModel.showModal(for: .bolus(waitForDuggestion: false)) }
+                            Button { state.showModal(for: .bolus(waitForDuggestion: false)) }
                             label: {
                             label: {
                                 Image("bolus")
                                 Image("bolus")
                                     .renderingMode(.template)
                                     .renderingMode(.template)
@@ -280,8 +283,8 @@ extension Home {
                                     .padding(8)
                                     .padding(8)
                             }.foregroundColor(.insulin)
                             }.foregroundColor(.insulin)
                             Spacer()
                             Spacer()
-                            if viewModel.allowManualTemp {
-                                Button { viewModel.showModal(for: .manualTempBasal) }
+                            if state.allowManualTemp {
+                                Button { state.showModal(for: .manualTempBasal) }
                                 label: {
                                 label: {
                                     Image("bolus1")
                                     Image("bolus1")
                                         .renderingMode(.template)
                                         .renderingMode(.template)
@@ -291,7 +294,7 @@ extension Home {
                                 }.foregroundColor(.insulin)
                                 }.foregroundColor(.insulin)
                                 Spacer()
                                 Spacer()
                             }
                             }
-                            Button { viewModel.showModal(for: .settings) }
+                            Button { state.showModal(for: .settings) }
                             label: {
                             label: {
                                 Image("settings1")
                                 Image("settings1")
                                     .renderingMode(.template)
                                     .renderingMode(.template)
@@ -306,16 +309,17 @@ extension Home {
                 }
                 }
                 .edgesIgnoringSafeArea(.vertical)
                 .edgesIgnoringSafeArea(.vertical)
             }
             }
+            .onAppear(perform: configureView)
             .navigationTitle("Home")
             .navigationTitle("Home")
             .navigationBarHidden(true)
             .navigationBarHidden(true)
             .ignoresSafeArea(.keyboard)
             .ignoresSafeArea(.keyboard)
             .popup(isPresented: isStatusPopupPresented, alignment: .top, direction: .top) {
             .popup(isPresented: isStatusPopupPresented, alignment: .top, direction: .top) {
                 VStack(alignment: .leading) {
                 VStack(alignment: .leading) {
-                    Text(viewModel.statusTitle).foregroundColor(.white)
+                    Text(state.statusTitle).foregroundColor(.white)
                         .padding(.bottom, 4)
                         .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)
                         Text("Error at \(dateFormatter.string(from: date))").foregroundColor(.white)
                             .padding(.bottom, 4)
                             .padding(.bottom, 4)
                             .padding(.top, 8)
                             .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
 import SwiftUI
 
 
 extension ISFEditor {
 extension ISFEditor {
-    class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: ISFEditorProvider {
+    final class StateModel: BaseStateModel<Provider> {
         @Injected() var settingsManager: SettingsManager!
         @Injected() var settingsManager: SettingsManager!
         @Published var items: [Item] = []
         @Published var items: [Item] = []
         private(set) var autosensISF: Decimal?
         private(set) var autosensISF: Decimal?

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

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


Некоторые файлы не были показаны из-за большого количества измененных файлов