How to Use Copy-on-Write in Swift
What you’ll build or solve
You’ll learn how Copy-on-Write works in Swift and how to implement it manually in a custom struct.
When this approach works best
Copy-on-Write is most useful when:
Learn Swift on Mimo
- You wrap a reference type inside a struct.
- You build performance-sensitive data models.
- You design large value types that are frequently copied.
- You want value semantics with efficient memory usage.
This is less relevant for small data structures where copying cost is negligible.
Prerequisites
- Xcode installed
- Basic Swift syntax
- Understanding of structs vs classes
- Familiarity with value and reference semantics
Swift collections such as Array, Dictionary, and String already use Copy-on-Write automatically.
Example:
Swift
var numbers1 = [1, 2, 3]
var numbers2 = numbers1 // Shares storage
numbers2.append(4) // Copy happens here
Assignment alone does not copy memory. Mutation triggers the copy.
You do not manually control this behavior for built-in types.
Step-by-step instructions
Step 1: Understand how Copy-on-Write works
Copy-on-Write combines:
- A value type (struct)
- Internal reference storage (class)
- A uniqueness check before mutation
The struct behaves like a value type. The class stores the actual data.
When you assign the struct, both copies temporarily share the same reference. When one copy mutates, Swift checks if it is uniquely referenced. If not, it creates a new copy before modifying.
Step 2: Implement Copy-on-Write in a custom struct
When you wrap a class inside a struct, you must manually preserve value semantics.
Start with a reference type:
Swift
class Storage {
var value: Int
init(value: Int) {
self.value = value
}
}
Now wrap it in a struct:
Swift
struct Counter {
private var storage: Storage
init(value: Int) {
storage = Storage(value: value)
}
var value: Int {
get { storage.value }
set {
if !isKnownUniquelyReferenced(&storage) {
storage = Storage(value: storage.value)
}
storage.value = newValue
}
}
}
Use it:
Swift
var counter1 = Counter(value: 10)
var counter2 = counter1
counter2.value = 20
print(counter1.value) // 10
print(counter2.value) // 20
Without the uniqueness check, both variables would share the same reference.
isKnownUniquelyReferenced checks whether the underlying class instance is shared. If it is shared, you create a new copy before mutation.
What to look for
- Use a private reference type inside the struct.
- Call
isKnownUniquelyReferencedbefore mutation. - Copy storage only when multiple references exist.
- Built-in Swift collections already follow this pattern.
- Reads do not require copying. Only writes do.
Examples you can copy
Example 1: Built-in Array behavior
Swift
var list1 = [1, 2, 3]
var list2 = list1
list2.append(4)
print(list1) // [1, 2, 3]
print(list2) // [1, 2, 3, 4]
Swift performs Copy-on-Write automatically.
Example 2: String behavior
Swift
var text1 = "Hello"
var text2 = text1
text2 += " World"
print(text1) // Hello
print(text2) // Hello World
Strings also use Copy-on-Write.
Example 3: Custom Copy-on-Write wrapper
Swift
var a = Counter(value: 5)
var b = a
b.value = 100
print(a.value) // 5
print(b.value) // 100
This behaves like a true value type.
Common mistakes and how to fix them
Mistake 1: Forgetting the uniqueness check
What you might do:
Swift
storage.value = newValue
Why it breaks:
Two struct instances may share the same reference, violating value semantics.
Correct approach:
Swift
if !isKnownUniquelyReferenced(&storage) {
storage = Storage(value: storage.value)
}
storage.value = newValue
Always check before mutating.
Mistake 2: Using a class directly instead of a struct
What you might do:
Swift
class Counter {
var value: Int
}
Why it changes behavior:
Classes use shared references by default. Copies affect the same instance.
Correct approach:
Wrap the class inside a struct if you need value semantics with efficient copying.
Troubleshooting
If modifying one variable affects another, verify you are not using a plain class.
If your custom struct still shares state, confirm you used isKnownUniquelyReferenced.
If performance is a concern, profile with Instruments instead of guessing.
If copying happens too often, review where the mutation occurs.
Quick recap
- Swift collections use Copy-on-Write automatically.
- Assignment does not copy memory.
- Mutation triggers copying.
- Custom structs must implement Copy-on-Write manually.
- Use
isKnownUniquelyReferencedbefore mutating shared storage. - Copy-on-Write preserves value semantics efficiently.
Join 40M+ people learning for free on Mimo
4.8 out of 5 across 1M+ reviews
Check us out on Apple AppStore, Google Play Store, and Trustpilot