Group Group Group Group Group Group Group Group Group

Dependency injection along with storyboards

#1

Really glad about the depth of the book as far as i have read .Kudos to the Authors & RW team. I am eagerly waiting for the release of the other chapters.I feel this is an advanced book & use of Rxswift is justified.

One thing that put me off is that whole UI is built through code which I think is really cumbersome. I think we need to take advantage of the Storyboard or XIB to build the UI which is faster in my opinion & more visual. I have seen Swinject allowing DI along with storyboards. Is it possible to inlcude a section to depict this?

#2

Hi @pragm4tic, I’m really glad you’re happy with the depth of the book! Thanks for reaching out. We wanted to include a bit about Storyboards/IB in the Dependency Injection chapter, but we just didn’t get the chance. This is a topic I’d really like to add to the book in future editions. At the moment, we are focusing on adding more architecture chapters. We’ve gotten a lot of requests for including more architecture patterns. In the meantime, here are my thoughts and some suggestions.

The book’s hand-written approach to Dependency Injection can be used with Storyboard backed view controllers. The trick is to replace the view controller initializers with implicitly unwrapped optional properties for the view controller dependencies. For example, instead of this:

public class MainViewController: NiblessViewController {

  // MARK: - Properties
  // View Model
  let viewModel: MainViewModel

  // Child View Controllers
  let launchViewController: LaunchViewController
  var signedInViewController: SignedInViewController?
  var onboardingViewController: OnboardingViewController?

  // State
  let disposeBag = DisposeBag()

  // Factories
  let makeOnboardingViewController: () -> OnboardingViewController
  let makeSignedInViewController: (UserSession) -> SignedInViewController

  // MARK: - Methods
  public init(viewModel: MainViewModel,
              launchViewController: LaunchViewController,
              onboardingViewControllerFactory: @escaping () -> OnboardingViewController,
              signedInViewControllerFactory: @escaping (UserSession) -> SignedInViewController) {
    self.viewModel = viewModel
    self.launchViewController = launchViewController
    self.makeOnboardingViewController = onboardingViewControllerFactory
    self.makeSignedInViewController = signedInViewControllerFactory
    super.init()
  }

...

You could implement the view controller like this:

public class MainViewController: UIViewController {

  // MARK: - Properties
  // View Model
  var viewModel: MainViewModel!

  // Child View Controllers
  var launchViewController: LaunchViewController!
  var signedInViewController: SignedInViewController?
  var onboardingViewController: OnboardingViewController?

  // State
  let disposeBag = DisposeBag()

  // Factories
  var makeOnboardingViewController: (() -> OnboardingViewController)!
  var makeSignedInViewController: ((UserSession) -> SignedInViewController)!

  // MARK: - Methods

...

The reason I prefer to build UI in code is because of the tradeoffs we have to make above. In the second example above, we had to convert all the constant properties into variable properties. We also had to use implicitly unwrapped optionals which means we lose some compile time safety / the code could crash at runtime if a dependency is not set. I like being able to implement view controller initializers and guarantee that a view controller has all the objects it needs to function. You might get enough value out of building UIs in Storyboards that you’re OK with this tradeoff and that’s completely valid. I recommend trying both approaches to get a feel for the difference. I’ve been going from build UI in code to build UI in Storyboard and back several times in the last several years, and for me I’ve just landed on recommending building UI in code. I realize this can be a polarizing topic. The reason I recommend writing UI in code is to eliminate as many possible reasons for an app to crash.

The second trick is in implementing the view controller factory methods found in the dependency containers. You can find one of these factory methods by opening any of the -DependencyContainer classes in the example app for either Chapter 5 or 6. For example, here’s the book’s implementation of makeMainViewController in KooberAppDependencyContainer:

  public func makeMainViewController() -> MainViewController {
    let launchViewController = makeLaunchViewController()

    let onboardingViewControllerFactory = {
      return self.makeOnboardingViewController()
    }

    let signedInViewControllerFactory = { (userSession: UserSession) in
      return self.makeSignedInViewController(session: userSession)
    }

    return MainViewController(viewModel: sharedMainViewModel,
                              launchViewController: launchViewController,
                              onboardingViewControllerFactory: onboardingViewControllerFactory,
                              signedInViewControllerFactory: signedInViewControllerFactory)
  }

To use Storyboards you could implement the same method like this:

  public func makeMainViewController() -> MainViewController {
    let launchViewController = makeLaunchViewController()

    let onboardingViewControllerFactory = {
      return self.makeOnboardingViewController()
    }

    let signedInViewControllerFactory = { (userSession: UserSession) in
      return self.makeSignedInViewController(session: userSession)
    }

    let storyboard = UIStoryboard(name: "MyStoryboard", bundle: nil)
    guard let mainViewController = storyboard.instantiateViewController(withIdentifier: "mainViewController") as? MainViewController else {
      fatalError("Error casting storyboard view controller to MainViewController.")
    }
    mainViewController.viewModel = sharedMainViewModel
    mainViewController.launchViewController = launchViewController
    mainViewController.makeOnboardingViewController = onboardingViewControllerFactory
    mainViewController.makeSignedInViewController = signedInViewControllerFactory

    return mainViewController
  }

If using Storyboards, you’d probably end up placing the Storyboard instance as a property of the dependency container class. The rest of the material in the book, such as scoping, should continue to apply. I hope this helps. Feel free to reach out with any questions or if there’s any other examples I could provide. Cheers.

#3

Thanks for the reply. Appreciate it. I will definitely try this out. Looking forward to the completed book