Explorar el Código

Cleanup for CGMSettingsStateModel

Sam King hace 1 año
padre
commit
814f9cd886

+ 18 - 3
Trio/Sources/Modules/CGMSettings/CGMSettingsStateModel.swift

@@ -4,6 +4,9 @@ import G7SensorKit
 import LoopKitUI
 import SwiftUI
 
+/// For a full description of the events that can happen for the CGM lifecycle, see comment at the top
+/// of HomeStateModel since these are the same events
+
 struct CGMModel: Identifiable, Hashable {
     var id: String
     var type: CGMType
@@ -56,6 +59,8 @@ extension CGMSettings {
         @Published var listOfCGM: [CGMModel] = []
         @Published var url: URL?
 
+        var shouldRunDeleteOnSettingsChange = true
+
         override func subscribe() {
             units = settingsManager.settings.units
             broadcaster.register(SettingsObserver.self, observer: self)
@@ -157,8 +162,15 @@ extension CGMSettings {
 
 extension CGMSettings.StateModel: CompletionDelegate {
     func completionNotifyingDidComplete(_: CompletionNotifying) {
-        if fetchGlucoseManager.cgmGlucoseSourceType == .none {
-            cgmCurrent = cgmDefaultModel
+        Task {
+            // this sleep is because this event and cgmManagerWantsDeletion
+            // are called in parallel.
+            try await Task.sleep(for: .seconds(0.1))
+            await MainActor.run {
+                if fetchGlucoseManager.cgmGlucoseSourceType == .none {
+                    cgmCurrent = cgmDefaultModel
+                }
+            }
         }
         shouldDisplayCGMSetupSheet = false
     }
@@ -194,13 +206,16 @@ extension CGMSettings.StateModel: SettingsObserver {
         // but both will call deleteGlucoseSource on the fetchGlucoseManager
         // so we listen for changes to the cgm setting and update our internal
         // state accordingly
-        if settingsManager.settings.cgm == .none {
+        if settingsManager.settings.cgm == .none, shouldRunDeleteOnSettingsChange {
+            shouldRunDeleteOnSettingsChange = false
             cgmCurrent = cgmDefaultModel
             DispatchQueue.main.async {
                 self.broadcaster.notify(GlucoseObserver.self, on: .main) {
                     $0.glucoseDidUpdate([])
                 }
             }
+        } else {
+            shouldRunDeleteOnSettingsChange = true
         }
     }
 }

+ 38 - 0
Trio/Sources/Modules/Home/HomeStateModel.swift

@@ -7,6 +7,44 @@ import Observation
 import SwiftDate
 import SwiftUI
 
+/// Notes on the CGM lifecycle:
+/// There are two classes of CGM devices: plugins and non-plugins. Plugins are implemented using
+/// LoopKit APIs and include most hardware CGMs like Dexcom G6, G7, Libre, and so on. Non-plugins
+/// drivers are implemented directly in Trio, and include the CGM Simulator and Nightscout CGM. For
+/// these different CGMs, there are a few different events, handled in different places, that happen to
+/// signify a change in the CGM lifecycle.
+///
+/// Both:
+/// - addCGM function invocation: Called by the UI in response to a user clicking the "add CGM" button
+///
+/// Non-plugins only:
+/// - deleteCGM function invocation: Called by the CGM View in response to a user clicking the "delete CGM" button
+///
+/// Plugins only:
+/// - completionNotifyingDidComplete: Called by the CGM driver to signify that Trio should close its UIViewController
+/// - cgmManagerOnboarding didCreateCGMManager: Called by the CGM driver after adding a new CGM
+/// - cgmManagerWantsDeletion: Called by the CGM driver when the user asks to delete a CGM
+/// There are no ordering constraints between completionNotifyingDidComplete and the other two
+/// Plugin events (it's up to the implementation of each individual driver). For example, the G7 driver invokes
+/// cgmManagerWantsDeletion on the delegate's queue while calling completionNotifyingDidComplete in parallel
+/// on the main queue.
+///
+/// In additinon to having different events for different types of CGMs, the handling of these events is spread out
+/// across various state managers, like HomeStateModel, CGMSettingsStateModel, and PluginSource.
+///
+/// There is CGM state in the HomeStateModel and CGMSettingsStateModel, FetchGlucoseManager, and
+/// SettingsManger
+///
+/// The flow for adding a CGM:
+/// - Non-plugin: addCGM (considered onboarded at this point)
+/// - Plugin: addCGM -> cgmManagerOnboarding (after success)
+///
+/// For deleting a CGM:
+/// - Non-plugin: deleteCGM (in HomeStateModel and CGMSettingsStateModel)
+/// - Plugin: cgmManagerWantsDeletion (in PluginSource)
+/// Then, both non-plugin and plugin:  set settings.cgm (in FetchGlucoseManager) ->
+///     settingsDidChange (in HomeStateModel and CGMSettingsStateModel)
+
 extension Home {
     @Observable final class StateModel: BaseStateModel<Provider> {
         @ObservationIgnored @Injected() var broadcaster: Broadcaster!