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:

  • 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:

varnumbers1= [1,2,3]
varnumbers2=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:

classStorage {
varvalue:Int

init(value:Int) {
self.value=value
    }
}

Now wrap it in a struct:

structCounter {
privatevarstorage:Storage

init(value:Int) {
storage=Storage(value:value)
    }

varvalue:Int {
get {storage.value }
set {
if!isKnownUniquelyReferenced(&storage) {
storage=Storage(value:storage.value)
            }
storage.value=newValue
        }
    }
}

Use it:

varcounter1=Counter(value:10)
varcounter2=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 isKnownUniquelyReferenced before 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

varlist1= [1,2,3]
varlist2=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

vartext1="Hello"
vartext2=text1

text2+=" World"

print(text1)// Hello
print(text2)// Hello World

Strings also use Copy-on-Write.


Example 3: Custom Copy-on-Write wrapper

vara=Counter(value:5)
varb=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:

storage.value=newValue

Why it breaks:

Two struct instances may share the same reference, violating value semantics.

Correct approach:

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:

classCounter {
varvalue: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 isKnownUniquelyReferenced before mutating shared storage.
  • Copy-on-Write preserves value semantics efficiently.