🦅 Apple Swift Language

The Swift
Programming Guide

A comprehensive reference and beginner tutorial for developers who already know how to code — just not yet in Swift.

Swift 5.9+ Xcode 15+ Difficulty Beginner → Intermediate iOS / macOS / Linux

What is Swift?

Swift is Apple's modern, open-source programming language designed for safety, speed, and expressiveness. Released in 2014, it replaced Objective-C as the primary language for iOS, macOS, watchOS, tvOS, and now server-side Linux apps. It combines ideas from C, Python, Rust, Haskell, and Ruby into a cohesive, statically-typed language.

🔒

Type Safe

Catches type errors at compile time, eliminating entire classes of bugs.

Fast

Compiles to native machine code; benchmark speed rivals C++.

🛡️

Memory Safe

ARC (Automatic Reference Counting) handles memory without GC pauses.

🧩

Protocol-Oriented

Prefer protocols and value types over class hierarchies.

🎯

Optionals

Null safety is built into the type system — no null pointer exceptions.

🌐

Open Source

Runs on Linux and Windows via Swift.org toolchain.

Installation

PlatformMethodREPL
macOSInstall Xcode from the App Store (includes Swift)swift repl
LinuxDownload toolchain from swift.org/downloadswift repl
WindowsSwift for Windows installer at swift.orgswift repl
Onlineswiftfiddle.com or Swift Playgrounds (iPad/Mac)

Running a Swift file

# Compile + run in one step (script mode)
swift hello.swift

# Or compile to a binary
swiftc hello.swift -o hello
./hello

# Interactive REPL
swift repl
ℹ️

Swift Package Manager (SPM) is the official build system for multi-file projects. Run swift package init --type executable to scaffold a new project.


Syntax Reference

If you know Python, JavaScript, Kotlin, or C#, Swift will feel immediately familiar. Here's a one-page orientation for those switching languages.

ConceptSwiftEquivalent (Python / JS / C++)
Variablevar x = 10x = 10 / let x = 10 / int x = 10
Constantlet PI = 3.14PI = 3.14 / const PI / const double
String interpolation"Hello \(name)"f"Hello {name}" / `Hello ${name}`
Printprint("hi")print("hi") / console.log / cout
Functionfunc add(_ a: Int, _ b: Int) -> Intdef / function / int
Array[1, 2, 3]same across languages
Dictionary["key": "value"]{} dict / Map / unordered_map
For loopfor i in 1...5 { }for i in range(6) / for(let i=1;i<=5;i++)
If / elseif x > 0 { } else { }same (Swift drops the parens)
Nil / nullnilNone / null / nullptr
Optional typevar s: String?No direct equivalent in C++/Java
Classclass Dog { }same
Structstruct Point { } (value type)Same keyword; Swift structs are powerful
Protocolprotocol Drawable { }interface / ABC / concept
Enumenum Dir { case north, south }Similar; Swift enums can hold data
Guard clauseguard let x = opt else { return }Early-return pattern
Type annotationvar score: Int = 0score: int = 0 (Python 3.6+)
SemicolonsOptional (omitted by convention)Required in C/Java; optional in JS
No main() neededScripts run top-to-bottom; apps use @mainLike Python scripts
🦅

Swift does not require parentheses around if/while conditions, but does require curly braces { } around every block — even single-line ones.


Hello, World!

The simplest Swift program — save as hello.swift and run with swift hello.swift.

// hello.swift — the classic first program
print("Hello, World!")

That's it. No main(), no imports, no semicolons required. Now a slightly richer version:

// hello2.swift — string interpolation & multi-print
let language = "Swift"
let version: Double = 5.9

print("Hello from \(language) \(version)!")
print("2 + 2 = \(2 + 2)")
print("π ≈ \(Double.pi)")

Output:

Hello from Swift 5.9!
2 + 2 = 4
π ≈ 3.141592653589793
💡

print() appends a newline by default. Use print("text", terminator: "") to suppress it.


Variables & Constants

var declares a mutable variable. let declares an immutable constant. Prefer let — the compiler warns you when a var is never mutated.

var score: Int = 0       // mutable, explicit type
score = 10               // reassignment OK

let maxScore = 100       // immutable; type inferred as Int
// maxScore = 99         // ❌ ERROR — cannot reassign a let

var name = "Alice"       // String inferred
var temp: Double = 98.6
var isActive: Bool = true

// Multiple assignment
var (x, y) = (10, 20)

Naming conventions

  • camelCase for variables, functions, and properties: myVariable, calculateArea()
  • PascalCase for types: Int, String, MyClass
  • Any Unicode is valid: let 🐦 = "swift" (though not recommended)

Types & Type Inference

Swift is statically typed but rarely requires you to write types explicitly, thanks to type inference.

TypeLiteralNotes
Int42Platform-native (64-bit on modern hardware). Also: Int8/16/32/64, UInt
Double3.1464-bit float. Preferred over Float (32-bit)
Booltrue / falseNo integer conversion (0 ≠ false)
String"Hello"Unicode-correct, value type
Character"A"Single Unicode grapheme cluster
Array<T>[1, 2, 3]Shorthand: [Int]
Dictionary<K,V>["a": 1]Shorthand: [String: Int]
Set<T>Set([1, 2, 3])Unordered, unique elements
Tuple(1, "hi")Fixed-size, heterogeneous
Optional<T>nilShorthand: String?
// Type inference
let age      = 25        // Int
let price    = 9.99     // Double
let greeting = "Hello"  // String

// Explicit annotation overrides inference
let pi: Float = 3.14   // Float, not Double

// Type conversion (no implicit casting!)
let intVal: Int = 5
let dblVal = Double(intVal) * 1.5  // must cast explicitly

// Tuples
let point = (x: 3, y: 7)
print(point.x)  // 3

// Type aliases
typealias Celsius = Double
var bodyTemp: Celsius = 37.0
⚠️

Swift has no implicit type coercion. You cannot add an Int to a Double without an explicit cast — this is intentional to prevent precision bugs.


Optionals: Swift's Superpower

An Optional is a type that can hold either a value or nil (absence of value). It is impossible in Swift to accidentally use nil — you must explicitly unwrap optionals first.

// Declaring optionals
var username: String?  // = nil by default
username = "Bob"

// ── 1. Optional Binding (safe) ──────────────────────
if let name = username {
    print("Hello, \(name)")   // name is String here
} else {
    print("No user")
}

// Shorthand (same name): Swift 5.7+
if let username {
    print("Got \(username)")
}

// ── 2. Guard Let (early exit pattern) ──────────────
func greet(user: String?) {
    guard let user else {
        print("No user provided")
        return
    }
    print("Welcome, \(user)!")  // user is non-optional here
}

// ── 3. Nil Coalescing ─────────────────────────────
let display = username ?? "Guest"   // "Bob" or "Guest"

// ── 4. Optional Chaining ──────────────────────────
let count = username?.count         // Int? (nil if username is nil)

// ── 5. Force Unwrap (use sparingly) ──────────────
let definitelyString = username!    // crashes if nil!
⚠️

Force unwrapping (!) crashes at runtime if the optional is nil. Use if let or guard let instead. Reserve ! only when you are 100% certain the value exists.


Operators

CategoryOperatorsNotes
Arithmetic+ - * / %Standard. % is remainder, not modulo
Assignment= += -= *= /=Note: ++ and -- were removed in Swift 3
Comparison== != < > <= >=Works for any Equatable / Comparable
Logical&& || !Short-circuit evaluation
Range1...5 1..<5Closed vs half-open. Very common in loops
Nil coalescing??a ?? b → a if non-nil, else b
Ternarya ? b : cSame as C/Java
Identity=== !==Reference equality (classes only)
Bitwise& | ^ ~ << >>Same as C
// Range operators
for i in 1...5  { } // 1, 2, 3, 4, 5 (closed)
for i in 1..<5  { } // 1, 2, 3, 4   (half-open)

// Overflow operators (safe wrapping arithmetic)
let max = UInt8.max             // 255
let wrapped = max &+ 1          // 0  (wraps)

// Operator overloading (custom types)
struct Vec2 {
    var x, y: Double
    static func + (a: Vec2, b: Vec2) -> Vec2 {
        Vec2(x: a.x + b.x, y: a.y + b.y)
    }
}

Control Flow

If / Else if / Else

let temp = 72

if temp < 50 {
    print("Cold")
} else if temp < 75 {
    print("Comfortable")
} else {
    print("Hot")
}

Switch

Swift's switch is far more powerful than C's. It does not fall through by default and works with any type.

let grade = "B"

switch grade {
case "A":
    print("Excellent")
case "B", "C":
    print("Good / Average")
case "D"..."F":           // range matching
    print("Needs work")
default:
    print("Unknown grade")
}

// switch with where clause
let num = 42
switch num {
case let n where n < 0:   print("Negative")
case let n where n.isMultiple(of: 2): print("Even: \(n)")
default: print("Odd")
}

Loops

// for-in with range
for i in 1...5 {
    print(i)
}

// for-in over collection
let fruits = ["apple", "banana", "cherry"]
for fruit in fruits { print(fruit) }

// enumerated (index + value)
for (i, fruit) in fruits.enumerated() {
    print("\(i): \(fruit)")
}

// while
var count = 3
while count > 0 { count -= 1 }

// repeat-while (do-while in other languages)
repeat {
    print("runs at least once")
} while false

// Control keywords
break     // exit loop or switch
continue  // skip to next iteration
fallthrough  // opt into switch fall-through

// Labeled loops
outer: for i in 0...3 {
    for j in 0...3 {
        if i == j { break outer }  // breaks both loops
    }
}

Functions

Functions in Swift have argument labels (external name) and parameter names (internal name) — a uniquely Swift concept that makes call sites read like natural English.

// Basic function
func greet(name: String) -> String {
    return "Hello, \(name)!"
}
greet(name: "Alice")   // label required at call site

// Omit external label with _
func square(_ n: Int) -> Int { n * n }
square(5)   // no label needed

// Separate external & internal label
func move(to destination: String) {
    print("Moving to \(destination)")
}
move(to: "Paris")  // reads like English

// Default parameter values
func createTag(name: String, color: String = "gray") -> String {
    "<\(name) color='\(color)'>"
}
createTag(name: "div")                 // uses "gray"
createTag(name: "div", color: "red")

// Multiple return values via tuple
func minMax(of array: [Int]) -> (min: Int, max: Int) {
    (array.min()!, array.max()!)
}
let result = minMax(of: [3, 1, 7, 2])
print(result.min)  // 1

// Variadic parameters
func sum(_ numbers: Int...) -> Int {
    numbers.reduce(0, +)
}
sum(1, 2, 3, 4)  // 10

// inout parameters (pass by reference)
func doubleValue(_ n: inout Int) { n *= 2 }
var x = 5
doubleValue(&x)  // x is now 10

// Functions as first-class values
let fn: (Int) -> Int = square
print(fn(6))  // 36

Single-expression functions

If the function body is a single expression, you can omit return:

func cube(_ n: Int) -> Int { n * n * n }

Closures

Closures are self-contained blocks of functionality — like anonymous functions (lambdas). They can capture variables from their surrounding context.

// Full closure syntax
let add: (Int, Int) -> Int = { (a: Int, b: Int) -> Int in
    return a + b
}

// Shorthand: infer types, omit return, use $0/$1
let addShort: (Int, Int) -> Int = { $0 + $1 }

// ── Higher-order functions ───────────────────────────
let nums = [1, 2, 3, 4, 5]

// map — transform each element
let doubled = nums.map { $0 * 2 }           // [2,4,6,8,10]

// filter — keep elements matching predicate
let evens   = nums.filter { $0.isMultiple(of: 2) }  // [2,4]

// reduce — fold into single value
let total   = nums.reduce(0) { $0 + $1 }       // 15
let total2  = nums.reduce(0, +)                // same, operator shorthand

// sorted
let words  = ["banana", "apple", "cherry"]
let sorted = words.sorted { $0 < $1 }          // ["apple","banana","cherry"]

// compactMap — map + unwrap optionals
let raw   = ["1", "two", "3"]
let ints  = raw.compactMap { Int($0) }           // [1, 3]

// Trailing closure syntax (when last argument is closure)
nums.forEach { print($0) }

// Capturing values
func makeCounter() -> () -> Int {
    var count = 0
    return { count += 1; return count }
}
let counter = makeCounter()
counter()  // 1
counter()  // 2

Collections

Arrays

var fruits: [String] = ["apple", "banana"]
fruits.append("cherry")
fruits.insert("avocado", at: 0)
fruits.remove(at: 1)
print(fruits.count)         // count
print(fruits.isEmpty)       // false
print(fruits.contains("apple"))   // true
print(fruits.joined(separator: ", "))  // join to string
let sliced = fruits[0...1]    // ArraySlice

Dictionaries

var scores: [String: Int] = ["Alice": 95, "Bob": 82]
scores["Carol"] = 91          // add/update
scores.removeValue(forKey: "Bob")
let aliceScore = scores["Alice"]  // Optional Int!

for (name, score) in scores {
    print("\(name): \(score)")
}
print(scores.keys)    // keys collection
print(scores.values)  // values collection

Sets

var tags: Set = ["swift", "ios", "apple"]
tags.insert("mobile")
tags.remove("apple")
print(tags.contains("ios"))    // true

let a: Set = [1, 2, 3]
let b: Set = [2, 3, 4]
print(a.union(b))               // {1,2,3,4}
print(a.intersection(b))       // {2,3}
print(a.subtracting(b))        // {1}

Strings

Swift strings are Unicode-correct value types. They are not arrays of characters — every element is a Unicode grapheme cluster.

var s = "Hello, 🌍"
print(s.count)             // 9 (not byte count)
print(s.isEmpty)           // false
print(s.uppercased())      // HELLO, 🌍
print(s.hasPrefix("Hello"))  // true
print(s.hasSuffix("🌍"))     // true
print(s.contains(","))      // true
print(s.replacingOccurrences(of: "Hello", with: "Hi"))

// String interpolation
let n = 42
let msg = "Answer = \(n * 2)"

// Multi-line string literals
let poem = """
    Roses are red,
    Violets are blue,
    Swift is fast,
    And safe too.
    """

// Split and join
let csv   = "a,b,c,d"
let parts = csv.split(separator: ",")   // ["a","b","c","d"]
let back  = parts.joined(separator: "-") // "a-b-c-d"

// String to Int and back
let maybeInt: Int? = Int("123")    // Optional
let str = String(42)               // "42"

Structs & Classes

🦅

Swift's key rule: Structs are value types (copied on assignment). Classes are reference types (shared). Prefer structs unless you need inheritance or reference semantics.

Struct (Value Type)

struct Rectangle {
    var width: Double
    var height: Double

    // Computed property
    var area: Double { width * height }

    // Must mark mutating methods in structs
    mutating func scale(by factor: Double) {
        width  *= factor
        height *= factor
    }
}

var r1 = Rectangle(width: 5, height: 3)
var r2 = r1      // COPIED — r2 is independent
r2.width = 10    // doesn't affect r1
print(r1.width)  // still 5

Class (Reference Type)

class Animal {
    var name: String
    var age: Int

    // Designated initializer
    init(name: String, age: Int) {
        self.name = name
        self.age  = age
    }

    // Convenience initializer
    convenience init(name: String) {
        self.init(name: name, age: 0)
    }

    func speak() -> String { "..." }

    // Deinitializer (called when ref count hits 0)
    deinit { print("\(name) freed") }
}

// Inheritance
class Dog: Animal {
    var breed: String

    init(name: String, breed: String) {
        self.breed = breed
        super.init(name: name, age: 0)
    }

    override func speak() -> String { "Woof!" }
}

let dog1 = Dog(name: "Rex", breed: "Labrador")
let dog2 = dog1   // REFERENCE — same object!
dog2.name = "Max"
print(dog1.name)  // "Max" — both point to same instance

// Properties: stored, computed, property observers
class Thermostat {
    var celsius: Double = 0 {
        willSet { print("About to change to \(newValue)") }
        didSet  { print("Changed from \(oldValue)") }
    }
    var fahrenheit: Double {
        get { celsius * 9 / 5 + 32 }
        set { celsius = (newValue - 32) * 5 / 9 }
    }
}

Enumerations

Swift enums are first-class types with methods, computed properties, and the ability to carry associated values — making them far more powerful than in C or Java.

// Basic enum
enum Direction {
    case north, south, east, west
}
var heading = Direction.north
heading = .east  // shorthand when type is known

// Raw values (like C enum)
enum Planet: Int {
    case mercury = 1, venus, earth, mars
}
print(Planet.earth.rawValue)   // 3
let p = Planet(rawValue: 2)   // Optional<Planet>

// Associated values (powerful!)
enum Barcode {
    case upc(Int, Int, Int, Int)
    case qrCode(String)
}

let code: Barcode = .qrCode("ABCDEF")

switch code {
case .upc(let ns, let m, let p, let c):
    print("UPC: \(ns)-\(m)-\(p)-\(c)")
case .qrCode(let str):
    print("QR: \(str)")
}

// Enum with methods
enum Suit: String, CaseIterable {
    case hearts, diamonds, clubs, spades
    var isRed: Bool { self == .hearts || self == .diamonds }
}

// Iterate all cases (CaseIterable)
for suit in Suit.allCases {
    print(suit.rawValue, suit.isRed)
}

Protocols

Protocols define a blueprint of requirements. Any type (struct, class, enum) can conform to a protocol. This is Swift's alternative to abstract classes and interfaces.

protocol Describable {
    var description: String { get }
    func describe()
}

// Default implementations via extensions
extension Describable {
    func describe() { print(description) }
}

struct Car: Describable {
    var make: String
    var description: String { "Car: \(make)" }
}

let car = Car(make: "Tesla")
car.describe()   // "Car: Tesla" (from extension)

// Protocol composition
protocol Named  { var name: String { get } }
protocol Aged   { var age: Int    { get } }

func intro(entity: Named & Aged) {
    print("\(entity.name) is \(entity.age)")
}

// Common built-in protocols
// Equatable  — == and !=
// Hashable   — can be used in Set/Dict keys
// Comparable — < > sorting
// Codable    — JSON encode/decode
// CustomStringConvertible — print() hook

struct Point: Equatable, Hashable, CustomStringConvertible {
    let x, y: Int
    var description: String { "(\(x), \(y))" }
}

let set: Set<Point> = [Point(x: 1, y: 2), Point(x: 3, y: 4)]

Extensions

Extensions add new functionality to any type — even types you don't own like Int or String.

extension Int {
    var isEven: Bool { self % 2 == 0 }
    func times(_ action: () -> Void) {
        for _ in 0..<self { action() }
    }
}
print(7.isEven)  // false
3.times { print("Hi") }  // prints 3 times

Generics

Generics let you write flexible, reusable code that works with any type while maintaining type safety.

// Generic function
func swap<T>(_ a: inout T, _ b: inout T) {
    let temp = a; a = b; b = temp
}

var x = 5, y = 10
swap(&x, &y)   // works for any type T

// Generic Stack (type constraint)
struct Stack<Element> {
    private var items: [Element] = []
    mutating func push(_ item: Element) { items.append(item) }
    mutating func pop()  -> Element?   { items.popLast() }
    var top: Element? { items.last }
}

var stack = Stack<Int>()
stack.push(1); stack.push(2)
print(stack.pop()!)  // 2

// Type constraints
func findMax<T: Comparable>(_ arr: [T]) -> T? {
    arr.max()
}
findMax([3, 1, 7, 2])       // 7
findMax(["banana", "apple"]) // "banana"

Error Handling

Swift uses typed error handling with throw, try, and catch. Errors must conform to the Error protocol.

// 1. Define errors
enum ValidationError: Error {
    case tooShort(minimum: Int)
    case containsSpaces
    case empty
}

// 2. Throwing function
func validateUsername(_ name: String) throws -> String {
    guard !name.isEmpty else { throw ValidationError.empty }
    guard name.count >= 3 else {
        throw ValidationError.tooShort(minimum: 3)
    }
    guard !name.contains(" ") else {
        throw ValidationError.containsSpaces
    }
    return name.lowercased()
}

// 3. Calling and catching
do {
    let clean = try validateUsername("Alice")
    print("Valid: \(clean)")
} catch ValidationError.tooShort(let min) {
    print("Username must be >= \(min) chars")
} catch ValidationError.containsSpaces {
    print("No spaces allowed")
} catch {
    print("Error: \(error)")  // catch-all
}

// try? converts to Optional (nil on error)
let result = try? validateUsername("hi")  // nil

// try! force-unwraps (crashes on error)
let forced = try! validateUsername("alice123")

// defer — cleanup that always runs
func processFile() throws {
    defer { print("File closed") }  // runs no matter what
    throw ValidationError.empty
}

Concurrency: async / await

Swift 5.5+ has first-class async/await for structured concurrency — cleaner than callbacks or Combine chains.

// Async function
func fetchUser(id: Int) async throws -> String {
    // Simulate network delay
    try await Task.sleep(nanoseconds: 1_000_000_000)
    return "User \(id)"
}

// Calling async code
async {
    do {
        let user = try await fetchUser(id: 42)
        print(user)
    } catch {
        print("Error: \(error)")
    }
}

// Parallel tasks with async let
func fetchAll() async throws {
    async let user1 = fetchUser(id: 1)   // runs in parallel
    async let user2 = fetchUser(id: 2)
    let (u1, u2) = try await (user1, user2)  // await both
    print(u1, u2)
}

// Actors — thread-safe reference types
actor BankAccount {
    var balance: Double = 0
    func deposit(_ amount: Double) { balance += amount }
}
let acct = BankAccount()
await acct.deposit(100)
ℹ️

Grand Central Dispatch (GCD) still works in Swift, but async/await is preferred for new code. SwiftUI uses @MainActor to ensure UI updates happen on the main thread.


Beginner Project: Personal Finance Tracker

This single-file project covers structs, enums, protocols, generics, closures, error handling, collections, optionals, computed properties, and extensions — almost everything covered in this guide.

Save as finance.swift and run with swift finance.swift.

// ═══════════════════════════════════════════════════════════
//  finance.swift — Personal Finance Tracker
//  Covers: structs, enums, protocols, closures, error
//          handling, generics, optionals, collections, etc.
// ═══════════════════════════════════════════════════════════

import Foundation

// ── 1. ENUMS: Transaction categories ─────────────────────
enum Category: String, CaseIterable {
    case food      = "🍔 Food"
    case transport = "🚗 Transport"
    case health    = "💊 Health"
    case income    = "💵 Income"
    case other     = "📦 Other"
}

// ── 2. STRUCT: A single transaction ──────────────────────
struct Transaction: CustomStringConvertible {
    let id: UUID              // unique identifier
    let date: Date
    let amount: Double
    let category: Category
    let note: String

    var isExpense: Bool { category != .income }

    // Computed property: formatted amount string
    var formattedAmount: String {
        let sign = isExpense ? "-" : "+"
        return "\(sign)$\(String(format: "%.2f", abs(amount)))"
    }

    // Protocol conformance: CustomStringConvertible
    var description: String {
        "[\(category.rawValue)] \(formattedAmount)  — \(note)"
    }

    // Memberwise init with defaults
    init(amount: Double, category: Category, note: String = "") {
        self.id       = UUID()
        self.date     = Date()
        self.amount   = amount
        self.category = category
        self.note     = note
    }
}

// ── 3. ERROR HANDLING: Budget errors ─────────────────────
enum BudgetError: Error {
    case negativeBudget(Double)
    case negativeAmount(Double)
    case noTransactions
}

// ── 4. PROTOCOL: Something that can produce a summary ────
protocol Summarizable {
    func summary() -> String
}

// ── 5. GENERIC HELPER: Find items matching predicate ─────
func find<T>(in items: [T], where predicate: (T) -> Bool) -> [T] {
    items.filter(predicate)
}

// ── 6. STRUCT: Ledger (the main manager) ─────────────────
struct Ledger: Summarizable {
    private var transactions: [Transaction] = []
    var monthlyBudget: Double

    init(budget: Double) throws {
        guard budget >= 0 else {
            throw BudgetError.negativeBudget(budget)
        }
        self.monthlyBudget = budget
    }

    // Add a transaction with validation
    mutating func add(_ tx: Transaction) throws {
        guard tx.amount > 0 else {
            throw BudgetError.negativeAmount(tx.amount)
        }
        transactions.append(tx)
    }

    // Computed properties
    var totalIncome: Double {
        transactions
            .filter { !$0.isExpense }
            .reduce(0) { $0 + $1.amount }
    }
    var totalExpenses: Double {
        transactions
            .filter { $0.isExpense }
            .reduce(0) { $0 + $1.amount }
    }
    var balance: Double { totalIncome - totalExpenses }
    var budgetRemaining: Double { monthlyBudget - totalExpenses }

    // Filter by category (uses generic helper)
    func transactions(in category: Category) -> [Transaction] {
        find(in: transactions) { $0.category == category }
    }

    // Most expensive category (uses map + sort)
    func spendingByCategory() -> [(Category, Double)] {
        Category.allCases
            .map { cat -> (Category, Double) in
                let total = transactions(in: cat)
                    .reduce(0) { $0 + $1.amount }
                return (cat, total)
            }
            .filter { $0.1 > 0 }
            .sorted { $0.1 > $1.1 }
    }

    // Largest single expense (optional return)
    func largestExpense() throws -> Transaction {
        let expenses = transactions.filter { $0.isExpense }
        guard let max = expenses.max(by: { $0.amount < $1.amount }) else {
            throw BudgetError.noTransactions
        }
        return max
    }

    // Protocol method: summary string
    func summary() -> String {
        let divider = String(repeating: "─", count: 40)
        var lines = [String]()
        lines.append("\n\(divider)")
        lines.append("  📒 FINANCE TRACKER SUMMARY")
        lines.append(divider)
        lines.append("  Budget:    $\(String(format: "%.2f", monthlyBudget))")
        lines.append("  Income:   +$\(String(format: "%.2f", totalIncome))")
        lines.append("  Expenses: -$\(String(format: "%.2f", totalExpenses))")
        lines.append("  Balance:   $\(String(format: "%.2f", balance))")
        lines.append("  Remaining: $\(String(format: "%.2f", budgetRemaining))")
        lines.append(divider)
        lines.append("  Spending by category:")
        for (cat, total) in spendingByCategory() {
            lines.append("    \(cat.rawValue): $\(String(format: "%.2f", total))")
        }
        lines.append(divider)
        return lines.joined(separator: "\n")
    }
}

// ── 7. EXTENSION on Double: currency formatting ───────────
extension Double {
    var asCurrency: String { "$\(String(format: "%.2f", self))" }
}

// ── 8. ENTRY POINT: main program ─────────────────────────
do {
    // Initialise ledger (throwing init)
    var ledger = try Ledger(budget: 2000.00)

    // Add some transactions
    let txData: [(Double, Category, String)] = [
        (3200.00, .income,    "Monthly salary"),
        (  85.50, .food,      "Grocery run"),
        (  12.99, .food,      "Coffee shop"),
        (  45.00, .transport, "Gas"),
        ( 200.00, .health,    "Gym membership"),
        (  60.00, .food,      "Restaurant dinner"),
        (  29.99, .other,     "Netflix"),
        ( 150.00, .transport, "Car repair"),
        ( 500.00, .income,    "Freelance project"),
    ]

    for (amt, cat, note) in txData {
        let tx = Transaction(amount: amt, category: cat, note: note)
        try ledger.add(tx)
    }

    // Print all food transactions using the generic find()
    print("\n🍔 Food Transactions:")
    ledger.transactions(in: .food).forEach { print("  \($0)") }

    // Optionals in action: largest expense
    let biggest = try ledger.largestExpense()
    print("\n💸 Largest expense: \(biggest)")

    // Closures: custom report using higher-order functions
    let overFifty = ledger.transactions(in: .food)
        .filter { $0.amount > 50 }
        .map { $0.formattedAmount }
    print("\n🔍 Food expenses over $50: \(overFifty)")

    // Extension on Double in use
    print("\n💰 Balance: \(ledger.balance.asCurrency)")

    // Print summary (Summarizable protocol)
    print(ledger.summary())

} catch BudgetError.negativeBudget(let val) {
    print("❌ Invalid budget: \(val)")
} catch BudgetError.negativeAmount(let val) {
    print("❌ Negative amount: \(val)")
} catch BudgetError.noTransactions {
    print("❌ No transactions found")
} catch {
    print("❌ Unexpected error: \(error)")
}

Expected Output

🍔 Food Transactions:
  [🍔 Food] -$85.50  — Grocery run
  [🍔 Food] -$12.99  — Coffee shop
  [🍔 Food] -$60.00  — Restaurant dinner

💸 Largest expense: [💊 Health] -$200.00  — Gym membership

🔍 Food expenses over $50: ["-$85.50", "-$60.00"]

💰 Balance: $3116.53

────────────────────────────────────────
  📒 FINANCE TRACKER SUMMARY
────────────────────────────────────────
  Budget:    $2000.00
  Income:   +$3700.00
  Expenses: -$583.47
  Balance:   $3116.53
  Remaining: $1416.53
────────────────────────────────────────
  Spending by category:
    💊 Health: $200.00
    🚗 Transport: $195.00
    🍔 Food: $158.49
    📦 Other: $29.99
────────────────────────────────────────

What this project covers

FeatureWhere used
Structs (value types)Transaction, Ledger
Enums with raw values & CaseIterableCategory, BudgetError
ProtocolsSummarizable, CustomStringConvertible
Genericsfind<T>()
Closures / HOF.filter, .map, .reduce, .sorted
Error handlingthrows, do/try/catch
Optionals & guard letlargestExpense()
Computed propertiestotalIncome, balance, formattedAmount
ExtensionsDouble.asCurrency
Collections (Array, Dictionary)Throughout Ledger
String interpolation + formattingsummary()
TuplestxData array of tuples

Where to Go From Here

📱

SwiftUI

Apple's declarative UI framework. Build iOS/macOS apps using Swift state-driven views.

🖥️

UIKit

The older, more verbose UI framework — still widely used and worth knowing.

📦

Swift Package Manager

Build multi-file projects, import libraries, and structure real apps.

🌐

Vapor

Server-side Swift framework for building web APIs and backends.

🔗

Combine

Apple's reactive framework for handling asynchronous event streams.

🤖

Core ML / CreateML

Run machine learning models on-device with Swift's ML frameworks.

Recommended resources

ResourceURLType
Swift.org docsswift.org/documentationOfficial
The Swift Programming Language (book)docs.swift.org/swift-bookOfficial, free
Hacking with Swifthackingwithswift.comTutorials
Swift by Sundellswiftbysundell.comArticles
Swift Playgrounds (iPad/Mac)App StoreInteractive
100 Days of SwiftUIhackingwithswift.com/100swiftuiCourse
🚀

Next challenge: Extend the Finance Tracker above — add a Budget struct per category, a Report protocol with a csv() method, or hook it up to a SwiftUI view with @State and @ObservedObject.