Skip to content

White Label Onboarding

Alan Chu edited this page Feb 5, 2023 · 3 revisions

OBAKit contains prebuilt views for "onboarding" a user, located in the OBAKit/Onboarding folder. These are SwiftUI views that can be displayed standalone or as part of a navigation stack.

Example: OneBusAway.app

sequenceDiagram
  participant AppDelegate as -[AppDelegate applicationReloadRootInterface:]
  AppDelegate-->+OnboardingNavigationController: needsToOnboard?
  AppDelegate->>+OnboardingNavigationController: Present OnboardingNavigationController
  OnboardingNavigationController->>+OnboardingView: Push One-Time Location Permission Page
  OnboardingView->>-OnboardingNavigationController: dismiss()
  OnboardingNavigationController->>+OnboardingView: Push Region Picker Page
  OnboardingView->>-OnboardingNavigationController: dismiss()
  OnboardingNavigationController->>-AppDelegate: Finish by calling OnboardingNavigationController.completion()
  AppDelegate-->+AppDelegate: Present OBAClassicApplicationRootController

Loading

Implementing onboarding

1. Create a custom Onboarding view

Take a look in OBAKit/Onboarding to see if there is already an onboarding view you want. Otherwise, read below on how to create a custom onboarding view.

An onboarding view is a SwiftUI view that conforms to protocol OBAKit.OnboardingView.

To implement OnboardingView, use the below code snippet as guidance:

struct MyCustomOnboardingView: View, OnboardingView {
  @Environment(\.dismiss) var dismissAction
  var dismissBlock: VoidBlock?

  var body: some View {
    Button("Next Step") {
      dismiss()           // This calls `OnboardingView.dismiss()`, which has a default implementation.
    }
  }
}

2. Create an Onboarding class in your app

import SwiftUI
import OBAKit

@objc public class OnboardingNavigationController: UINavigationController {
  @objc public static var needsToOnboard: Bool {
    // A "cheap" method to check whether we should show the user the  Onboarding screen
    // before actually initializing this class.
    return UserDefaults.standard.bool(for: "UserHasOnboarded", default: false)
  }

  var completionHandler: VoidBlock

  // When onboarding is finished, OnboardingNavigationController calls completionHandler.
  @objc public init(completionHandler: @escaping VoidBlock) {
     self.completionHandler = completionHandler
     super.init(nibName: nil, bundle: nil)
  }

  override public func viewDidLoad() {
    super.viewDidLoad()

    let view = MyCustomOnboardingView(dismissBlock: self.completionHandler)
    self.setViewControllers([UIHostingController(rootView: view)])
  }
}

3. Implement in your AppDelegate

In your app's AppDelegate, implement -[AppDelegate applicationReloadRootInterface:]. In the method, check if the user needsToOnboard to show either the Application Controller or the Onboarding Controller.

- (void)applicationReloadRootInterface:(OBAApplication*)application {
    void(^showRootController)(void) = ^{
        self.rootController = [[OBAClassicApplicationRootController alloc] initWithApplication:application];
        self.window.rootViewController = self.rootController;
    };

    if ([OnboardingNavigationController needsToOnboard]) {
        self.window.rootViewController = [[OnboardingNavigationController alloc] initWithCompletion:^{
            showRootController();
            [UIView transitionWithView:self.window duration:0.5 options:UIViewAnimationOptionTransitionFlipFromLeft animations:nil completion:nil];
        }];
    } else {
        showRootController();
    }
}

Example

Take a look at Apps/OneBusAway/AppDelegate.m for an Onboarding example that implements multiple pages.