HomeStateModel+CGM.swift 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  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. func completionNotifyingDidComplete(_ notifying: CompletionNotifying) {
  41. debug(.service, "Completion fired by: \(type(of: notifying))")
  42. Task {
  43. // this sleep is because this event and cgmManagerWantsDeletion
  44. // are called in parallel.
  45. try await Task.sleep(for: .seconds(0.2))
  46. await MainActor.run {
  47. if fetchGlucoseManager.cgmGlucoseSourceType == .none {
  48. cgmCurrent = cgmDefaultModel
  49. }
  50. }
  51. }
  52. shouldDisplayCGMSetupSheet = false
  53. }
  54. }
  55. extension Home.StateModel: CGMManagerOnboardingDelegate {
  56. func cgmManagerOnboarding(didCreateCGMManager manager: LoopKitUI.CGMManagerUI) {
  57. settingsManager.settings.cgm = cgmCurrent.type
  58. settingsManager.settings.cgmPluginIdentifier = cgmCurrent.id
  59. fetchGlucoseManager.updateGlucoseSource(
  60. cgmGlucoseSourceType: cgmCurrent.type,
  61. cgmGlucosePluginId: cgmCurrent.id,
  62. newManager: manager
  63. )
  64. DispatchQueue.main.async {
  65. self.broadcaster.notify(GlucoseObserver.self, on: .main) {
  66. $0.glucoseDidUpdate([])
  67. }
  68. }
  69. }
  70. func cgmManagerOnboarding(didOnboardCGMManager _: LoopKitUI.CGMManagerUI) {
  71. // nothing to do
  72. }
  73. }