JSTAcademy
0 XP
Dashboard
Crash Courses
Structs, Classes & Enums
18 min
Masters+175 XP
Crash Courses · Masters

Structs, Classes & Enums

Understand Swift's value vs. reference semantics, design structs and classes correctly, and use enums with associated values and raw values.
18 min read+175 XP on completionCert: Swift Crash Course
Tap any word in the text below to start reading from there.

Structs, Classes & Enums

The Core Decision: Struct vs. Class

Use struct (value type) when:

  • The data is a simple value: a coordinate, a color, a date range
  • Copies should be independent (no shared mutable state)
  • The type doesn't need inheritance
  • Default in Swift — most types in the standard library are structs (String, Array, Dictionary, Int)

Use class (reference type) when:

  • Instances must be shared and mutated by multiple owners
  • The type needs inheritance
  • The type manages external resources (files, network connections)
  • Lifetime must be controlled explicitly (ARC, weak references)

---

Structs

struct Point {
    var x: Double
    var y: Double

    // Computed property
    var magnitude: Double {
        (x * x + y * y).squareRoot()
    }

    // Memberwise initializer is auto-generated by Swift
    // init(x: Double, y: Double) is provided for free

    // Custom initializer
    init(angle: Double, radius: Double) {
        self.x = radius * cos(angle)
        self.y = radius * sin(angle)
    }

    // Must mark mutating — structs are value types
    mutating func translate(dx: Double, dy: Double) {
        x += dx
        y += dy
    }

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

var p1 = Point(x: 3.0, y: 4.0)
let p2 = Point(x: 0.0, y: 0.0)

print(p1.magnitude)      // 5.0
print(p1.distance(to: p2)) // 5.0

p1.translate(dx: 1.0, dy: 0.0)
print(p1)  // Point(x: 4.0, y: 4.0)

// Value semantics demonstration
var p3 = p1
p3.x = 100.0
print(p1.x)  // 4.0 — p1 is unchanged

---

Classes

class BankAccount {
    let owner: String
    private(set) var balance: Double  // readable externally, writable only internally

    // Property observer
    var transactionCount: Int = 0 {
        didSet {
            print("Transaction count updated: \(transactionCount)")
        }
    }

    init(owner: String, initialBalance: Double = 0) {
        self.owner = owner
        self.balance = initialBalance
    }

    func deposit(_ amount: Double) {
        precondition(amount > 0, "Amount must be positive")
        balance += amount
        transactionCount += 1
    }

    @discardableResult
    func withdraw(_ amount: Double) -> Bool {
        guard amount > 0 && amount <= balance else { return false }
        balance -= amount
        transactionCount += 1
        return true
    }

    var description: String {
        "BankAccount[\(owner): $\(String(format: "%.2f", balance)), \(transactionCount) txns]"
    }
}

let acct = BankAccount(owner: "Jordan", initialBalance: 500.0)
acct.deposit(250.0)
acct.withdraw(100.0)
print(acct.description)

// Reference semantics demonstration
let acct2 = acct   // same object — just another reference
acct2.deposit(1000)
print(acct.balance)  // 1650.0 — acct also reflects the change

---

Enums

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

let dir = Direction.north
switch dir {
case .north: print("Going north")
case .south: print("Going south")
case .east:  print("Going east")
case .west:  print("Going west")
}

// Raw values
enum Planet: Int {
    case mercury = 1, venus, earth, mars  // auto-increments from 1
}
print(Planet.earth.rawValue)  // 3
print(Planet(rawValue: 1)!)   // mercury

// String raw values
enum HTTPMethod: String {
    case get = "GET", post = "POST", put = "PUT", delete = "DELETE"
}
print(HTTPMethod.get.rawValue)  // "GET"

// Associated values — the most powerful feature
enum NetworkResult {
    case success(data: Data, statusCode: Int)
    case failure(error: Error)
    case loading
}

func handleResult(_ result: NetworkResult) {
    switch result {
    case .success(let data, let code):
        print("Success \(code): \(data.count) bytes")
    case .failure(let error):
        print("Error: \(error.localizedDescription)")
    case .loading:
        print("Loading...")
    }
}

// CaseIterable — iterate all cases
enum Season: CaseIterable {
    case spring, summer, fall, winter
}
print("Seasons:", Season.allCases)  // [spring, summer, fall, winter]
for season in Season.allCases {
    print(season)
}

---

Enum Methods and Computed Properties

enum Weekday: Int, CaseIterable {
    case monday = 1, tuesday, wednesday, thursday, friday, saturday, sunday

    var isWeekend: Bool {
        self == .saturday || self == .sunday
    }

    var name: String {
        switch self {
        case .monday:    return "Monday"
        case .tuesday:   return "Tuesday"
        case .wednesday: return "Wednesday"
        case .thursday:  return "Thursday"
        case .friday:    return "Friday"
        case .saturday:  return "Saturday"
        case .sunday:    return "Sunday"
        }
    }

    func next() -> Weekday {
        let nextRaw = (rawValue % 7) + 1
        return Weekday(rawValue: nextRaw)!
    }
}

print(Weekday.friday.isWeekend)   // false
print(Weekday.saturday.isWeekend) // true
print(Weekday.friday.next().name) // Saturday
0%