Skip to content

Commit

Permalink
[LOOP-3375] Onboarding service integration (#388)
Browse files Browse the repository at this point in the history
- https://tidepool.atlassian.net/browse/LOOP-3375
- Update to latest CGMManager, PumpManager, and Service protocols
- Remove special case of pump manager onboarding
- OnboardingManager handles created, but not yet onboarded, view controllers
  • Loading branch information
Darin Krauss authored Apr 20, 2021
1 parent 56dddaa commit 4ea6662
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 61 deletions.
2 changes: 1 addition & 1 deletion Loop/Extensions/DeviceDataManager+DeviceStatus.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ extension DeviceDataManager {
{
return .openAppURL(url)
} else if let cgmManagerUI = (cgmManager as? CGMManagerUI) {
return .presentViewController(cgmManagerUI.settingsViewController(for: displayGlucoseUnitObservable, bluetoothProvider: bluetoothProvider, colorPalette: .default))
return .presentViewController(cgmManagerUI.settingsViewController(bluetoothProvider: bluetoothProvider, displayGlucoseUnitObservable: displayGlucoseUnitObservable, colorPalette: .default))
} else {
return .setupNewCGM
}
Expand Down
43 changes: 17 additions & 26 deletions Loop/Managers/DeviceDataManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ final class DeviceDataManager {
return pluginManager.availablePumpManagers + availableStaticPumpManagers
}

func setupPumpManager(withIdentifier identifier: String, initialSettings settings: PumpManagerSetupSettings) -> Swift.Result<SetupUIResult<UIViewController & PumpManagerCreateNotifying & PumpManagerOnboardNotifying & CompletionNotifying, PumpManager>, Error> {
func setupPumpManager(withIdentifier identifier: String, initialSettings settings: PumpManagerSetupSettings) -> Swift.Result<SetupUIResult<PumpManagerViewController, PumpManager>, Error> {
switch setupPumpManagerUI(withIdentifier: identifier, initialSettings: settings) {
case .failure(let error):
return .failure(error)
Expand All @@ -380,17 +380,15 @@ final class DeviceDataManager {

struct UnknownPumpManagerIdentifierError: Error {}

func setupPumpManagerUI(withIdentifier identifier: String, initialSettings settings: PumpManagerSetupSettings) -> Swift.Result<SetupUIResult<UIViewController & PumpManagerCreateNotifying & PumpManagerOnboardNotifying & CompletionNotifying, PumpManagerUI>, Error> {
func setupPumpManagerUI(withIdentifier identifier: String, initialSettings settings: PumpManagerSetupSettings) -> Swift.Result<SetupUIResult<PumpManagerViewController, PumpManagerUI>, Error> {
guard let pumpManagerUIType = pumpManagerTypeByIdentifier(identifier) else {
return .failure(UnknownPumpManagerIdentifierError())
}

let result = pumpManagerUIType.setupViewController(initialSettings: settings, bluetoothProvider: bluetoothProvider, colorPalette: .default)
if case .createdAndOnboarded(let pumpManagerUI) = result {
if let basalRateSchedule = loopManager.basalRateSchedule {
pumpManagerUI.syncBasalRateSchedule(items: basalRateSchedule.items, completion: { _ in })
}
self.pumpManager = pumpManagerUI
pumpManagerOnboarding(didCreatePumpManager: pumpManagerUI)
pumpManagerOnboarding(didOnboardPumpManager: pumpManagerUI, withFinalSettings: settings)
}

return .success(result)
Expand Down Expand Up @@ -455,7 +453,7 @@ final class DeviceDataManager {
return availableCGMManagers
}

func setupCGMManager(withIdentifier identifier: String) -> Swift.Result<SetupUIResult<UIViewController & CGMManagerCreateNotifying & CGMManagerOnboardNotifying & CompletionNotifying, CGMManager>, Error> {
func setupCGMManager(withIdentifier identifier: String) -> Swift.Result<SetupUIResult<CGMManagerViewController, CGMManager>, Error> {
if let cgmManager = setupCGMManagerFromPumpManager(withIdentifier: identifier) {
return .success(.createdAndOnboarded(cgmManager))
}
Expand All @@ -475,14 +473,15 @@ final class DeviceDataManager {

struct UnknownCGMManagerIdentifierError: Error {}

fileprivate func setupCGMManagerUI(withIdentifier identifier: String) -> Swift.Result<SetupUIResult<UIViewController & CGMManagerCreateNotifying & CGMManagerOnboardNotifying & CompletionNotifying, CGMManagerUI>, Error> {
fileprivate func setupCGMManagerUI(withIdentifier identifier: String) -> Swift.Result<SetupUIResult<CGMManagerViewController, CGMManagerUI>, Error> {
guard let cgmManagerUIType = cgmManagerTypeByIdentifier(identifier) else {
return .failure(UnknownCGMManagerIdentifierError())
}

let result = cgmManagerUIType.setupViewController(bluetoothProvider: bluetoothProvider, colorPalette: .default)
let result = cgmManagerUIType.setupViewController(bluetoothProvider: bluetoothProvider, displayGlucoseUnitObservable: displayGlucoseUnitObservable, colorPalette: .default)
if case .createdAndOnboarded(let cgmManagerUI) = result {
self.cgmManager = cgmManagerUI
cgmManagerOnboarding(didCreateCGMManager: cgmManagerUI)
cgmManagerOnboarding(didOnboardCGMManager: cgmManagerUI)
}

return .success(result)
Expand Down Expand Up @@ -838,19 +837,15 @@ extension DeviceDataManager: CGMManagerDelegate {
}
}

// MARK: - CGMManagerCreateDelegate
// MARK: - CGMManagerOnboardingDelegate

extension DeviceDataManager: CGMManagerCreateDelegate {
func cgmManagerCreateNotifying(didCreateCGMManager cgmManager: CGMManagerUI) {
extension DeviceDataManager: CGMManagerOnboardingDelegate {
func cgmManagerOnboarding(didCreateCGMManager cgmManager: CGMManagerUI) {
log.default("CGM manager with identifier '%{public}@' created", cgmManager.managerIdentifier)
self.cgmManager = cgmManager
}
}

// MARK: - CGMManagerOnboardDelegate

extension DeviceDataManager: CGMManagerOnboardDelegate {
func cgmManagerOnboardNotifying(didOnboardCGMManager cgmManager: CGMManagerUI) {
func cgmManagerOnboarding(didOnboardCGMManager cgmManager: CGMManagerUI) {
precondition(cgmManager.isOnboarded)
log.default("CGM manager with identifier '%{public}@' onboarded", cgmManager.managerIdentifier)
}
Expand Down Expand Up @@ -1049,19 +1044,15 @@ extension DeviceDataManager: PumpManagerDelegate {
}
}

// MARK: - PumpManagerCreateDelegate
// MARK: - PumpManagerOnboardingDelegate

extension DeviceDataManager: PumpManagerCreateDelegate {
func pumpManagerCreateNotifying(didCreatePumpManager pumpManager: PumpManagerUI) {
extension DeviceDataManager: PumpManagerOnboardingDelegate {
func pumpManagerOnboarding(didCreatePumpManager pumpManager: PumpManagerUI) {
log.default("Pump manager with identifier '%{public}@' created", pumpManager.managerIdentifier)
self.pumpManager = pumpManager
}
}

// MARK: - PumpManagerOnboardDelegate

extension DeviceDataManager: PumpManagerOnboardDelegate {
func pumpManagerOnboardNotifying(didOnboardPumpManager pumpManager: PumpManagerUI, withFinalSettings settings: PumpManagerSetupSettings) {
func pumpManagerOnboarding(didOnboardPumpManager pumpManager: PumpManagerUI, withFinalSettings settings: PumpManagerSetupSettings) {
precondition(pumpManager.isOnboarded)
log.default("Pump manager with identifier '%{public}@' onboarded", pumpManager.managerIdentifier)

Expand Down
74 changes: 61 additions & 13 deletions Loop/Managers/OnboardingManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,9 @@ class OnboardingManager {

private func displayOnboarding(_ onboarding: OnboardingUI) {
var onboardingViewController = onboarding.onboardingViewController(onboardingProvider: self, displayGlucoseUnitObservable: deviceDataManager.displayGlucoseUnitObservable, colorPalette: .default)
onboardingViewController.cgmManagerCreateDelegate = deviceDataManager
onboardingViewController.cgmManagerOnboardDelegate = deviceDataManager
onboardingViewController.pumpManagerCreateDelegate = deviceDataManager
onboardingViewController.pumpManagerOnboardDelegate = deviceDataManager
onboardingViewController.serviceCreateDelegate = servicesManager
onboardingViewController.serviceOnboardDelegate = servicesManager
onboardingViewController.cgmManagerOnboardingDelegate = deviceDataManager
onboardingViewController.pumpManagerOnboardingDelegate = deviceDataManager
onboardingViewController.serviceOnboardingDelegate = servicesManager
onboardingViewController.completionDelegate = self

windowProvider?.window?.rootViewController = onboardingViewController
Expand Down Expand Up @@ -267,8 +264,23 @@ extension OnboardingManager: CGMManagerProvider {

var availableCGMManagers: [CGMManagerDescriptor] { deviceDataManager.availableCGMManagers }

func setupCGMManager(withIdentifier identifier: String) -> Swift.Result<SetupUIResult<UIViewController & CGMManagerCreateNotifying & CGMManagerOnboardNotifying & CompletionNotifying, CGMManager>, Error> {
return deviceDataManager.setupCGMManager(withIdentifier: identifier)
func onboardCGMManager(withIdentifier identifier: String) -> Swift.Result<OnboardingResult<CGMManagerViewController, CGMManager>, Error> {
guard let cgmManager = deviceDataManager.cgmManager else {
return deviceDataManager.setupCGMManager(withIdentifier: identifier)
}
guard cgmManager.managerIdentifier == identifier else {
return .failure(OnboardingError.invalidState)
}

if cgmManager.isOnboarded {
return .success(.createdAndOnboarded(cgmManager))
}

guard let cgmManagerUI = cgmManager as? CGMManagerUI else {
return .failure(OnboardingError.invalidState)
}

return .success(.userInteractionRequired(cgmManagerUI.settingsViewController(bluetoothProvider: self, displayGlucoseUnitObservable: deviceDataManager.displayGlucoseUnitObservable, colorPalette: .default)))
}
}

Expand All @@ -279,8 +291,19 @@ extension OnboardingManager: PumpManagerProvider {

var availablePumpManagers: [PumpManagerDescriptor] { deviceDataManager.availablePumpManagers }

func setupPumpManager(withIdentifier identifier: String, initialSettings settings: PumpManagerSetupSettings) -> Swift.Result<SetupUIResult<UIViewController & PumpManagerCreateNotifying & PumpManagerOnboardNotifying & CompletionNotifying, PumpManager>, Error> {
return deviceDataManager.setupPumpManager(withIdentifier: identifier, initialSettings: settings)
func onboardPumpManager(withIdentifier identifier: String, initialSettings settings: PumpManagerSetupSettings) -> Swift.Result<OnboardingResult<PumpManagerViewController, PumpManager>, Error> {
guard let pumpManager = deviceDataManager.pumpManager else {
return deviceDataManager.setupPumpManager(withIdentifier: identifier, initialSettings: settings)
}
guard pumpManager.managerIdentifier == identifier else {
return .failure(OnboardingError.invalidState)
}

if pumpManager.isOnboarded {
return .success(.createdAndOnboarded(pumpManager))
}

return .success(.userInteractionRequired(pumpManager.settingsViewController(bluetoothProvider: self, colorPalette: .default)))
}
}

Expand All @@ -291,15 +314,27 @@ extension OnboardingManager: ServiceProvider {

var availableServices: [ServiceDescriptor] { servicesManager.availableServices }

func setupService(withIdentifier identifier: String) -> Swift.Result<SetupUIResult<UIViewController & ServiceCreateNotifying & ServiceOnboardNotifying & CompletionNotifying, Service>, Error> {
return servicesManager.setupService(withIdentifier: identifier)
func onboardService(withIdentifier identifier: String) -> Swift.Result<OnboardingResult<ServiceViewController, Service>, Error> {
guard let service = activeServices.first(where: { $0.serviceIdentifier == identifier }) else {
return servicesManager.setupService(withIdentifier: identifier)
}

if service.isOnboarded {
return .success(.createdAndOnboarded(service))
}

guard let serviceUI = service as? ServiceUI else {
return .failure(OnboardingError.invalidState)
}

return .success(.userInteractionRequired(serviceUI.settingsViewController(colorPalette: .default)))
}
}

// MARK: - OnboardingProvider

extension OnboardingManager: OnboardingProvider {
var allowSkipOnboarding: Bool { FeatureFlags.mockTherapySettingsEnabled } // NOTE: SKIP ONBOARDING - DEBUG AND TEST ONLY
var allowDebugFeatures: Bool { FeatureFlags.mockTherapySettingsEnabled } // NOTE: DEBUG FEATURES - DEBUG AND TEST ONLY
}

// MARK: - OnboardingUI
Expand All @@ -315,6 +350,19 @@ fileprivate extension OnboardingUI {
}
}

// MARK: - OnboardingError

enum OnboardingError: LocalizedError {
case invalidState

var errorDescription: String? {
switch self {
case .invalidState:
return NSLocalizedString("An unexpected onboarding error state occurred.", comment: "Invalid onboarding state")
}
}
}

// MARK: - UserDefaults

fileprivate extension UserDefaults {
Expand Down
19 changes: 8 additions & 11 deletions Loop/Managers/ServicesManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class ServicesManager {
return pluginManager.availableServices + availableStaticServices
}

func setupService(withIdentifier identifier: String) -> Swift.Result<SetupUIResult<UIViewController & ServiceCreateNotifying & ServiceOnboardNotifying & CompletionNotifying, Service>, Error> {
func setupService(withIdentifier identifier: String) -> Swift.Result<SetupUIResult<ServiceViewController, Service>, Error> {
switch setupServiceUI(withIdentifier: identifier) {
case .failure(let error):
return .failure(error)
Expand All @@ -60,14 +60,15 @@ class ServicesManager {

struct UnknownServiceIdentifierError: Error {}

fileprivate func setupServiceUI(withIdentifier identifier: String) -> Swift.Result<SetupUIResult<UIViewController & ServiceCreateNotifying & ServiceOnboardNotifying & CompletionNotifying, ServiceUI>, Error> {
fileprivate func setupServiceUI(withIdentifier identifier: String) -> Swift.Result<SetupUIResult<ServiceViewController, ServiceUI>, Error> {
guard let serviceUIType = serviceUITypeByIdentifier(identifier) else {
return .failure(UnknownServiceIdentifierError())
}

let result = serviceUIType.setupViewController(colorPalette: .default)
if case .createdAndOnboarded(let serviceUI) = result {
addActiveService(serviceUI)
serviceOnboarding(didCreateService: serviceUI)
serviceOnboarding(didOnboardService: serviceUI)
}

return .success(result)
Expand Down Expand Up @@ -177,19 +178,15 @@ extension ServicesManager: ServiceDelegate {
}
}

// MARK: - ServiceCreateDelegate
// MARK: - ServiceOnboardingDelegate

extension ServicesManager: ServiceCreateDelegate {
func serviceCreateNotifying(didCreateService service: Service) {
extension ServicesManager: ServiceOnboardingDelegate {
func serviceOnboarding(didCreateService service: Service) {
log.default("Service with identifier '%{public}@' created", service.serviceIdentifier)
addActiveService(service)
}
}

// MARK: - ServiceCreateDelegate

extension ServicesManager: ServiceOnboardDelegate {
func serviceOnboardNotifying(didOnboardService service: Service) {
func serviceOnboarding(didOnboardService service: Service) {
precondition(service.isOnboarded)
log.default("Service with identifier '%{public}@' onboarded", service.serviceIdentifier)
}
Expand Down
17 changes: 7 additions & 10 deletions Loop/View Controllers/StatusTableViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1400,7 +1400,7 @@ final class StatusTableViewController: LoopChartsTableViewController {
// assert?
return
}
settingsViewController.pumpManagerOnboardDelegate = deviceManager
settingsViewController.pumpManagerOnboardingDelegate = deviceManager
settingsViewController.completionDelegate = self
show(settingsViewController, sender: self)
}
Expand All @@ -1411,8 +1411,8 @@ final class StatusTableViewController: LoopChartsTableViewController {
return
}

var settings = cgmManager.settingsViewController(for: deviceManager.displayGlucoseUnitObservable, bluetoothProvider: deviceManager.bluetoothProvider, colorPalette: .default)
settings.cgmManagerOnboardDelegate = deviceManager
var settings = cgmManager.settingsViewController(bluetoothProvider: deviceManager.bluetoothProvider, displayGlucoseUnitObservable: deviceManager.displayGlucoseUnitObservable, colorPalette: .default)
settings.cgmManagerOnboardingDelegate = deviceManager
settings.completionDelegate = self
show(settings, sender: self)
}
Expand Down Expand Up @@ -1892,8 +1892,7 @@ extension StatusTableViewController {
case .success(let success):
switch success {
case .userInteractionRequired(var setupViewController):
setupViewController.cgmManagerCreateDelegate = deviceManager
setupViewController.cgmManagerOnboardDelegate = deviceManager
setupViewController.cgmManagerOnboardingDelegate = deviceManager
setupViewController.completionDelegate = self
show(setupViewController, sender: self)
case .createdAndOnboarded:
Expand All @@ -1914,8 +1913,7 @@ extension StatusTableViewController {
case .success(let success):
switch success {
case .userInteractionRequired(var setupViewController):
setupViewController.pumpManagerCreateDelegate = deviceManager
setupViewController.pumpManagerOnboardDelegate = deviceManager
setupViewController.pumpManagerOnboardingDelegate = deviceManager
setupViewController.completionDelegate = self
show(setupViewController, sender: self)
case .createdAndOnboarded:
Expand Down Expand Up @@ -1989,8 +1987,7 @@ extension StatusTableViewController: ServicesViewModelDelegate {
case .success(let success):
switch success {
case .userInteractionRequired(var setupViewController):
setupViewController.serviceCreateDelegate = deviceManager.servicesManager
setupViewController.serviceOnboardDelegate = deviceManager.servicesManager
setupViewController.serviceOnboardingDelegate = deviceManager.servicesManager
setupViewController.completionDelegate = self
show(setupViewController, sender: self)
case .createdAndOnboarded:
Expand All @@ -2008,7 +2005,7 @@ extension StatusTableViewController: ServicesViewModelDelegate {

fileprivate func showServiceSettings(_ serviceUI: ServiceUI) {
var settingsViewController = serviceUI.settingsViewController(colorPalette: .default)
settingsViewController.serviceOnboardDelegate = deviceManager.servicesManager
settingsViewController.serviceOnboardingDelegate = deviceManager.servicesManager
settingsViewController.completionDelegate = self
show(settingsViewController, sender: self)
}
Expand Down

0 comments on commit 4ea6662

Please sign in to comment.