The language that replaced C in your pocket — safe, expressive, and surprisingly delightful.
01 — Optionals
Swift made nil safe — not by hiding it, but by making it a first-class citizen. You know when something might be missing, and the compiler makes you deal with it.
// This can be nil — Swift forces you to acknowledge it var username: String? = nil // Safe unwrapping — the "if let" dance if let name = username { print("Hello, \(name)!") } else { print("Who are you?") } // Guard: bail early, keep the happy path clean func greet(user: String?) { guard let name = user else { print("No user found.") return } print("Hey, \(name) 👋") } // Optional chaining — drill in without crashing let city = user?.address?.city ?? "Unknown city"
The ?? nil-coalescing operator is like a shrug emoji for missing values: "use this, or that if it's nil."
The billion-dollar mistake — Tony Hoare, who invented null references in 1965, later called it his "billion-dollar mistake." Swift's optionals are the language-level apology.
02 — Enums with Associated Values
Most languages have enums as fancy integers. Swift enums can carry data, have methods, and model your entire domain — they're basically algebraic data types in disguise.
enum NetworkResult<T> { case success(T) case failure(Error) case loading } func handle(result: NetworkResult<String>) { switch result { case .success(let data): print("Got: \(data)") case .failure(let error): print("Oops: \(error)") case .loading: print("Spinning… ⏳") } } // Enums can have computed properties and methods! enum Suit: String, CaseIterable { case hearts = "♥️" case diamonds = "♦️" case clubs = "♣️" case spades = "♠️" var isRed: Bool { self == .hearts || self == .diamonds } } print(Suit.allCases.map { $0.rawValue }) // → ["♥️", "♦️", "♣️", "♠️"]
The compiler checks switch exhaustiveness — miss a case and it won't compile. No silent bugs sneaking through.
03 — Structs & Value Semantics
In Swift, structs are value types — when you pass them around, you get a real copy. No spooky action at a distance. No "why did modifying b change a?" debugging sessions at 2 AM.
struct Point { var x, y: Double func distance(to other: Point) -> Double { let dx = x - other.x let dy = y - other.y return (dx*dx + dy*dy).squareRoot() } } var a = Point(x: 0, y: 0) var b = a // real copy — b is independent b.x = 10 print(a.x) // → 0.0 (a is untouched! 🎉) print(b.x) // → 10.0 // Named argument labels make call sites read like sentences let origin = Point(x: 0, y: 0) let tip = Point(x: 3, y: 4) print(origin.distance(to: tip)) // → 5.0
Named argument labels — distance(to: tip) — turn function calls into readable prose. Swift enforces this by default.
04 — Protocols
Swift favours Protocol-Oriented Programming over class inheritance. Protocols define capabilities — and with extensions, you can give them default implementations. It's interface + mixin in one.
protocol Describable { var description: String { get } } // Default implementation via extension — free behaviour! extension Describable { func shout() { print(description.uppercased() + "!!!") } } struct Car: Describable { let make, model: String var description: String { "\(make) \(model)" } } let car = Car(make: "Tesla", model: "Model S") car.shout() // → TESLA MODEL S!!! // Protocol as a type — polymorphism without inheritance func describe(items: [any Describable]) { items.forEach { print($0.description) } }
Protocol extensions give you free default behaviour — like mixins, but checked at compile time.
05 — Async / Await
Remember callback hell? Pyramid of doom? Swift's async/await — added in Swift 5.5 — makes concurrent code look like sequential code. Your eyes will thank you.
// The old way (callback pyramid of doom 😱) // fetchUser { user in // fetchPosts(for: user) { posts in // fetchComments(for: posts[0]) { comments in // // still going... // } // } // } // The Swift way — reads top to bottom ✨ func loadDashboard() async throws { let user = try await fetchUser() let posts = try await fetchPosts(for: user) let comments = try await fetchComments(for: posts[0]) render(user: user, posts: posts, comments: comments) } // Run two things in parallel with async let func loadSidebar() async throws { async let trending = fetchTrending() async let friends = fetchFriends() // Both fire simultaneously ⚡ let (t, f) = try await (trending, friends) render(trending: t, friends: f) }
async let fires both requests in parallel — then await joins them. Zero callback nesting.
06 — Result Builders & SwiftUI
SwiftUI uses Swift's result builders to create a declarative DSL that reads like a layout spec. No XML, no storyboards — just expressive Swift all the way down.
struct ProfileCard: View { let name: String let role: String @State private var isFollowing = false var body: some View { VStack(spacing: 12) { Image(systemName: "person.circle.fill") .font(.system(size: 60)) .foregroundColor(.orange) Text(name).font(.title2.bold()) Text(role).foregroundColor(.secondary) Button(isFollowing ? "Following ✓" : "Follow") { isFollowing.toggle() } .buttonStyle(.borderedProminent) .tint(isFollowing ? .green : .orange) .animation(.spring(), value: isFollowing) } .padding() .background(.regularMaterial) .cornerRadius(20) } }
The layout hierarchy is the code structure. No disconnect between what you write and what you see.
@State is a property wrapper that makes the button reactive — when isFollowing flips, the view re-renders automatically. SwiftUI + Swift = reactive UI without a framework.
07 — And so much more
The compiler catches type mismatches before your users do. Always.
Compiled to native code. Runs as fast as C in most benchmarks — without the footguns.
@State, @Published, @AppStorage — reusable behaviour as annotations.
Swift actors protect shared state in concurrent code — data races become compile errors.
Swift runs on Linux and Windows too. It's not just for Apple platforms anymore.
Trailing closures, string interpolation, and shorthand like $0 keep code lean.