https://images.unsplash.com/photo-1498747324273-943f73ca00b6?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb

Photo by helloimnik on 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
		}		
}


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
		}		
}


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/

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 )

Twitter picture

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

Facebook photo

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

Connecting to %s