Implementing Builder Pattern to create Navigation Stack for UIViewController on iOS

Arifin Firdaus
3 min readOct 3, 2021

--

Photo by helloimnik at Unsplash

Introduction

A Builder pattern is one of Design Pattern that belongs to the creational Design Pattern. The pattern exist to solve issue on creating a complex object. This pattern can helps developer to create an object in a simple and elegant way. To use the pattern, we need to create our own type of the object that we want to build. Let's dive in to the problem.

Case Study

Supposed that we have a requirement :

GIVEN the customer got a notification
WHEN the customer press the notification
THEN the customer should navigated to the transaction detail screen
GIVEN the customer is on the detail screen from push notification
WHEN the customer press back button
OR swipe back
THEN the customer should show transaction list screen

Now, based on this requirement, we can see that there is an automatic way to create a certain stack of UIViewController. Of course, we can managed this using deeplink, and then navigate to the screen using Coordinator.

But, what if there is a simple way to navigate to the target screen? What if, to be able to do based on the requirement, there is a handy way without creating many coordinator(s)?

That is why we can creates the Builder class. A Builder, is one of the Creational Design Pattern, that can helps us to create an complex object in an elegant way.

The typical API when using a builder pattern is like this :

let user = UserBuilder()
.setName(Name(fullName, lastName))
.setAddress(Address(road, city, postalCode, country))
.build()

(Builder pattern from the client’s point of view).

With this API design, we can translate that into our needs, which is creating the navigation stack for the UIViewController.

Our goal is to create this Builder API :

let viewController = NavigationControllerStackBuilder(root: HomeViewController())
.addStack(TransactionsViewController())
.addStack(TransactionDetailViewController())
.build()
// Do something with the `viewController`

Beautiful isn’t it?

Steps

To use the pattern, we need to create our own type of the object that we want to build. First, we need to define the Builder class.

class NavigationControllerStackBuilder {}

Then, since the UINavigationController’s API needs to inject a rootViewController when creating the navigationController object, then we need also passing the rootViewController to our Builder, using the constructor injection.

class NavigationControllerStackBuilder {  private let navigationController: UINavigationController  // injecting the initial view controller
init(root: UIViewController) {
self.navigationController = UINavigationController(rootViewController: root)
}
}

After that, we need to add a builder behavior. In this case, we want our builder to be able to stack the viewControllers while pushing it. We can then create the addStack(UIViewController) method.

We need to return self, or its own type, the Builder instance, so that it can be chained with the next operation.

class NavigationControllerStackBuilder {  private let navigationController: UINavigationController  init(root: UIViewController) {
self.navigationController = UINavigationController(rootViewController: root)
}
// adding stack
func addStack(_ viewController: UIViewController) -> NavigationControllerStackBuilder {
self.navigationController.pushViewController(viewController, animated: true)
return self
}
}

Finally, we need to return the instance. In this case, returning the viewController on the build() method. This is the standard method to returning the modified object for the Builder pattern.

class NavigationControllerStackBuilder {		private let navigationController: UINavigationController		init(root: UIViewController) {
self.navigationController = UINavigationController(rootViewController: root)
}
func addStack(_ viewController: UIViewController) -> NavigationControllerStackBuilder {
self.navigationController.pushViewController(viewController, animated: true)
return self
}

// returning the last viewController that has the navigationController.viewControllers previous stacks.
func build() -> UIViewController {
return navigationController.visibleViewController
}
}

So inside the NavigationControllerStackBuilder class, the full implementation should like this :

class NavigationControllerStackBuilder {  private let navigationController: UINavigationController  init(root: UIViewController) {
self.navigationController = UINavigationController(rootViewController: root)
}
func addStack(_ viewController: UIViewController) -> NavigationControllerStackBuilder {
self.navigationController.pushViewController(viewController, animated: true)
return self
}

func build() -> UIViewController {
return navigationController.visibleViewController
}
}

Creating the Instance using the Builder Pattern

Now, when the client needs to display the viewController, let say from push notification, the rootViewController, you can see the app will displays the last pushed screen with the stack in the navigationController. Give it a try 🙂.

let viewController = NavigationControllerStackBuilder(root: HomeViewController())
.addStack(TransactionsViewController())
.addStack(TransactionDetailViewController())
.build()
window?.rootViewController = viewController

Conclusion

Builder pattern is a creational Design Pattern. The pattern helps us to create a complex object with a simple API. When using a builder pattern, we need to create our own API to make the object creation posssible, and followed by build()method to return the object that has been modified on the chaining builder API.

References :

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

--

--

Arifin Firdaus
Arifin Firdaus

Written by Arifin Firdaus

Software Engineer — iOS | Apple Developer Academy Graduate | Brawijaya University | arifinfrds.com | LinkedIn : linkedin.com/in/arifin

No responses yet