Builder Pattern in Swift for Beginners

activesludge
Swift2Go
Published in
6 min readDec 19, 2020

“Have you ever not been able to create an object, just because you haven’t got all the parameters in hand?”

Gif by Jambonbon on Tumblr

I hope you are not hungry, because we will use hamburgers as metaphor.

Table of Contents

1. Introduction
2. Director, Builder, Product
3. When should you use it?
4. Implementation
5. Be careful when
6. Conclusion

1. Introduction

In swift, we have initializers. If we want to have an instance of an object, we create it using init function inside the struct or class.

In case of a hamburger, the initializer would look like this.

To create a Hamburger object, we have to fill each parameter requested by init.

What if we needed an hamburger without any sauce or any topping? Or what if I want to add toppings first then sauces? Or what if there is a case that we don’t have all the parameters at hand?

We can create alternative init functions. But it’s a duplication of code. Even if we tried, half way there, we would think “There has to be a better way”.

That is when the topic of this article, comes in handy. Builder design pattern is a creational design pattern that builds an object step by step without having to change the recipe, like a hamburger.

You can even turn builder function to composable to have more elegant looking dot syntax. A most of 3rd party swift libraries, makes developer’s life easier by taking advantage of builder pattern, such as RxSwift.

Builder with composables. Used as dot syntax. Looks way more professional.

Implementations may vary from developer to developer. You can make this more advanced or primitive, whichever is suited for your needs.

Below implementation is a basic version and using metaphor. Nevertheless, it will give you a good grasp on the concept.

We will use Playground. So create a new .playground, save it and proceed.

2. Director, Builder, Product

Builder design pattern has three main actors.

Director accepts inputs and coordinates with the builder. This is usually a view controller or a helper class that’s used by a view controller. It takes a builder as a parameter, and uses the returned Product however it wants.

Tip: Since the Builder is a dependency of the Director, what we will do below is making it “loosely coupled”. Which is a good practice for future. However, it doesn’t have to take a builder as a parameter. The builder can also be embedded inside the director. It is called “tightly coupled” and usually is a bad practice.

Builder is the one accepts step-by-step inputs and handles the creation of the product. This is often a class, so it can be reused by reference. Here is where we can implement our “setters”.

Tip: A way to make the builder more advanced, is to creating Builder as a protocol, and having different concrete Builder classes to implement necessary Builder protocol functions, such as “build()”. And implement each concrete class however you want.

Product is the complex object to be created. This can be either a struct or a class, depending on desired reference semantics. It’s usually a model, but it can be any type depending on your use case.

Tip: I couldn’t come up with a tip about Product, but here’s a one for hamburger! Minimize the amount of spice you mix to the meat. Rest the meat at least half an hour. Flat out the minced meat wider than the bread’s width.

3. When should you use it?

Use the builder pattern when you want to create a complex object using a series of steps.

This pattern works especially well when a product requires multiple inputs. The builder abstracts how these inputs are used to create the product, and it accepts them in whatever order the director wants to provide them. Below are some real life examples:

  • User fills a form (sign up, survey, etc.)
  • User creates custom object (question, flash card, game level, collage, etc.)
  • User creates their order (pizza toppings)

4. Implementation

Create Product

We first define Hamburger, which has properties for meat, sauce and toppings. Why are they “let”, because once you make a hamburger, you can’t change it.

  1. We declare Meat as an enum. Let’s say each hamburger must have exactly one meat selection. (I’m not sure whether tofu can be classified as a meat selection. Sorry if that’s wrong.)
  2. We define Sauces and Toppings as an OptionSet. This will allow you to combine multiple sauces together. We’re gonna need more than pickles for a good burger!
  3. CustomStringConvertible extensions will help us print a readable string.

Tip: OptionSet is a protocol that presents a mathematical set interface to a bit set. It helps us implement single, multiple or no selection option way of structures. Don’t worry about it now. Yet definitely check it out later. For more info: https://developer.apple.com/documentation/swift/optionset

Create Builder

We declare properties for meat, sauces and toppings, which exactly match the inputs for Hamburger. Unlike a Hamburger, you declare these using var to be able to change them. You also specify private(set) for each to ensure only HamburgerBuilder can set them directly.

Since you declared each property using private(set), you need to provide public methods to change them. You do so via addSauces(_:), removeSauces(_:), addToppings(_:), removeToppings(_:) and setMeat(_:).

Lastly, you define build() to create the Hamburger from the selections. private(set) forces consumers to use the public setter methods. This allows the Builder to perform validation before setting the properties.
For example, you might want to ensure a meat is available prior to setting it.

Implement Director

A BurgerShop knows how to create two burgers: createCheeseBurger and createVegetarianBurger. Director can implement its own functions.

Moment of Truth

We’re finally ready to see this code in action. Add the following at the end of the playground:

With Director, using predefined Builder functions:

Remember: In real life, the Director has usually a tableView or textView or anything. And it builds as inputs come from the user.

What if we don’t have a predefined function and will build our burger as inputs come?

Without predifened functions:

5. Be careful when

The builder pattern works best for creating complex products that require multiple inputs using a series of steps. If your product doesn’t have several inputs or can’t be created step by step, the builder pattern may be more trouble than it’s worth.

Instead, consider providing convenience initializers to create the product.

6. Conclusion

  • The builder pattern is great for creating complex objects in a step-by-step fashion. It involves three objects: the director, product and builder.
  • The director accepts inputs and coordinates with the builder; the product is the complex object that’s created; and the builder takes step-by-step inputs and creates the product.
  • This pattern can be more advanced or more primitive regarding with the requirement or architectural decisions. Directors can use multiple builders as parameter. Builders can create composable objects for passing it to the next operation.

I hope this article will be useful to you. Have a great one.

References
raywenderlich.com
refactoring.guru/design-patterns/builder
developer.apple.com/documentation/swift

--

--