Factory Design Pattern Illustration

Overview

AppDelegate or SceneDelegate is the main file that we as iOS developers know to configure, inject, or instantiate a global instance or frameworks. This place become essential, especially because it is actually the starting point of our app.

Because it knows so many things, like configuring the frameworks, handling push notifications routing, checking app’s life cycle, and others, this place can be easily messy. When you notice this class is doing many different things, we can separate the responsibility of creating the rootViewController into another component, like a factory class.

A Factory in the AppDelegate or SceneDelegate

Factory class applied in AppDelegate / SceneDelegate

A factory is one of the creational design pattern. It hides the complexity of creating object and only needs given dependency, if needed. The Factory API we need is like this :

protocol TransactionsViewControllerFactory {
    func makeTransactionsViewController() -> UIViewController
}

Since we don’t need another implementation for the factory in this example, we can get rid of the protocol , and change it to a class with static method instead.

final class TransactionsViewControllerFactory {

    private init() { } // we don't need the init at this example.

    static func makeTransactionsViewController() -> UIViewController {
        
        let storyboard = UIStoryboard(name: "Main", bundle: .main)
        let identifier = "TransactionsViewController"
        let viewController = storyboard.instantiateViewController(identifier: identifier) as! TransactionsViewController
        
        let loader = LocalTransactionLoader(store: CoreDataTransactionStore(.persistent))
        let presenter = DefaultTransactionsPresenter(loader: loader)
        
        viewController.presenter = presenter
        viewController.onAddTransactionButtonTapped = self.presentAddTranstionViewController(_:)
        presenter.view = viewController
        
        return viewController
    }
}

Another example of using the factory is on the code implemention of presentAddTranstionViewController function. Its using another ViewController Factory to instantiate the component.

private static func presentAddTranstionViewController(_ transactionsVC: TransactionsViewController) -> Void {
        
        let addTransactionVC = AddTransactionsViewControllerFactory.makeAddTransactionViewController(transactionsVC: transactionsVC)
        let addTransactionNC = UINavigationController(rootViewController: addTransactionVC)
        transactionsVC.present(addTransactionNC, animated: true, completion: nil)
}

To enable the ViewController creation as the SUT (System Under Tests) in the test target, we can provide a dependency into the factory method of the Factory class.

final class TransactionsViewControllerFactory {

  // ...
  static func makeTransactionsViewController(presenter: TransactionsPresenter) -> UIViewController {
    // Injecting the given presenter
    return transactionsViewController   
  }
}

Another options is to create the default arguments on the method, so that the AppDelegate does not need to create the ViewController’s dependency.

Then, the AppDelegate (or SceneDelegate) will be clean, like this example :

self.window?.rootViewController = TransactionsViewControllerFactory.makeTransactionsViewController()
self.window?.makeKeyAndVisible()

References

Head First Design Patterns https://www.oreilly.com/library/view/head-first-design/0596007124/

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s