Builder Design Pattern in iOS

Omar Saibaa
7 min readNov 1, 2023

--

The Builder design pattern is a creational design pattern that separates the construction of a complex object from its representation. It allows you to create different variations of a complex object without changing the code that creates it.

The Builder design pattern is often used in iOS development to create view controllers, view models, and other UI elements. It can also be used to create data models, network requests, and other complex objects.

Example 1: 👶

Imagine you are building a Lego castle. You want to build a big and complex castle, but you don’t know where to start.

You could use a Builder to help you build the castle. The Builder is like a set of instructions for building a Lego castle. The Builder tells you which Lego bricks to use and how to put them together.

To build the castle, you would simply follow the Builder’s instructions. The Builder would tell you which Lego bricks to use first, then which Lego bricks to use next, and so on.

Once you have finished following the Builder’s instructions, you will have built a big and complex Lego castle!

The Builder design pattern is like a set of instructions for building complex objects. It tells you the steps that you need to take to build the object, and it provides you with the tools that you need to build the object.

Example 2: 🍎

Imagine you are building a house. You want to customize the house by choosing different features, such as the number of bedrooms, bathrooms, and garages. You could hire a builder to help you build the house.

The builder would have a set of instructions for building a house. The builder would follow these instructions to build your house with the features that you specified.

Example 3: 💻

The Builder design pattern is often used in iOS development to create view controllers, view models, and other UI elements. It can also be used to create data models, network requests, and other complex objects.

Here is another example of how to use the Builder design pattern to create a data model:

class UserModelBuilder {
private var user: User = User()

func setName(name: String) {
user.name = name
}

func setEmail(email: String) {
user.email = email
}

func build() -> User {
return user
}
}

let userModelBuilder = UserModelBuilder()

// Set the user's name and email.
userModelBuilder.setName(name: "John Doe")
userModelBuilder.setEmail(email: "john.doe@example.com")

// Build the user object.
let user = userModelBuilder.build()

This code creates a user model builder class that can be used to create user objects with different names and emails. To create a new user object, you simply call the build() method on the builder object.

How to implement the Builder pattern in iOS?

  1. Create a protocol for the complex object that you want to build. This protocol should define the interface for the complex object.
  2. Create a concrete class for each of the different types of complex objects that you want to build. These classes should implement the protocol that you created in step 1.
  3. Create a builder class. This class should have methods for each step in the construction process for the complex object.
  4. In the builder class, implement the following methods:
  • build(): This method should construct the complex object and return it.
  • add(part: Part): This method should add a part to the complex object.
  • remove(part: Part): This method should remove a part from the complex object.
  • getComplexObject(): This method should return the completed complex object.

5. In the client code, use the builder class to construct the complex object.

Here is an example of how to implement the Builder pattern in iOS:

protocol ComplexObject {
// Define the interface for the complex object.
}

class ConcreteComplexObject: ComplexObject {
// Implement the interface for the complex object.
}

class Builder {
private var complexObject: ComplexObject = ConcreteComplexObject()

func add(part: Part) {
complexObject.add(part: part)
}

func remove(part: Part) {
complexObject.remove(part: part)
}

func build() -> ComplexObject {
return complexObject
}
}

class Client {
private let builder: Builder

init(builder: Builder) {
self.builder = builder
}

func construct() {
// Add parts to the complex object using the builder.
builder.add(part: .part1)
builder.add(part: .part2)
builder.add(part: .part3)

// Get the completed complex object from the builder.
let complexObject = builder.build()

// Use the complex object.
}
}

This is just a simple example, but it illustrates how the Builder pattern can be used to create complex objects in iOS.

Example 4: 💪

Imagine you are developing a car rental app. You want to allow users to rent cars with different types, features, and pickup locations. You could use the Builder pattern to create a car rental reservation builder.

The car rental reservation builder would have methods for setting the car type, features, pickup location, and pickup date. The car rental reservation builder would also have a method for building the car rental reservation object.

Here is an example of a car rental reservation builder in Swift:

class CarRentalReservationBuilder {
private var carRentalReservation: CarRentalReservation = CarRentalReservation()

func set(carType: CarType) {
carRentalReservation.carType = carType
}

func set(features: [CarFeature]) {
carRentalReservation.features = features
}

func set(pickupLocation: Location) {
carRentalReservation.pickupLocation = pickupLocation
}

func set(pickupDate: Date) {
carRentalReservation.pickupDate = pickupDate
}

func build() -> CarRentalReservation {
return carRentalReservation
}
}

To rent a car with a specific car type, features, pickup location, and pickup date, you would simply create a car rental reservation builder object and then call the builder’s methods to set the desired values. Once you have set all of the desired values, you would call the builder’s build() method to get the completed car rental reservation object.

Here is an example of how to use the car rental reservation builder to rent a Toyota Camry with GPS and a child seat from Dubai International Airport on November 1, 2023:

let carRentalReservationBuilder = CarRentalReservationBuilder()

// Set the car type to Toyota Camry.
carRentalReservationBuilder.set(carType: .toyotaCamry)

// Set the features to GPS and a child seat.
carRentalReservationBuilder.set(features: [.gps, .childSeat])

// Set the pickup location to San Dubai International Airport.
carRentalReservationBuilder.set(pickupLocation: Location(latitude: 37.7753, longitude: -122.4194))

// Set the pickup date to November 1, 2023.
carRentalReservationBuilder.set(pickupDate: Date(timeIntervalSince1970: 1662202800))

// Build the car rental reservation object.
let carRentalReservation = carRentalReservationBuilder.build()

// Rent the car.
rentCar(carRentalReservation: carRentalReservation)

Example 5: 🐉

Imagine you are developing a Twitter app. You want to allow users to post tweets with images and videos. You could use the Builder pattern to create a tweet builder.

The tweet builder would have methods for adding text, images, and videos to the tweet. The tweet builder would also have a method for building the tweet object.

Here is an example of a tweet builder in Swift:

class TweetBuilder {
private var tweet: Tweet = Tweet()

func add(text: String) {
tweet.text = text
}

func add(image: UIImage?) {
tweet.image = image
}

func add(videoURL: URL?) {
tweet.videoURL = videoURL
}

func build() -> Tweet {
return tweet
}
}

To post a tweet with an image, you would simply create a tweet builder object and then call the builder’s add(image:) and build() methods.

class Tweet {
var text: String?
var image: UIImage?
var videoURL: URL?

init() {}
}

class TweetBuilder {
private var tweet: Tweet = Tweet()

func add(text: String) {
tweet.text = text
}

func add(image: UIImage?) {
tweet.image = image
}

func add(videoURL: URL?) {
tweet.videoURL = videoURL
}

func build() -> Tweet {
return tweet
}
}

class ViewController: UIViewController {
@IBAction func postTweet() {
let tweetBuilder = TweetBuilder()
tweetBuilder.add(image: UIImage(named: "image.png"))
let tweet = tweetBuilder.build()

// Post the tweet to Twitter.
}
}

The Builder pattern makes your code more flexible and reusable because it allows you to create different types of tweets without having to change the code that posts the tweets.

Here is a complete example of using the Builder pattern to post a tweet with an image:

class Tweet {
var text: String?
var image: UIImage?
var videoURL: URL?

init() {}
}

class TweetBuilder {
private var tweet: Tweet = Tweet()

func add(text: String) {
tweet.text = text
}

func add(image: UIImage?) {
tweet.image = image
}

func add(videoURL: URL?) {
tweet.videoURL = videoURL
}

func build() -> Tweet {
return tweet
}
}

class ViewController: UIViewController {
@IBAction func postTweet() {
let tweetBuilder = TweetBuilder()
tweetBuilder.add(image: UIImage(named: "image.png"))
let tweet = tweetBuilder.build()

// Post the tweet to Twitter.
}
}

Advantages of the Builder design pattern in iOS:

  • Flexibility: The Builder pattern makes your code more flexible because it allows you to create different variations of a complex object without changing the code that creates it.
  • Reusability: The Builder pattern makes your code more reusable because it allows you to create a single builder class that can be used to create different types of objects.
  • Testability: The Builder pattern makes your code more testable because it allows you to mock or stub builders in your unit tests. This makes it easier to test your code without having to rely on external dependencies.
  • Readability: The Builder design pattern can make your code more readable because it separates the construction of a complex object from its representation. This can make it easier to understand how a complex object is created.

Disadvantages of the Builder design pattern in iOS:

  • Complexity: The Builder pattern can add complexity to your code, especially if you are creating complex objects.
  • Performance overhead: The Builder pattern can introduce a small performance overhead because it requires you to create a separate builder object for each complex object that you want to create.

Tips for using the Builder pattern in iOS:

  • Use the Builder pattern only when you need to create complex objects.
  • Use a builder class hierarchy to create different types of builders for different types of complex objects.
  • Use generics to make your builder classes more reusable.
  • Avoid using the Builder pattern for simple objects.

Finally:

The Builder design pattern is a powerful tool for creating complex objects in a flexible and reusable way. It can make your code more readable and testable, but it can also add complexity and performance overhead. Use the Builder pattern wisely, and only when you need to create complex objects.

Thanks for reading and Don’t forget to claps, comment and follow me for more.

--

--