Factory Method, Design Patterns Series -Part 3

Dilip Kumar
3 min read6 days ago

--

Photo by Noah Boyer on Unsplash

Hey iOS developers! welcome back to series of design patterns Let’s talk about object creation the Factory pattern

What’s the Factory Method?

The Factory Method is a creational design pattern where a superclass defines an interface for creating objects, but lets subclasses decide which class to instantiate. Think of it as a stamp machine — you press a button, and it delivers the exact object you need.

Key Players:

  1. Creator Protocol/Class: Declares the factory method.
  2. Concrete Creators: Override the factory method to produce specific objects.
  3. Product Protocol: The type of object being created.

SwiftUI’s Factory Method Magic

Ever wondered how Button or List in SwiftUI handles different initializers so seamlessly? It’s all thanks to @ViewBuilder, which acts as a factory for constructing view hierarchies!

Example: Custom Alert Factory

protocol AlertFactory {
func makeAlert() -> Alert
}

struct ErrorAlert: AlertFactory {
func makeAlert() -> Alert {
Alert(title: Text("Oops!"), message: Text("Something went wrong."))
}
}

struct SuccessAlert: AlertFactory {
func makeAlert() -> Alert {
Alert(title: Text("Success!"))
}
}

// Usage in SwiftUI:
struct ContentView: View {
let alertFactory: AlertFactory

var body: some View {
Button("Show Alert") { ... }
.alert(alertFactory.makeAlert())
}
}

This approach decouples the creation of different alert types, making your code easier to maintain and extend.

When to Use Factory Method ✅

  1. Decoupling Object Creation: Hide complex instantiation logic (e.g., parsing JSON into models).
  2. Supporting Multiple Platforms: Create different UIs for iOS/macOS without changing client code.
  3. Testing: Swap real factories with mocks in unit tests.

UIKit Example: Dynamic TableView Cells

protocol CellFactory {
func createCell(_ tableView: UITableView, indexPath: IndexPath) -> UITableViewCell
}

class PostCellFactory: CellFactory {
func createCell(_ tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexPath)
// Configure post cell
return cell
}
}

// In your ViewController:
let factory: CellFactory = PostCellFactory()
let cell = factory.createCell(tableView, indexPath: indexPath)

Using a factory pattern here makes it easy to swap out cell types or make changes to the cell configuration without touching the client code.

When Not to Use It 🚫

While the Factory Method is powerful, it’s not always the best solution. Here’s when you might want to skip it:

  • Simple Object Creation: If you’re just initializing a basic object like a String, you don’t need a factory.
  • Overengineering: Avoid creating factory hierarchies for a single object type — it might lead to unnecessary complexity.

Swift Best Practices

To get the most out of the Factory Method, consider these best practices:

  1. Leverage Protocols:
protocol ServiceFactory {
func makeNetworkService() -> NetworkService
}

2. Use Associated Types:

protocol Factory {
associatedtype Product
func create() -> Product
}

3. Combine with Dependency Injection:

class ViewModel {
private let serviceFactory: ServiceFactory

init(serviceFactory: ServiceFactory = DefaultServiceFactory()) {
self.serviceFactory = serviceFactory
}
}

These practices help keep your code clean, modular, and more flexible for testing and future changes.

Refactor Challenge: From Chaos to Factory

Let’s look at a common example. Here’s what the code might look like before applying the Factory Method:

Before (Brittle Code):

class PaymentProcessor {
func processPayment(type: String) {
switch type {
case "creditCard": CreditCardProcessor().process()
case "paypal": PayPalProcessor().process()
// 10 more cases...
}
}
}

This can quickly become hard to maintain and scale. Now, let’s refactor it using the Factory Method.

After (Factory Method FTW):

protocol PaymentProcessorFactory {
func create() -> PaymentProcessor
}

class CreditCardProcessorFactory: PaymentProcessorFactory {
func create() -> PaymentProcessor { CreditCardProcessor() }
}

class PayPalProcessorFactory: PaymentProcessorFactory {
func create() -> PaymentProcessor { PayPalProcessor() }
}

// Client code:
let factory: PaymentProcessorFactory = CreditCardProcessorFactory()
let processor = factory.create()
processor.process()

Now, adding new payment methods becomes much easier, as you only need to create new factories rather than modifying the existing code.

Key Takeaway

The Factory Method pattern is a great tool for writing decoupled, scalable code. Use it when:

  • Object creation logic is prone to change.
  • You need to support multiple variants of a product.
  • Testability is a priority.

Up Next: The Builder pattern — because sometimes, creating an object feels like assembling IKEA furniture without instructions. (Spoiler: URLRequest is a low-key Builder!)

Pro Tip: Check SwiftUI’s ViewBuilder implementation. It’s a masterclass in factory patterns!

Next Article: [Builder — Crafting Complex Objects Like a Swift Michelangelo]
Follow to stay in the pattern loop!

--

--

Dilip Kumar
Dilip Kumar

Written by Dilip Kumar

I use Medium to stay accountable as I explore Apple's ecosystem. Expect real-world analogies and celebrity website mockups.

No responses yet