5 Must-Know Creational Design Patterns in Swift: Master the Art of Object Creation

5 Must-Know Creational Design Patterns in Swift: Master the Art of Object Creation

This article is part of the Design Patterns in Swift series.

Creating objects is an integral aspect of iOS app development, and doing it right can make all the difference. Creational Design Patterns provide powerful, tried-and-tested techniques for simplifying object creation while keeping your code clean and maintainable.

In this blog post, we'll explore 5 essential Creational Design Patterns with Swift code examples, helping you master the art of object creation.

1. Singleton: Ensure a Class Has Only One Instance

The Singleton pattern guarantees that a class has only one instance, providing a global access point to that instance. This is useful when you want to share data or resources across your app.

class Singleton {
    static let sharedInstance = Singleton()
    private init() { }
    // Your properties and methods here
}

// Usage
let singletonInstance = Singleton.sharedInstance

2. Prototype: Clone Objects Instead of Creating New Ones

The Prototype pattern involves creating new objects by cloning existing ones, rather than instantiating new instances. This is particularly useful when object creation is expensive or time-consuming.

protocol Copyable {
    func copy() -> Self
}

class MyClass: Copyable {
    var value: Int

    init(value: Int) {
        self.value = value
    }

    func copy() -> Self {
        return MyClass(value: self.value) as! Self
    }
}

// Usage
let original = MyClass(value: 10)
let copy = original.copy()

3. Builder: Construct Complex Objects Step by Step

The Builder pattern separates the construction of complex objects from their representation, allowing for a step-by-step construction process. This pattern is ideal when you have many optional parameters or complex initialization logic.

class Pizza {
    var size: String?
    var cheese: String?
    var sauce: String?

    class Builder {
        private var pizza = Pizza()

        func setSize(_ size: String) -> Builder {
            pizza.size = size
            return self
        }

        func setCheese(_ cheese: String) -> Builder {
            pizza.cheese = cheese
            return self
        }

        func setSauce(_ sauce: String) -> Builder {
            pizza.sauce = sauce
            return self
        }

        func build() -> Pizza {
            return pizza
        }
    }
}

// Usage
let pizza = Pizza.Builder().setSize("Large").setCheese("Mozzarella").build()

4. Factory Method: Create Objects Without Specifying Their Concrete Classes

The Factory Method pattern provides an interface for creating objects in a superclass, allowing subclasses to decide which class to instantiate. This helps decouple your code and makes it more flexible.

protocol Animal {
    func speak()
}

class Dog: Animal {
    func speak() {
        print("Woof!")
    }
}

class Cat: Animal {
    func speak() {
        print("Meow!")
    }
}

enum AnimalType {
    case dog, cat
}

class AnimalFactory {
    static func createAnimal(ofType type: AnimalType) -> Animal {
        switch type {
        case .dog:
            return Dog()
        case .cat:
            return Cat()
        }
    }
}

// Usage
let dog = AnimalFactory.createAnimal(ofType: .dog)
dog.speak()

The Abstract Factory pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes. This pattern is useful when you need to create objects that belong to different groups or themes.

protocol Button {
    func display()
}

class LightButton: Button {
    func display() {
        print("Light button displayed")
    }
}

class DarkButton: Button {
    func display() {
        print("Dark button displayed")
    }
}

protocol Switch {
    func toggle()
}

class LightSwitch: Switch {
    func toggle() {
        print("Light switch toggled")
    }
}

class DarkSwitch: Switch {
    func toggle() {
        print("Dark switch toggled")
    }
}

protocol UserInterfaceFactory {
    func createButton() -> Button
    func createSwitch() -> Switch
}

class LightUserInterfaceFactory: UserInterfaceFactory {
    func createButton() -> Button {
        return LightButton()
    }

    func createSwitch() -> Switch {
        return LightSwitch()
    }
}

class DarkUserInterfaceFactory: UserInterfaceFactory {
    func createButton() -> Button {
        return DarkButton()
    }

    func createSwitch() -> Switch {
        return DarkSwitch()
    }
}

// Usage
let lightFactory: UserInterfaceFactory = LightUserInterfaceFactory()
let lightButton = lightFactory.createButton()
let lightSwitch = lightFactory.createSwitch()

lightButton.display()
lightSwitch.toggle()

let darkFactory: UserInterfaceFactory = DarkUserInterfaceFactory()
let darkButton = darkFactory.createButton()
let darkSwitch = darkFactory.createSwitch()

darkButton.display()
darkSwitch.toggle()

These patterns will help you write clean, maintainable, and flexible code, making it easier to adapt and grow your applications over time.

Start using these powerful techniques today, and supercharge your app development skills!

I hope you enjoyed this article, and if you have any questions, comments, or feedback, then feel free to comment here or reach out via Twitter.

Thanks for reading!

Check the next article in this series Structural Design Patterns in Swift.