Greetings, traveler!
We continue our exploration of Creational Design Patterns. Now, it is the turn of the Prototype Design Pattern.
When might we need to use it? Consider a scenario where we need to create multiple copies of an object, each with minor differences. How could we approach this? Sure thing, we can create something like this.
struct Object {
var name: String
var color: UIColor
var count: Int
}
let firstObject = Object(name: "First Name", color: .red, count: 1)
let secondObject = Object(name: "Second Name", color: .red, count: 1)
let thirdObject = Object(name: "Third Name", color: .red, count: 1)But it looks inconvenient. We can also make a mistake while copying the code. However, we can follow the Prototype pattern, which will simplify the process and reduce the chances of errors.
Example of usage
Let’s take a real-world example to illustrate the Prototype Design Pattern. Imagine we’re designing a furniture catalog. We need to create a set of furniture: chair, table, and sofa. We created a model for these objects in our previous discussion on the Builder pattern. Let’s repeat the code.
enum FurnitureKind {
case chair, table, sofa
}
enum WoodKind {
case oak, pine, cherry
}
struct Furniture {
let kind: FurnitureKind
let color: UIColor
let material: WoodKind
}Now, let’s create multiple copies of it. Every single copy will have only one difference — the furniture type.
let chair = Furniture(kind: .chair, color: .brown, material: .cherry)
let table = Furniture(kind: .table, color: .brown, material: .cherry)
let sofa = Furniture(kind: .sofa, color: .brown, material: .cherry)Let’s create a function inside our Furniture model to avoid mistakes and achieve a cleaner solution.
struct Furniture {
let kind: FurnitureKind
let color: UIColor
let material: WoodKind
func copy(
kind: FurnitureKind? = nil,
color: UIColor? = nil,
material: WoodKind? = nil
) -> Furniture {
Furniture(
kind: kind ?? self.kind,
color: color ?? self.color,
material: material ?? self.material
)
}
}And now, let’s use it:
let chair = Furniture(kind: .chair, color: .brown, material: .cherry)
let table = chair.copy(kind: .table)
let sofa = chair.copy(kind: .sofa)Much better!
When It Makes Sense — and When It Doesn’t
The Prototype pattern is designed to create new objects by cloning existing ones. Instead of rebuilding configuration from scratch, you define a base instance (a “prototype”) and produce variations from it. This avoids duplication and reduces the risk of inconsistent configurations across multiple objects.
In Swift, however, the practicality of this pattern heavily depends on whether you are dealing with value types or reference types.
Value Types: Why Structs Rarely Need Prototype
In Swift, struct types have value semantics. This means every assignment, return, or mutation implicitly produces a copy of the struct. Swift handles this efficiently through Copy-on-Write, giving you copying “for free”:
let original = Product(name: "Table", color: .brown)
var copy = original
copy.color = .blackHere, copy and original become independent without any special pattern, no custom cloning logic, and no risk of shared mutable state.
Because of this, structs rarely need the Prototype pattern.
Reference Types: Where Prototype Actually Matters
For class types, copying no longer happens automatically. Classes have reference semantics, so two variables may point to the same underlying instance:
let a = Chair()
let b = a // b references the same object!Mutating b affects a, creating bugs that are often subtle.
This is where Prototype becomes meaningful: you explicitly define how an object is cloned, whether the copying is shallow or deep, and what parts of the object graph need to be duplicated.
NSCopying: The Standard Way to Implement Copying for Classes
Swift’s Foundation framework provides a dedicated protocol — NSCopying — for reference-type cloning:
final class Furniture: NSCopying {
var name: String
var color: UIColor
var material: WoodKind
init(name: String, color: UIColor, material: WoodKind) {
self.name = name
self.color = color
self.material = material
}
func copy(with zone: NSZone? = nil) -> Any {
Furniture(
name: self.name,
color: self.color,
material: self.material
)
}
}Conclusion
This is all I wanted to say about the Prototype Design Pattern. In the following article, we will talk about Singleton (finally!) — see you there!
Check out other posts in the Design Patterns series:
- Visitor Design Pattern in Swift
- Template Method Design Pattern in Swift
- Strategy Design Pattern in Swift
- State Design Pattern in Swift
- Observer Design Pattern in Swift
- Memento Design Pattern in Swift
- Mediator Design Pattern in Swift
- Iterator Design Pattern in Swift
- Command Design Pattern in Swift
- Chain of Responsibility Design Pattern in Swift
- Proxy Design Pattern in Swift
- FlyWeight Design Pattern in Swift
- Facade Design Pattern in Swift
- Decorator Design Pattern in Swift
- Composite Design Pattern in Swift
- Bridge Design Pattern in Swift
- Adapter Design Pattern in Swift
- Singleton Design Pattern in Swift
- Builder Design Pattern in Swift
- Abstract Factory Design Pattern in Swift
- Factory Method Design Pattern in Swift
- Design Patterns: Basics
