Last active
February 7, 2021 19:42
-
-
Save lamprosg/e62b1fef70e81450abde2ab39e80431d to your computer and use it in GitHub Desktop.
(iOS) Onboarding flow
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// The list of the onboarding steps | |
enum OnboardingStep: String { | |
case overview | |
case documentScan | |
case saveAndLogin | |
} | |
/// The onboarding errors | |
/// | |
/// - failed: Onboarding failed with the described error | |
enum OnboardingError: Error { | |
case failed(String) | |
} | |
/// Onboarding step coordinator | |
class OnboardingCoordinator { | |
private weak var navigation: UINavigationController! | |
private var stepManager: OnboardingStepManager! | |
var steps: [OnboardingStep] = [] | |
/// OnboardingCoordinator initializer | |
/// | |
/// - Parameter navigation: The navigation controller where steps will be pushed | |
/// - data: Any onboarding data if available | |
init(navigation:UINavigationController, data: OnboardingData? = nil) { | |
super.init() | |
self.navigation = navigation | |
self.stepManager = OnboardingStepManager(navigation: navigation, coordinator: self, data: data) | |
self.setUpSteps() | |
//Start with 1st step if available | |
if let firstStep = self.steps.first { | |
self.show(step: firstStep) | |
} | |
} | |
/// This should not be used | |
private override init() {} | |
/************************************=**/ | |
/// Set up your steps here | |
private func setUpSteps() -> Void { | |
self.steps = [.overview, //Includes .editLoanDetails which is added in real time based on the user's decision to edit them | |
.documentScan , //Passport VC. Shows capture camera VC too | |
.saveAndLogin | |
] | |
updateSignatureStepFromSettings() | |
} | |
private func updateSignatureStepFromSettings() { | |
let showSignature: Bool = UserDefaults.standard.bool(forKey: "showSignature") | |
if !showSignature { | |
let signatureIndex = self.steps.firstIndex(of: .signature) | |
if let index = signatureIndex { | |
self.steps.remove(at: index) | |
} | |
} | |
} | |
/************************************=**/ | |
/// Starts the onboarding flow | |
private func show(step:OnboardingStep) -> Void { | |
self.stepManager.currentStep = self.getStepIndex(for: step) | |
self.getMethodFor(step: step)() { [weak self] (data: OnboardingData?) in | |
guard let this = self else { return } | |
this.stepManager.data = data?.copy() as? OnboardingData | |
if this.stepManager.currentStep == (this.steps.count-1) { | |
let firstVC = this.navigation.viewControllers.first | |
if let loginVC: LoginViewController = firstVC as? LoginViewController { | |
loginVC.startDemo() | |
} | |
} | |
else { | |
this.stepManager.currentStep += 1 | |
this.show(step: this.steps[this.stepManager.currentStep]) | |
} | |
} | |
} | |
private func getMethodFor(step: OnboardingStep) -> (@escaping(_ data: OnboardingData?) -> Void) -> Void { | |
switch step { | |
case .overview: | |
return self.stepManager.showOverview | |
case .documentScan: | |
return self.stepManager.showDocumentScan | |
case .saveAndLogin: | |
return self.stepManager.showSaveAndLogin | |
} | |
} | |
private func getStepIndex(for step: OnboardingStep) -> Int { | |
guard let index = self.steps.firstIndex(of: step) else { | |
return 0 | |
} | |
return index | |
} | |
func update(stepIndex:Int) -> Void { | |
self.stepManager.currentStep = stepIndex | |
} | |
func popViewController(with data: OnboardingData?) -> Void { | |
self.stepManager.data = data?.copy() as? OnboardingData | |
self.navigation.popViewController(animated: true) | |
} | |
} | |
// MARK: - Step manager | |
/// Onboarding step | |
class OnboardingStepManager { | |
private weak var navigation: UINavigationController! | |
var data: OnboardingData? | |
private weak var coordinator: OnboardingCoordinator? | |
var currentStep:Int = 0 | |
/// Convenience OnboardingStepManager initializer | |
/// | |
/// - Parameters: | |
/// - navigation: The navigation controller where steps will be pushed | |
/// - coordinator: The onboarding coordinator | |
/// - data: The onboarding data model if any | |
init(navigation:UINavigationController, coordinator: OnboardingCoordinator, data: OnboardingData? = nil) { | |
self.navigation = navigation | |
self.coordinator = coordinator | |
self.data = data?.copy() as? OnboardingData | |
} | |
/// This should not be used | |
private init() {} | |
/// Welcome / overview step | |
func showOverview(completion:@escaping(_ data: OnboardingData?) -> Void) -> Void { | |
let welcomeVC: WelcomeVC = WelcomeVC() | |
welcomeVC.stepFinishedWithSuccess = completion | |
welcomeVC.data = self.data?.copy() as? OnboardingData | |
welcomeVC.coordinator = self.coordinator | |
welcomeVC.currentStep = currentStep | |
self.navigation.pushViewController(welcomeVC, animated: true) | |
} | |
func showDocumentScan(completion: @escaping(_ data: OnboardingData?) -> Void) -> Void { | |
let documentScanVC: DocumentScanVC = DocumentScanVC() | |
documentScanVC.stepFinishedWithSuccess = completion | |
documentScanVC.data = self.data?.copy() as? OnboardingData | |
documentScanVC.coordinator = self.coordinator | |
documentScanVC.currentStep = currentStep | |
self.navigation.pushViewController(documentScanVC, animated: true) | |
} | |
func showSaveAndLogin(completion: @escaping(_ data: OnboardingData?) -> Void) -> Void { | |
let saveAndLoginVC: SaveAndLoginVC = aveAndLoginVC() | |
saveAndLoginVC.stepFinishedWithSuccess = completion | |
saveAndLoginVC.data = self.data?.copy() as? OnboardingData | |
saveAndLoginVC.coordinator = self.coordinator | |
saveAndLoginVC.currentStep = currentStep | |
self.navigation.pushViewController(saveAndLoginVC, animated: true) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment