Skip to content
beginner Phase 1 · Swift Foundations

OOP in Swift

Define classes, structs, enums, inheritance, and value vs reference semantics.

1h
4 problems
Topic Progress 0%

Classes vs Structs

Classes vs Structs

Swift provides two primary building blocks for creating custom types: classes (reference types) and structs (value types). Both can have properties, methods, initializers, and conform to protocols.

Defining a Struct

struct Point {
    var x: Double
    var y: Double
    
    func distance(to other: Point) -> Double {
        let dx = x - other.x
        let dy = y - other.y
        return sqrt(dx * dx + dy * dy)
    }
}

let p1 = Point(x: 0, y: 0)
let p2 = Point(x: 3, y: 4)
p1.distance(to: p2)  // 5.0

Structs receive a memberwise initializer automatically:

let p3 = Point(x: 1.0, y: 2.0)  // auto-generated init

Defining a Class

class Person {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    func greet() -> String {
        return "Hello, I'm \(name)"
    }
}

let person = Person(name: "Alice", age: 30)
person.greet()  // "Hello, I'm Alice"

Classes require explicit initializers. They do not get memberwise initializers.

Inheritance

Classes support single inheritance:

class Student: Person {
    var grade: String
    
    init(name: String, age: Int, grade: String) {
        self.grade = grade
        super.init(name: name, age: age)
    }
    
    override func greet() -> String {
        return "Hi, I'm \(name), a \(grade) student"
    }
}

let student = Student(name: "Bob", age: 15, grade: "A")
student.greet()  // "Hi, I'm Bob, a A student"

Use override to override parent methods. Use super to call the parent implementation.

When to Use Which?

Feature Struct Class
Type Value type Reference type
Inheritance No Yes (single)
Deinitializer No Yes
Memberwise init Auto-generated No
Identity No (===) Yes
Copy behavior Deep copy Reference copy

Apple's recommendation: Prefer structs unless you specifically need class features (inheritance, reference semantics, deinitializers).

Properties

Both structs and classes support stored and computed properties:

struct Rectangle {
    var width: Double
    var height: Double
    
    var area: Double {  // computed property
        return width * height
    }
    
    var perimeter: Double {
        return 2 * (width + height)
    }
}

let r = Rectangle(width: 10, height: 5)
r.area       // 50.0
r.perimeter  // 30.0

Methods

Both support instance methods and static methods:

struct Calculator {
    static func add(_ a: Int, _ b: Int) -> Int {
        return a + b
    }
}

Calculator.add(2, 3)  // 5

Classes additionally support class methods (overridable static methods).

Enums & Pattern Matching

Enums & Pattern Matching

Enums define a group of related values, enabling type-safe code without raw strings or integer constants.

Basic Enums

enum Direction {
    case north, south, east, west
}

var heading: Direction = .north
heading = .east  // no need to repeat Direction

Enums with Associated Values

Each case can hold different associated data:

enum Barcode {
    case upc(Int, Int, Int, Int)
    case qrCode(String)
}

let product = Barcode.qrCode("ABCDEFG")

switch product {
case .upc(let system, let manufacturer, let product, let check):
    print("UPC: \(system)-\(manufacturer)-\(product)-\(check)")
case .qrCode(let code):
    print("QR: \(code)")
}
// "QR: ABCDEFG"

Enums with Raw Values

Enums can have underlying raw values:

enum Planet: Int {
    case mercury = 1, venus, earth, mars
}

let earth = Planet(rawValue: 3)  // Optional(.earth)
let position = Planet.earth.rawValue  // 3

enum HTTPMethod: String {
    case get = "GET"
    case post = "POST"
    case put = "PUT"
    case delete = "DELETE"
}

let method = HTTPMethod.get
print(method.rawValue)  // "GET"

Pattern Matching

Swift's switch statement supports powerful pattern matching:

let number = 42

switch number {
case 0:
    print("zero")
case 1...9:
    print("single digit")
case 10...99:
    print("double digit")
default:
    print("large number")
}
// "double digit"

Tuple pattern matching:

let point = (2, 0)

switch point {
case (0, 0):
    print("origin")
case (_, 0):
    print("on x-axis")
case (0, _):
    print("on y-axis")
case (-2...2, -2...2):
    print("near origin")
default:
    print("elsewhere")
}
// "on x-axis"

where clauses:

let temp = 72

switch temp {
case ..<32 where temp > 0:
    print("freezing but above zero")
case 32...:
    print("above freezing")
default:
    print("below freezing")
}

if case (pattern matching without switch):

let result: Result<Int, Error> = .success(42)

if case .success(let value) = result {
    print("Got: \(value)")
}

Enums are one of Swift's most powerful features. They combine type safety with pattern matching to create expressive, maintainable code.

Value vs Reference Types

Value vs Reference Types

Understanding the distinction between value types and reference types is fundamental to writing correct Swift code.

Value Types

A value type is copied when assigned or passed to a function. Each copy is independent.

Swift's value types include:

  • Structs (including Array, Dictionary, Set)
  • Enums
  • Tuples
  • All basic types (Int, String, Bool, Double)
struct Temperature {
    var celsius: Double
    var fahrenheit: Double { return celsius * 9/5 + 32 }
}

var t1 = Temperature(celsius: 100)
var t2 = t1  // copy
t2.celsius = 0

print(t1.celsius)  // 100 — unchanged
print(t2.celsius)  // 0

Reference Types

A reference type is not copied; both variables refer to the same instance in memory.

Swift's reference types:

  • Classes
  • Closures (capture by reference)
  • Functions
class Thermometer {
    var celsius: Double
    init(celsius: Double) { self.celsius = celsius }
}

var t1 = Thermometer(celsius: 100)
var t2 = t1  // same instance
t2.celsius = 0

print(t1.celsius)  // 0 — changed!
print(t2.celsius)  // 0

Identity Operators

Use === and !== to check if two references point to the same instance:

let a = Thermometer(celsius: 100)
let b = a
let c = Thermometer(celsius: 100)

a === b  // true — same instance
a === c  // false — different instances
a !== c  // true

Copy-on-Write (COW)

Swift collections (Array, Dictionary, Set, String) use copy-on-write optimization. They appear to be copied but share storage until one is mutated:

var a = [1, 2, 3]
var b = a  // shares storage
// Both point to same backing store

b.append(4)  // NOW a deep copy occurs
// a = [1, 2, 3] (unchanged)
// b = [1, 2, 3, 4]

This gives you value semantics with the performance of reference types when copies aren't mutated.

When to Use Which?

Use structs when:

  • You want independent copies
  • The type represents a value (point, color, date range)
  • You don't need inheritance

Use classes when:

  • You need inheritance
  • You need reference semantics (shared mutable state)
  • You need deinitializers
  • You need identity comparison (===)

Practical Example

struct Money {
    var amount: Double
    var currency: String
    
    func display() -> String {
        return "\(currency) \(amount)"
    }
}

class BankAccount {
    var owner: String
    var balance: Double
    
    init(owner: String, balance: Double) {
        self.owner = owner
        self.balance = balance
    }
    
    func deposit(_ amount: Double) {
        balance += amount
    }
}

// Money is a value type — copying creates independence
let price1 = Money(amount: 10, currency: "USD")
var price2 = price1
price2.amount = 20
print(price1.amount)  // 10 — unchanged

// BankAccount is a reference type — copying shares the instance
let account1 = BankAccount(owner: "Alice", balance: 1000)
let account2 = account1
account2.deposit(500)
print(account1.balance)  // 1500 — shared state changed

Understanding when values are copied versus shared is essential for avoiding subtle bugs, especially in concurrent code and UI state management.

Quiz

1. What is the main difference between a struct and a class?

Question 1 options

2. Which Swift types support inheritance?

Question 2 options

3. What does `===` check?

Question 3 options

4. What is copy-on-write?

Question 4 options

5. When should you prefer a struct over a class?

Question 5 options

Flashcards

Question

What is a value type?

Answer

A type that is copied when assigned or passed. Each copy is independent. Examples: structs, enums, Int, String.

Question

What is a reference type?

Answer

A type where multiple variables point to the same instance. Changes to one affect all references. Examples: classes, closures.

Question

How do you define an enum with associated values?

Answer

Each case can have associated data: `case upc(Int, Int, Int, Int)`

Question

What does `super.init()` do?

Answer

Calls the designated initializer of the parent class in a subclass initializer.

Question

When does copy-on-write trigger a deep copy?

Answer

Only when a shared-storage copy is actually mutated, not when it's just assigned.

Revision Notes

Key Takeaways

  • 1. Structs are value types; classes are reference types
  • 2. Only classes support inheritance in Swift
  • 3. Enums can have associated values and support pattern matching
  • 4. Copy-on-write shares storage until mutation occurs
  • 5. Prefer structs unless you need class-specific features

Interview Tips

  • Explain value vs reference semantics with examples
  • Know when to use struct vs class
  • Describe copy-on-write optimization
  • Be able to implement enum pattern matching

Cheat Sheet

OOP in Swift Cheat Sheet

Struct:

  • Value type, copied on assignment
  • Auto memberwise init
  • No inheritance

Class:

  • Reference type, shared on assignment
  • Manual init required
  • Supports inheritance, deinitializers, ===

Enum:

  • Value type, defines related cases
  • Supports associated values and raw values
  • Pattern matching with switch

Key Rules:

  • Prefer structs by default
  • Use classes for inheritance or shared mutable state
  • === checks reference identity (classes only)
  • Copy-on-write optimizes collection copying