HomeStateModel+CGM.swift 3.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. import LoopKitUI
  2. /// Notes on the CGM lifecycle:
  3. /// There are two classes of CGM devices: plugins and non-plugins. Plugins are implemented using
  4. /// LoopKit APIs and include most hardware CGMs like Dexcom G6, G7, Libre, and so on. Non-plugins
  5. /// drivers are implemented directly in Trio, and include the CGM Simulator and Nightscout CGM. For
  6. /// these different CGMs, there are a few different events, handled in different places, that happen to
  7. /// signify a change in the CGM lifecycle.
  8. ///
  9. /// Both:
  10. /// - addCGM function invocation: Called by the UI in response to a user clicking the "add CGM" button
  11. ///
  12. /// Non-plugins only:
  13. /// - deleteCGM function invocation: Called by the CGM View in response to a user clicking the "delete CGM" button
  14. ///
  15. /// Plugins only:
  16. /// - completionNotifyingDidComplete: Called by the CGM driver to signify that Trio should close its UIViewController
  17. /// - cgmManagerOnboarding didCreateCGMManager: Called by the CGM driver after adding a new CGM
  18. /// - cgmManagerWantsDeletion: Called by the CGM driver when the user asks to delete a CGM
  19. /// There are no ordering constraints between completionNotifyingDidComplete and the other two
  20. /// Plugin events (it's up to the implementation of each individual driver). For example, the G7 driver invokes
  21. /// cgmManagerWantsDeletion on the delegate's queue while calling completionNotifyingDidComplete in parallel
  22. /// on the main queue.
  23. ///
  24. /// In additinon to having different events for different types of CGMs, the handling of these events is spread out
  25. /// across various state managers, like HomeStateModel, CGMSettingsStateModel, and PluginSource.
  26. ///
  27. /// There is CGM state in the HomeStateModel and CGMSettingsStateModel, FetchGlucoseManager, and
  28. /// SettingsManger
  29. ///
  30. /// The flow for adding a CGM:
  31. /// - Non-plugin: addCGM (considered onboarded at this point)
  32. /// - Plugin: addCGM -> cgmManagerOnboarding (after success)
  33. ///
  34. /// For deleting a CGM:
  35. /// - Non-plugin: deleteCGM (in HomeStateModel and CGMSettingsStateModel)
  36. /// - Plugin: cgmManagerWantsDeletion (in PluginSource)
  37. /// Then, both non-plugin and plugin: set settings.cgm (in FetchGlucoseManager) ->
  38. /// settingsDidChange (in HomeStateModel and CGMSettingsStateModel)
  39. extension Home.StateModel: CompletionDelegate {
  40. /// This completion handler is called by both the CGM and the pump
  41. func completionNotifyingDidComplete(_ notifying: CompletionNotifying) {
  42. debug(.service, "Completion fired by: \(type(of: notifying))")
  43. Task {
  44. // this sleep is because this event and cgmManagerWantsDeletion
  45. // are called in parallel.
  46. try await Task.sleep(for: .seconds(0.2))
  47. await MainActor.run {
  48. if fetchGlucoseManager.cgmGlucoseSourceType == .none {
  49. cgmCurrent = cgmDefaultModel
  50. }
  51. }
  52. }
  53. shouldDisplayCGMSetupSheet = false
  54. shouldDisplayPumpSetupSheet = false
  55. }
  56. }
  57. extension Home.StateModel: CGMManagerOnboardingDelegate {
  58. func cgmManagerOnboarding(didCreateCGMManager manager: LoopKitUI.CGMManagerUI) {
  59. settingsManager.settings.cgm = cgmCurrent.type
  60. settingsManager.settings.cgmPluginIdentifier = cgmCurrent.id
  61. fetchGlucoseManager.updateGlucoseSource(
  62. cgmGlucoseSourceType: cgmCurrent.type,
  63. cgmGlucosePluginId: cgmCurrent.id,
  64. newManager: manager
  65. )
  66. DispatchQueue.main.async {
  67. self.broadcaster.notify(GlucoseObserver.self, on: .main) {
  68. $0.glucoseDidUpdate([])
  69. }
  70. }
  71. }
  72. func cgmManagerOnboarding(didOnboardCGMManager _: LoopKitUI.CGMManagerUI) {
  73. // nothing to do
  74. }
  75. }