Design Patterns in Swift — Factory

Ruslan Dzhafarov
6 min readJan 23, 2023

--

The Factory design pattern is a creational design pattern that provides a way to create objects without specifying the exact class of object that will be created. Instead, a factory class is responsible for creating objects based on a set of input parameters.

The Factory design pattern is particularly useful in situations where a class cannot anticipate the type of objects it needs to create, or when a class wants to delegate responsibility for object creation to its subclasses.

In Swift, the Factory design pattern can be implemented using a protocol and a factory class. The protocol defines the methods and properties that the factory class must implement, and the factory class creates objects that conform to the protocol.

Here is an example of using the Factory pattern to create different UI components in a iOS app.

Let’s say we have a UIView class that can create different types of UI elements such as a UILabel, UIButton, and UIImageView.

protocol UIComponent {
func create() -> UIView
}

class LabelFactory: UIComponent {
func create() -> UIView {
let label = UILabel()
label.text = "Label"
label.textColor = .black
label.font = UIFont.systemFont(ofSize: 18)
return label
}
}

class ButtonFactory: UIComponent {
func create() -> UIView {
let button = UIButton()
button.setTitle("Button", for: .normal)
button.setTitleColor(.black, for: .normal)
button.backgroundColor = .lightGray
return button
}
}

class ImageViewFactory: UIComponent {
func create() -> UIView {
let imageView = UIImageView()
imageView.image = UIImage(named: "defaultImage")
imageView.contentMode = .scaleAspectFit
return imageView
}
}

In this example, the UIComponent protocol defines the create() method that all factory classes must implement. LabelFactory, ButtonFactory, and ImageViewFactory classes all conform to the UIComponent protocol and provide their own implementation of the create() method.

In the client code, we can use the factory class to create UI components without having to know the details of how the components are created.

let labelFactory = LabelFactory()
let buttonFactory = ButtonFactory()
let imageViewFactory = ImageViewFactory()

let label = labelFactory.create()
let button = buttonFactory.create()
let imageView = imageViewFactory.create()

In this example, the client code only needs to know which factory class to use to create a specific component. This makes the code more flexible and easier to maintain. Additionally, if we want to add new types of components, we can simply create a new factory class that conforms to the UIComponent protocol without having to change the client code.

It is worth mentioning that, this is just an example, in general creating UI components could be done with Storyboard or Xib files as well. But this example is useful for dynamic creation of UI components.

The Factory design pattern provides a flexible and extensible way to create objects, and can help to encapsulate the logic of object creation within a single class. In addition to the above example, it can also be used in situations such as creating different types of UI controls or network connections.

In summary, the Factory Design pattern is a creational design pattern that provides a way to create objects without specifying the exact class of object that will be created. By using a protocol and a factory class, we can create objects that conform to a specific protocol, making it a flexible and extensible way to create objects.

A real-world example of using the Factory design pattern when building an iOS application could be when building a news app that displays articles in different formats such as text, video, and audio.

In this case, we could define a NewsArticle protocol that outlines the properties and methods that all article formats must have, and then create concrete classes for each format such as TextArticle, VideoArticle, and AudioArticle that conform to the NewsArticle protocol.

protocol NewsArticle {
var title: String { get }
var author: String { get }
var date: Date { get }
var image: UIImage? { get }
var content: String { get }
var type: ArticleType { get }
func play()
}

enum ArticleType {
case text
case video
case audio
}

class TextArticle: NewsArticle {
var title: String
var author: String
var date: Date
var image: UIImage?
var content: String
var type: ArticleType = .text

init(title: String, author: String, date: Date, image: UIImage?, content: String) {
self.title = title
self.author = author
self.date = date
self.image = image
self.content = content
}

func play() {
// no implementation needed
}
}

class VideoArticle: NewsArticle {
var title: String
var author: String
var date: Date
var image: UIImage?
var content: String
var type: ArticleType = .video
var videoURL: URL

init(title: String, author: String, date: Date, image: UIImage?, content: String, videoURL: URL) {
self.title = title
self.author = author
self.date = date
self.image = image
self.content = content
self.videoURL = videoURL
}

func play() {
// play the video
}
}

class AudioArticle: NewsArticle {
var title: String
var author: String
var date: Date
var image: UIImage?
var content: String
var type: ArticleType = .audio
var audioURL: URL

init(title: String, author: String, date: Date, image: UIImage?, content: String, audioURL: URL) {
self.title = title
self.author = author
self.date = date
self.image = image
self.content = content
self.audioURL = audioURL
}

func play() {
// play the audio
}
}

We could then create a NewsArticleFactory class that creates the appropriate article format based on the data it receives from the server:

class NewsArticleFactory {
func createArticle(data: [String: Any]) -> NewsArticle? {
guard let type = data["type"] as? String else { return nil }
let title = data["title"] as? String ?? ""
let author = data["author"] as? String ?? ""
let date = data["date"] as? Date ?? Date()
let image = data["image"] as? UIImage ?? nil
let content = data["content"] as? String ?? ""

switch type {
case "text":
return TextArticle(title: title, author: author, date: date, image: image, content: content)
case "video":
guard let videoURL = data["video_url"] as? URL else { return nil }
return VideoArticle(title: title, author: author, date: date, image: image, content: content, videoURL: videoURL)
case "audio":
guard let audioURL = data["audio_url"] as? URL else { return nil }
return AudioArticle(title: title, author: author, date: date, image: image, content: content, audioURL: audioURL)
default:
return nil
}
}
}

In this example, the NewsArticleFactory class takes in data from the server and uses a switch statement to determine which type of article to create. The factory class then creates the appropriate article object and returns it.

By using the factory pattern, the client code does not need to know the details of how the article objects are created, it only needs to know that it will receive a NewsArticle object. Additionally, if we want to add new types of articles, we can simply add a new case to the switch statement in the factory class, without having to change the client code.

This makes the code more flexible and easier to maintain, and it also separates the concerns of creating the objects and using them.

Thank you for reading until the end. If you have any questions or feedback, feel free to leave a comment below.

If you enjoyed reading this article, please press the clap button 👏 . Your support encourages me to share more of my experiences and write more articles like this. Follow me here on medium for more updates!

Happy coding! 😊👨‍💻👩‍💻

More from Ruslan Dzhafarov

Advanced UIKit Techniques

9 stories

Design Patterns in Swift

9 stories

Advanced Swift Programming

3 stories

--

--

Ruslan Dzhafarov

Senior iOS Developer since 2013. Sharing expert insights, best practices, and practical solutions for common development challenges