Swift
Pass By Value
Pass By Reference
Programming
Swift Programming

Is Swift Pass By Value or Pass By Reference

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Swift is often described as pass-by-value for value types and pass-by-reference behavior for class instances, but that shorthand hides important details. Function parameters are always passed by value semantically, yet the value being copied may itself be a reference to an object. Understanding this distinction is essential for writing predictable Swift code, especially with structs, classes, and inout parameters.

Value Types Versus Reference Types

Swift has two major categories relevant to parameter passing:

  • value types such as struct, enum, and tuple
  • reference types such as class

When you assign or pass a value type, Swift conceptually copies the value. When you assign or pass a class instance, the copied value is the reference, so both variables point to the same underlying object.

Value Type Example

swift
1struct Counter {
2    var value: Int
3}
4
5func increment(_ c: Counter) -> Counter {
6    var copy = c
7    copy.value += 1
8    return copy
9}
10
11let original = Counter(value: 10)
12let updated = increment(original)
13
14print(original.value) // 10
15print(updated.value)  // 11

original is unchanged because the function operates on a copy.

Reference Type Example

swift
1final class Box {
2    var value: Int
3    init(_ value: Int) { self.value = value }
4}
5
6func mutate(_ box: Box) {
7    box.value += 1
8}
9
10let a = Box(10)
11mutate(a)
12print(a.value) // 11

Here function parameter is still passed by value, but that value is a reference. Mutating the referenced object is visible outside the function.

inout Creates Explicit Write-Back Semantics

Swift does not have traditional pass-by-reference for ordinary parameters. Instead, it provides inout for explicit mutation of caller state.

swift
1func swapInts(_ a: inout Int, _ b: inout Int) {
2    let temp = a
3    a = b
4    b = temp
5}
6
7var x = 1
8var y = 2
9swapInts(&x, &y)
10print(x, y) // 2 1

inout makes mutation intent explicit at call site through the & prefix.

Copy-on-Write Nuance in Swift Collections

Swift arrays, dictionaries, and strings are value types, but they use copy-on-write optimization. That means physical copying may be delayed until mutation happens.

swift
1var a = [1, 2, 3]
2var b = a      // no immediate deep copy required
3b.append(4)    // copy occurs here before mutation
4
5print(a) // [1, 2, 3]
6print(b) // [1, 2, 3, 4]

Semantically this is still value behavior, even if runtime optimizes memory operations.

Immutable and Mutable Parameters

Function parameters are constants by default in Swift. You cannot reassign them directly.

swift
1func demo(_ n: Int) {
2    // n += 1 // compile error
3    var local = n
4    local += 1
5    print(local)
6}

This encourages clear local mutation instead of accidental parameter reassignment.

Practical Mental Model

A reliable mental model:

  • every parameter is passed as a value
  • value might be data itself or a reference handle
  • 'inout is the mechanism for caller-side mutation'

This model removes confusion around statements like "class is pass-by-reference." The effect is reference-like, but the parameter passing mechanism remains value-based.

API Design Implications

Choosing struct or class affects how APIs behave under assignment and parameter passing.

Use struct when:

  • independent copies should not share mutable state
  • predictable local mutation is preferred
  • thread-safety and value semantics are important

Use class when:

  • shared identity matters
  • mutation should be visible across holders
  • lifecycle management and inheritance are needed

Type choice directly shapes side effects and testability.

Debugging Unexpected Mutation

If state changes unexpectedly after a function call, inspect whether you passed a class reference or value type.

Helpful checks:

  • print object identity for classes with ObjectIdentifier
  • verify whether collection mutation triggered copy-on-write
  • audit APIs for inout usage

Small identity tests catch many hidden shared-state bugs.

Common Pitfalls

  • Saying Swift is only pass-by-reference or only pass-by-value without type context.
  • Expecting class instances to auto-copy when passed to functions.
  • Forgetting inout when function must modify caller variables.
  • Misreading copy-on-write behavior as true shared mutable value semantics.
  • Choosing classes for simple data models and introducing avoidable side effects.

Summary

  • Swift parameters are passed by value semantically.
  • Value types copy data behavior, while class parameters copy references.
  • 'inout provides explicit caller-variable mutation.'
  • Copy-on-write optimizes value types without changing value semantics.
  • Correct type design is the key to predictable mutation behavior in Swift.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.