Chapters

Hide chapters

Design Patterns by Tutorials

Third Edition · iOS 13 · Swift 5 · Xcode 11

11. Factory Pattern
Written by Jay Strawn

The factory pattern is a creational pattern that provides a way to make objects without exposing creation logic. It involves two types:

  1. The factory creates objects.
  2. The products are the objects that are created.

Technically, there are multiple “flavors” of this pattern, including simple factory, abstract factory and others. However, each of these share a common goal: to isolate object creation logic within its own construct.

In this chapter, you’ll be adding onto the previous chapter’s project, Coffee Quest, to learn about a simple factory. It creates objects of a common type or protocol, and the factory’s type itself is known and used by consumers directly.

When should you use it?

Use the factory pattern whenever you want to separate out product creation logic, instead of having consumers create products directly.

A factory is very useful when you have a group of related products, such as polymorphic subclasses or several objects that implement the same protocol. For example, you can use a factory to inspect a network response and turn it into a concrete model subtype.

A factory is also useful when you have a single product type, but it requires dependencies or information to be provided to create it. For example, you can use a factory to create a “job applicant response” email: The factory can generate email details depending on whether the candidate was accepted, rejected or needs to be interviewed.

Playground example

Open IntermediateDesignPattern.xcworkspace in the Starter directory, or continue from your own playground workspace from the last chapter, then open the Factory page. As mentioned above, you’ll create a factory to generate job applicant response emails. Add the following after Code Example:

import Foundation

public struct JobApplicant {
  public let name: String
  public let email: String
  public var status: Status
  
  public enum Status {
    case new
    case interview
    case hired
    case rejected
  }
}

public struct Email {
  public let subject: String
  public let messageBody: String
  public let recipientEmail: String
  public let senderEmail: String
}

Here, you’ve defined JobApplicant and Email models. An applicant has a name, email, and four types of status. The email’s subject and messageBody will be different depending on an applicant’s status.

Next, add the following code:

// 1
public struct EmailFactory {
  
  // 2
  public let senderEmail: String
  
  // 3
  public func createEmail(to recipient: JobApplicant) -> Email {
    let subject: String
    let messageBody: String

    switch recipient.status {
    case .new:
      subject = "We Received Your Application"
      messageBody = 
        "Thanks for applying for a job here! " +
        "You should hear from us in 17-42 business days."

    case .interview:
      subject = "We Want to Interview You"
      messageBody = 
        "Thanks for your resume, \(recipient.name)! " +
        "Can you come in for an interview in 30 minutes?"

    case .hired:
      subject = "We Want to Hire You"
      messageBody = 
        "Congratulations, \(recipient.name)! " +
        "We liked your code, and you smelled nice. " +
        "We want to offer you a position! Cha-ching! $$$"

    case .rejected:
      subject = "Thanks for Your Application"
      messageBody = 
        "Thank you for applying, \(recipient.name)! " +
        "We have decided to move forward " +
        "with other candidates. " +
        "Please remember to wear pants next time!"
    }

    return Email(subject: subject,
                 messageBody: messageBody,
                 recipientEmail: recipient.email,
                 senderEmail: senderEmail)
  }
}

Here’s what you’re doing above:

  1. Create an EmailFactory struct.

  2. Create a public property for senderEmail. You set this property within the EmailFactory initializer.

  3. Create a function named createEmail that takes a JobApplicant and returns an Email. Inside createEmail, you’ve added a switch case for the JobApplicant’s status to populate the subject and messageBody variables with appropriate data for the email.

Now the email templates have been constructed, it’s time to use your factory on a prospective applicant!

Add the following code below your EmailFactory definition:

var jackson = JobApplicant(name: "Jackson Smith",
                           email: "jackson.smith@example.com",
                           status: .new)

let emailFactory = 
  EmailFactory(senderEmail: "RaysMinions@RaysCoffeeCo.com")

// New
print(emailFactory.createEmail(to: jackson), "\n")

// Interview
jackson.status = .interview
print(emailFactory.createEmail(to: jackson), "\n")

// Hired
jackson.status = .hired
print(emailFactory.createEmail(to: jackson), "\n")

Here, you’re creating a new JobApplicant named “Jackson Smith”. Next, you create a new EmailFactory instance, and finally, you use the instance to generate emails based on the JobApplicant object status property.

Looks like Jackson will be getting a job soon. He probably set himself apart from other applicants by impressing Ray’s Coffee Co. with his extensive knowledge of design patterns!

What should you be careful about?

Not all polymorphic objects require a factory. If your objects are very simple, you can always put the creation logic directly in the consumer, such as a view controller itself.

Alternatively, if your object requires a series of steps to build it, you may be better off using the builder pattern or another pattern instead.

Tutorial project

You’ll continue the Coffee Quest app from the previous chapter. If you skipped the previous chapter, or you want a fresh start, open Finder and navigate to where you downloaded the resources for this chapter. Then, open starter\CoffeeQuest\CoffeeQuest.xcworkspace (not .xcodeproj) in Xcode.

Note: If you opt to start fresh, then you’ll need to open up APIKeys.swift and add your Yelp API key. See Chapter 10, “Model-View-ViewModel Pattern” for instructions on how to generate this.

You’ll use the factory pattern to improve the mechanism behind changing icons based on their Yelp rating.

First, right-click on the CoffeeQuest group and create a new group named Factories. Next, right-click on the Factories group and select New File…. Select iOS ▸ Swift File and click Next. Call it AnnotationFactory.swift and click Create. Your folder structure should look similar to the following:

Finally, replace the contents of AnnotationFactory.swift with the following:

import UIKit
import MapKit
import YelpAPI

public class AnnotationFactory {
  
  public func createBusinessMapViewModel(
    for business: YLPBusiness) -> BusinessMapViewModel? {
    
    guard 
      let yelpCoordinate = business.location.coordinate else {
        return nil
    }

    let coordinate = 
      CLLocationCoordinate2D(
        latitude: yelpCoordinate.latitude,
        longitude: yelpCoordinate.longitude)

    let name = business.name
    let rating = business.rating
    let image: UIImage
    switch rating {
    case 3.0..<3.5:
      image = UIImage(named: "bad")!
    case 3.5..<4.0:
      image = UIImage(named: "meh")!
    case 4.0..<4.75:
      image = UIImage(named: "good")!
    case 4.75...5.0:
      image = UIImage(named: "great")!
    default:
      image = UIImage(named: "bad")!
    }
    return BusinessMapViewModel(coordinate: coordinate,
                                image: image,
                                name: name,
                                rating: rating)
  }
}

This should look familiar (if you’ve read the previous chapters!). It’s the code added in the previous chapter where you create the BusinessMapViewModel for the given coffee shop.

This is your first factory! When you employ the factory pattern, it will often feel like you’re factoring out code, like you are here. Any other component of your app that wants to create a BusinessMapViewModel from a coffee shop model can do so now.

This means when the project gets larger, changing map annotations is less likely to break coupled modules because all the transformation logic is contained in one place!

Add a new level of coffee rating to your factory called "terrible" for anything less than 3 stars. I know; I’m a coffee snob! Your switch statement should look like the following:

switch rating {
case 0.0..<3.0:
  image = UIImage(named: "terrible")!
case 3.0..<3.5:
  image = UIImage(named: "bad")!
case 3.5..<4.0:
  image = UIImage(named: "meh")!
case 4.0..<4.75:
  image = UIImage(named: "good")!
case 4.75...5.0:
  image = UIImage(named: "great")!
default:
  image = UIImage(named: "bad")!
}

This is an example of how factories cannot be closed for modification, as you need to add and remove cases to make different objects.

Just as you did in the view controller, you’re switching on rating to determine which image to use.

Open ViewController.swift and add the following property below // MARK: - Properties :

public let annotationFactory = AnnotationFactory()

Finally, replace addAnnotations() with the following code:

private func addAnnotations() {    
  for business in businesses {
    guard let viewModel = 
      annotationFactory.createBusinessMapViewModel(
        for: business) else {
          continue
    }
    mapView.addAnnotation(viewModel)
  }
}

Time for your view controller to actually use this factory! The factory creates a businessMapViewModel for each business returned in the Yelp search.

Build and run to verify that everything works as before.

Key points

You learned about the factory pattern in this chapter. Here are its key points:

  • A factory’s goal is to isolate object creation logic within its own construct.

  • A factory is most useful if you have a group of related products, or if you cannot create an object until more information is supplied (such as completing a network call, or waiting on user input).

  • The factory method adds a layer of abstraction to create objects, which reduces duplicate code.

You’ve once again slimmed down the view controller. Not much has changed visually in your app, but implementing a factory allows for easy changes as projects inevitably grow larger.

You might have noticed that your factory can only take a YLPBusiness from the Yelp API. What if you wanted to switch to a different service, such as Google Places? It would be a good idea to rewrite your code so you can take any third-party class and convert it into a more generic Business type. You’ll do this in the next chapter using an adapter pattern.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.