Design Patterns in Swift: Advanced Techniques and Examples

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Advanced Design Patterns in Swift: From Theory to Practice

Design patterns are not just abstract concepts, but time-tested solutions to common problems. If you have already mastered basic patterns (Singleton, Delegate, MVC) and are working on complex iOS/macOS applications, it's time to move to the next level. In this article, we will cover four advanced patterns: Builder, Strategy, Observer (via Combine) and Coordinator. Each pattern will be illustrated with real Swift code and tips for integrating into existing projects.



1. Builder — flexible configuration of complex objects

The Builder pattern is ideal for creating objects with many optional parameters. Instead of long initializers with dozens of parameters, you use step-by-step construction.

Example: configuring UIAlertController

class AlertBuilder {    private var title: String?    private var message: String?    private var actions: [UIAlertAction] = []    private var preferredStyle: UIAlertController.Style = .alert

func setTitle(_ title: String) -> AlertBuilder { self.title = title return self }

func setMessage(_ message: String) -> AlertBuilder { self.message = message return self }

func addAction(title: String, style: UIAlertAction.Style = .default, handler: ((UIAlertAction) -> Void)? = nil) -> AlertBuilder { let action = UIAlertAction(title: title, style: style, handler: handler) actions.append(action) return self }

func setStyle(_ style: UIAlertController.Style) -> AlertBuilder { self.preferredStyle = style return self }

func build() -> UIAlertController { let alert = UIAlertController(title: title, message: message, preferredStyle: preferredStyle) actions.forEach { alert.addAction($0) } return alert }}

// Usagelet alert = AlertBuilder() .setTitle("Error") .setMessage("Failed to load data") .addAction(title: "OK", style: .default) .addAction(title: "Retry", style: .cancel) .build()present(alert, animated: true)

When to use: when creating configurators, complex ViewControllers or data models with many options.



2. Strategy — switching algorithms on the fly

The Strategy pattern allows you to isolate a family of algorithms and make them interchangeable. This is especially useful for payment processing, data validation, or rendering.

Example: content filtering system

protocol FilterStrategy {    func apply(to items: [String]) -> [String]}

class AlphabeticalFilter: FilterStrategy { func apply(to items: [String]) -> [String] { return items.sorted() }}

class LengthFilter: FilterStrategy { func apply(to items: [String]) -> [String] { return items.filter { $0.count > 5 } }}

class ContentManager { private var strategy: FilterStrategy

init(strategy: FilterStrategy) { self.strategy = strategy }

func setStrategy(_ strategy: FilterStrategy) { self.strategy = strategy }

func filter(items: [String]) -> [String] { return strategy.apply(to: items) }}

// Usagelet manager = ContentManager(strategy: AlphabeticalFilter())let data = ["Swift", "Objective-C", "Kotlin", "Java", "Python"]print(manager.filter(items: data)) // ["Java", "Kotlin", "Objective-C", "Python", "Swift"]

manager.setStrategy(LengthFilter())print(manager.filter(items: data)) // ["Objective-C", "Python"]

Tip: combine Strategy with a factory method to select a strategy based on user settings.



3. Observer via Combine — re

Blogs

Book Recommendations