Before You Start
Chris Lattner, the creator of LLVM, spent four years building Swift in secret before Apple unveiled it at WWDC 2014. These two lessons explain why, and get a toolchain installed.
What is Swift?
Chris Lattner spent 4 years building it in secret before WWDC 2014
Swift was designed by Chris Lattner, the creator of the LLVM compiler infrastructure, over four years of secret development inside Apple before its surprise unveiling at WWDC 2014 — replacing Objective-C with a language built from the start around safety (optionals, value types) without giving up the performance Apple's platforms needed.
Swift's origin at Apple under Chris Lattner, its 2014 unveiling, and where Swift 6.x sits in that history.
Setup and Toolchain
Swift itself runs on Linux and Windows — Xcode is only for iOS/macOS apps
The Swift compiler and toolchain run natively on Linux and Windows with no Xcode involved at all; Xcode is required only when you're actually building and submitting an iOS or macOS app, not for learning or writing Swift itself.
Installing the Swift toolchain, compiling and running a first .swift file, and when Xcode is actually required.
Language Fundamentals
The syntax you will use in nearly every file: constants over variables by default, an exhaustive switch, real closures, and error handling that is genuinely lighter than Java's. Five lessons before optionals and value types properly begin.
Variables and Types
let makes a value truly constant — var is the exception
Swift treats <code>let</code> as the default reach for most values, with <code>var</code> reserved for the genuine exception where reassignment is actually needed — a small syntactic nudge, like Kotlin's <code>val</code>, toward writing more code that simply can't be accidentally mutated.
let vs. var, Swift's basic types, type inference, and string interpolation.
Control Flow
switch must be exhaustive AND never falls through by default
Swift's <code>switch</code> makes two real, compiler-enforced departures from C's classic version: every possible case must genuinely be covered (exhaustiveness), and execution never silently falls through into the next case unless you explicitly ask for it with <code>fallthrough</code>.
if/else and while loops, switch's exhaustiveness requirement, pattern matching in switch cases, and fallthrough.
Functions
A parameter can have two different names — one for the caller, one internal
A Swift function parameter can carry an external name the caller writes at the call site and a completely different internal name the function body actually uses — letting a call read like a natural sentence while the implementation still gets a name that makes sense to it.
Function declarations, external vs. internal parameter names, default parameter values, and variadic parameters.
Closures
@escaping isn't optional — required the moment a closure might outlive the call
The compiler requires the <code>@escaping</code> annotation the exact moment a closure might be stored and called after the function that received it has already returned — a real, enforced distinction from a closure that only runs synchronously during the call, not just documentation.
Closure syntax and capturing values, trailing closure syntax, @escaping and when it's required, and autoclosures.
Error Handling
throws and try are required — genuinely lighter than Java's checked exceptions
A Swift function that can fail must be explicitly marked <code>throws</code>, and every call site must use <code>try</code> to acknowledge that — a real, compiler-enforced requirement, though genuinely lighter-weight than Java's checked-exception system since there's no exception-type hierarchy to declare.
do/try/catch, the Error protocol, throws function declarations, and try?/try! variants.
Optionals, Value Types and Enums — Swift's Defining Discipline
This is the stage that makes Swift, Swift: optionals as a real, compiler-checked answer to null, value semantics that make copying a struct genuinely safe, and enums that can carry real data. Seven lessons, the heart of the course.
Optionals and Value Types
if let, guard let, ??, and why structs copy independently
Swift's <code>Optional</code> makes the possibility of absence part of a value's actual type, checked at compile time through <code>if let</code>, <code>guard let</code>, the nil-coalescing operator <code>??</code>, and optional chaining — and that same safety-first design extends to value types, where assigning a struct to a new variable produces a genuine, independent copy rather than a shared reference.
Optional types and unwrapping (if let, guard let, ??, force-unwrap), optional chaining, and the value-vs-reference-type distinction.
Structs and Classes
Assign a struct and get a copy; assign a class and share the object
Assigning a struct instance to a new variable produces a genuine, fully independent copy — change one and the other is untouched — while assigning a class instance makes both variables point at exactly the same underlying object, so a change through either one is visible through both.
Struct vs. class declarations, value semantics vs. reference semantics, initializers, and choosing between them.
Enums
A case can carry its own data — no separate subclasses needed
A Swift enum case can carry genuinely different associated data of its own — unlike most languages' plain named-constant enums — which is what lets a single enum model something like a network response's several distinct shapes without a separate class hierarchy at all.
Enum cases with associated values, raw values, computed properties on enums, and enums with methods.
Collections
Array, Set, and Dictionary are all structs with copy-on-assignment
Because <code>Array</code>, <code>Set</code>, and <code>Dictionary</code> are all themselves structs, they inherit exactly the same copy-on-assignment guarantee every other Swift struct has — implemented efficiently under the hood through copy-on-write, so the actual copy only happens the moment one side is genuinely mutated.
Array, Set, and Dictionary basics, their struct-based value semantics, copy-on-write, and common collection operations.
Pattern Matching
if case checks one pattern without needing a full switch
<code>if case</code> lets you check a single pattern — say, one specific enum case with associated values — without writing out a full <code>switch</code> and its other, irrelevant branches, which is genuinely useful exactly when only one case actually matters to the code at hand.
if case and guard case, pattern matching with associated values, where clauses in patterns, and tuple patterns.
String Handling
str[0] doesn't even compile — a deliberate Unicode-correctness choice
Indexing a Swift string with a plain integer doesn't even compile, a deliberate design choice made because a single visible character (grapheme cluster) can genuinely be composed of multiple Unicode scalar values — so Swift forces you through its <code>String.Index</code> type rather than pretending integer indexing is safe when it silently isn't.
String as a collection of Characters, String.Index and why it exists, Unicode correctness, and common string operations.
Extensions
Add a computed property to Int — but never a stored one
A Swift extension can add a genuinely new computed property, method, or even protocol conformance to an existing type like <code>Int</code> — including ones you don't own — but it can never add a stored property, a real structural limitation rooted in how existing instances' memory layout can't retroactively grow.
Extension syntax, adding computed properties and methods to existing types, protocol conformance via extensions, and the stored-property limitation.
Protocols, Generics and Access Control
Protocol-oriented programming, the generics machinery that makes it type-safe, controlling what a module exposes, and the automatic JSON handling built on top of all three. Four lessons.
Access Control
public lets another module use your class — open is the separate, stronger level
<code>public</code> lets code in a different module use your class, but that alone is not enough to subclass or override it there — <code>open</code> is Swift's separate, deliberately stronger access level specifically required before another module can do that.
private, fileprivate, internal, public, and open, and the specific public-vs-open distinction for subclassing.
Generics Deep Dive
associatedtype lets a protocol require a type without naming it
A protocol's <code>associatedtype</code> lets it require that conforming types provide some type, without the protocol itself ever naming what that type actually is — each conforming type fills in its own concrete answer, inferred automatically from how it's used, which is what makes protocols like <code>Collection</code> genuinely generic.
Generic functions and types, associatedtype in protocols, where clauses for constraining generics, and generic subscripts.
Codable and JSON
Conform a struct to Codable and the compiler writes the JSON logic automatically
Conforming a struct or class to <code>Codable</code> makes the compiler automatically synthesize its entire JSON encoding and decoding logic, matching property names to JSON keys — zero manual key-by-key mapping code required for the common case.
Codable, Encodable and Decodable, CodingKeys for custom key mapping, and JSONEncoder/JSONDecoder.
Protocols and Generics, Tied Together
Conformance, default implementations, some/any, where clauses and Codable together
This lesson is the synthesis for the stage: protocol conformance, default implementations via protocol extensions, the <code>some</code> and <code>any</code> keywords for working with protocol types, generics with <code>where</code> clauses from the previous lesson, and <code>Codable</code> — Swift's protocol-oriented style seen as one coherent whole rather than separate features.
Protocol conformance and default implementations via extensions, the some vs. any keywords, generics with where clauses, and Codable, together.
Concurrency and Memory
Swift's structured, actor-based concurrency model and the automatic reference counting underneath every class instance — including the one real, common way it can still leak memory. Four lessons.
Async/Await
An async function can't just be called from sync code — Task{} is the real bridge
An <code>async</code> function cannot simply be called from ordinary synchronous code the way a regular function can; <code>Task { }</code> is the actual bridge that starts a new piece of asynchronous work running from synchronous context, which is worth understanding before async/await otherwise seems to mysteriously not compile.
async function declarations, await, Task{} as the sync-to-async bridge, and structured concurrency basics.
The Concurrency Model
An actor automatically serializes every access to its own state
A Swift <code>actor</code> automatically serializes every access to the state it owns, so two pieces of code can never race on the same actor's data — and that safety guarantee is exactly why reading even a simple property from outside the actor requires <code>await</code>, not a stylistic quirk.
Actors and automatic state serialization, @MainActor, Sendable and safely crossing concurrency domains, and structured concurrency guarantees.
Memory Management
Two classes with strong references to each other never get deallocated
Two class instances each holding a strong reference to the other create a retain cycle that ARC can never break on its own — not a bug in automatic reference counting, but genuinely its single most common real-world failure mode, and exactly what <code>weak</code> and <code>unowned</code> references exist to prevent.
Automatic Reference Counting (ARC) basics, strong reference cycles, weak vs. unowned references, and closures capturing self.
Concurrency and ARC, Tied Together
async/await, actors, Sendable, and ARC's retain cycles together
This lesson is the synthesis for the stage: async/await and <code>Task</code> from two lessons ago, actors and <code>@MainActor</code> for safe state access, <code>Sendable</code> for safely crossing concurrency domains, structured concurrency's guarantees, and ARC's memory model including the retain cycles the previous lesson covered — concurrency and memory management seen as the one connected system they actually are in real Swift code.
async/await, Task, actors, @MainActor, Sendable, structured concurrency, ARC, weak/unowned references, and retain cycles, together.
Apple Platforms, Tooling and Beyond
Property wrappers, the SwiftUI and Combine frameworks built on top of everything so far, reaching outside Swift entirely to Objective-C and the server, and the tooling that ties a real project together. Eight lessons to close out the course.
Property Wrappers
@propertyWrapper is real and writable — @State isn't special compiler magic
<code>@propertyWrapper</code> is a genuine, ordinary language mechanism you can write yourself — SwiftUI's <code>@State</code> is not special, hidden compiler magic at all, just an ordinary struct that happens to use this exact same mechanism, which is worth knowing before SwiftUI's property wrappers otherwise feel like unexplainable magic.
@propertyWrapper syntax, wrappedValue and projectedValue, writing a custom property wrapper, and how SwiftUI's wrappers use the same mechanism.
SwiftUI Basics
A View's body describes the UI — it's redrawn automatically when state changes
A SwiftUI <code>View</code>'s <code>body</code> is not a UI-building function that runs once — it is a declarative description of what the UI should look like for the current state, and SwiftUI automatically redraws it whenever any state the body reads actually changes.
View protocol and body, @State and @Binding, basic layout views (VStack, HStack), and how state changes trigger automatic redraws.
Combine
For a one-shot async result, async/await is now usually simpler
Combine models values that arrive over time as a genuine reactive stream — but for the common case of a single, one-shot asynchronous result, Swift's own <code>async</code>/<code>await</code> is now usually the simpler, more direct choice, which is worth knowing before reaching for Combine out of habit.
Publishers and Subscribers, common operators (map, filter, combineLatest), and when Combine fits better than plain async/await.
The Standard Library
Result<Success, Failure> is just an enum — a typed alternative to throws
<code>Result<Success, Failure></code> is genuinely just an enum with two cases, no hidden compiler magic involved, offering a real typed alternative to Swift's own <code>throws</code> mechanism for situations where you want to store, pass around, or defer handling of a fallible outcome.
Result<Success, Failure>, converting between Result and throws, and other standard library essentials (Comparable, Hashable, Equatable).
Objective-C Interoperability
A struct or an enum with associated values can't be used from Objective-C at all
<code>@objc</code> cannot bridge everything: a Swift struct, or an enum carrying associated values, genuinely cannot be exposed to and used from Objective-C code at all, which matters directly for any codebase still mixing the two languages during a gradual migration.
@objc and @objcMembers, which Swift features can and can't bridge to Objective-C, and mixed-language project setup.
Server-Side Swift
Vapor runs on the exact same toolchain that builds iOS apps
Vapor, a genuine server-side Swift framework, runs on the identical Swift toolchain used to build iOS apps — the same <code>async</code>/<code>await</code> and structured concurrency work exactly the same way on a Linux server as they do in an iOS app, with no separate server-flavored dialect of the language to learn.
Vapor basics, routing and request handling, and how the same async/await model applies identically on the server.
Swift Package Manager
Package.swift is written in actual, compiled Swift code
Unlike almost every other language's dependency manifest — typically JSON, YAML, or XML — <code>Package.swift</code> is itself real, compiled Swift code, which means it can use conditionals, variables, and any other language feature to describe a package's dependencies and targets.
Package.swift structure, declaring dependencies and targets, and the executable-Swift-as-manifest design.
Testing
@Test alone marks a function as a real test — no class or inheritance needed
Swift Testing needs no test class, no inheritance from a base test case, and no method name that starts with <code>test</code> — the <code>@Test</code> attribute alone is enough to mark any ordinary function as a real, runnable test, a genuinely lighter-weight model than XCTest's class-based approach.
@Test and #expect in Swift Testing, comparison with XCTest's class-based model, and parameterized tests.
Concepts That Cross Languages
These pages explain ideas that are not specific to Swift — they apply to every language you will ever learn. Read them once and the next language costs you far less effort. They pair well with the lessons above rather than replacing them.
Build Real Things — 10 Projects
Reading is not enough. These 10 projects — five basic, five medium — are chosen because each one exercises something specific you learned above, and because they are genuinely idiomatic Swift rather than generic exercises. Each link below opens the project brief; the Swift implementation walkthrough is in progress.
readLine() input parsing, a guess loop, and Int.random(in:) for the secret number.
A switch over an enum for conversion direction, and string interpolation for output.
Reading a file with String(contentsOfFile:), and a [String: Int] dictionary for a frequency table.
A Codable struct for the task shape, and JSONEncoder/JSONDecoder for persistence.
An OptionSet for character-set selection, and a review of Int.random(in:)'s suitability for real security.
URLSession's async/await APIs for real requests, and regular expressions for link extraction.
Vapor's routing for a real HTTP server, Codable for typed JSON responses, and an in-memory actor-backed store.
An enum with associated values for subcommands, and a switch the compiler checks for exhaustiveness.
An actor-backed store for thread-safe access, and Vapor serving the HTTP layer with structured concurrency.
Parsing lines with Swift's regex literals, and a Dictionary-based counter for top-N aggregation.
Practice & Experimentation
Ongoing, not a final step. Use these throughout the course — try every snippet you read, and run anything you are unsure about rather than assuming.
Run Swift code without installing anything, via the official Swift Playground.
Copy-ready idiomatic patterns to keep beside you while you build.
All 30 topic pages in one index, for looking things up later.