An eleven-week SwiftUI course

Swift by Heart

✦

For an experienced UIKit engineer who last wrote Swift around 2016. 57 lessons: about eleven weeks at one each weekday, or at your own pace. The goal: pass a senior iOS interview.

57 lessons
Pace57 lessons, on weekdays only. At one each weekday the course takes about eleven weeks; a shorter plan puts two on some weekdays, and a longer one leaves some free. The last two lessons are practice interviews. A lesson takes from 45 minutes to two hours.
ToolsXcode 27, iOS 27, Swift 6.4
Starting pointEighteen years of iOS with UIKit. Swift last used around 2016.
ProjectsLesson 1: a project made from scratch in Xcode — how it is set up and configured, and what an app is made of. Lessons 2–4: three small projects, each starting from a little app you download. They end in TinyUI, a miniature SwiftUI you write yourself. From lesson 5: Trips, one app that grows with the course — and, in the lessons that try one thing on its own, a small project of their own.
Lessons done
0 of 57
Interview problems solved
0 of 11
✦

Guides

✦

One subject each, all the way down. Come back to them from any lesson; the lessons that bear on one point to it.

6248003032315232163256Guide · The attribute graphInside the Attribute GraphA view's nodes, what a tap does to them, and a debugger stopped at every stepOpen the guide → piece 1awaitthe answerpiece 2Guide · awaitawait, in fullFrom one await to a loop, a stream, and an actor of your ownOpen the guide → Bindinggetsetone value, somewhere elseGuide · BindingsBinding, @Binding and @BindableFrom one Binding you make by hand, up to the two property wrappers that write it for youOpen the guide → id 1id 2id 3Guide · ForEachForEach, in fullWhat it keeps, when it makes its rows, and how it tells them apartOpen the guide → \Trip.city.countoffset: 8Guide · Key pathsKey paths, in fullEvery kind, every step a path can take, and what the compiler makes of oneOpen the guide → body.property{ 3176 }read, then toldGuide · @Observable@Observable, in fullHow one plain read in a body signs the view up to be told when the property changesOpen the guide → ~Copyableconsumeone owner at a timeGuide · OwnershipOwnership, in full~Copyable, borrowing, consuming and consume, and what the compiler does underneathOpen the guide → {}buildBlockGuide · Result buildersResult builders, in fullEvery method a builder can have, and what SwiftUI's own builder leaves outOpen the guide → modelclosureeach holds the otherGuide · Retain cyclesRetain cycles in SwiftUIWhen an @Observable model is never freed, and when it is — a screen that only names its model never traps it; the model it owns canOpen the guide → ✦All nineThe guides, in fullWhat each one covers, section by section, and the lessons it comes from.See them all →

How this works

  1. Press Start the course and choose your start and finish dates. The 57 lessons are laid out on the weekdays between them, and every page then shows your own dates.
  2. Every lesson has its own page. Open today’s from the calendar below.
  3. Work through that page in Xcode, typing the code yourself. Lessons 1–55 are step-by-step tutorials that build something, with a real screenshot at every step; the last two lessons are practice interviews.
  4. Go through the flash cards at the end of the page until you can answer each one before you tap it.
  5. At the end of each part, try the interview problem before you look at its answer.
  6. Mark the lesson done.

Keep these open

Part 1 · Lesson 1
✦

Before you start: a project in Xcode

✦

Before any Swift: making an app in Xcode 27 the way an interview expects you to, from a new project to a passing unit test — and a first look at what an app is made of.

You buildPeek, an app made from scratch with its tests, that shows its own parts: what its bundle says, what its scene is doing, the app delegate it still has, and which actor runs its code.

You can explainWhat each option of a new project decides; what replaced the app delegate; which actor your code runs on.

Lesson 1

Make a project, and look inside it

A new project in Xcode, and what an app is made of. Making an app the way an interview expects: every option of a new project, its settings and scheme, a unit test that runs — and the parts that replaced the app delegate.

Peek, an app made from scratch with its tests, that shows its own parts: what its bundle says, what its scene is doing, and which actor runs its code.

Build-along tutorial →
Part 2 · Lessons 2–4
✦

Modern Swift: what changed since 2016

✦

Swift changed a great deal after Swift 3. Every piece of SwiftUI syntax that looks unusual is really a language feature added since then. This part covers those features on their own, before any SwiftUI.

You buildProperty wrappers of your own, then TinyUI — a miniature SwiftUI that prints to the console, with widgets, a result builder and some Widget.

You can explainWhat some View promises the compiler; what $value really is; how a body with no commas and no return becomes one value.

above the line: the Swift release; below: the SwiftUI syntax it made possible 20162019 202120232026 WHERE YOU LEFT OFF · SWIFT 3 SWIFT 5.1 property wrappers · opaque types · result builders SWIFT 5.9 macros SWIFT 4 · CODABLE · KEY PATHS SWIFT 5.5 · ASYNC/AWAIT · ACTORS SWIFT 6 → 6.4 · STRICT CONCURRENCY → some View · @State · the body { } block → @Observable · #Preview → .task { await … } → @MainActor by default · @concurrent
Swift releases since 2016, and the SwiftUI syntax each one made possible
Lesson 2

Fix it with a property wrapper

Property wrappers. How @Something var works, and what the $ prefix really is.

Build-along tutorial →
Lesson 3

Build the pieces of a tiny UI framework

Opaque types and existentials: some vs any. Why every SwiftUI view returns some View, and what that promises the compiler.

Build-along tutorial →
Lesson 4

Give TinyUI the syntax of SwiftUI

Result builders. How a block of views with no commas and no return becomes a single value.

Then the interview problem for this part: A helper that will not compile

Build-along tutorial →
Part 3 · Lessons 5–7
✦

Getting started with SwiftUI

✦

How a SwiftUI app starts, how a view works, and the preview workflow that replaces build-and-run.

You buildTrips begins: a card for one trip, built in the canvas with a preview for each state — then what runs before the first view, and what a view really is.

You can explainWhy a view is a struct; when body runs; what a scene is.

Lesson 5

Start Trips, and build its first view in the canvas

Xcode previews. Seeing a view update live as you type, without launching the app.

Build-along tutorial →
Lesson 6

What runs before the first view

App structure: @main, App, Scene, WindowGroup. What replaces AppDelegate and SceneDelegate, and what a scene is.

Build-along tutorial →
Lesson 7

What a view really is

How a view works: body and some View. A view is a description, not an object on screen — and what follows from that.

Then the interview problem for this part: The analytics event that fired forty times

Build-along tutorial →
Part 4 · Lessons 8–11
✦

Views and layout

✦

Showing content and arranging it. Stacks and modifiers replace most of what you used Auto Layout for.

You buildTrips laid out on purpose: the card's stacks and layers, a strip of pictures that scrolls, filter chips in a layout of your own, and a list that becomes two columns.

You can explainThe three steps of layout; why the order of modifiers matters.

Lesson 8

Words, pictures and symbols

Text, images, and SF Symbols. Showing text, pictures, and icons, and making images fit their frames.

Build-along tutorial →
Lesson 9

Rows, columns and layers

Stacks, spacers, and alignment. Arranging views in rows, columns, and layers — the replacement for most Auto Layout.

Build-along tutorial →
Lesson 10

How a view gets its size

How layout works: frames and sizing. The three-step negotiation between a parent and its child that decides every size.

Build-along tutorial →
Lesson 11

Modifiers, grids, and a layout of your own

View modifiers, and why their order matters. Each modifier wraps the view before it, so order changes the result.

Grids and custom layouts. Tables, scrolling grids, and writing your own container when neither fits.

Then the interview problem for this part: An infinite photo carousel

Build-along tutorial →
Part 5 · Lessons 12–16
✦

Modern Swift, part 2: macros, key paths, Codable, async and observation

✦

The rest of the language under SwiftUI, now that you have built screens that use it. First TinyUI comes back, with a file of 2016 Swift to bring up to date and a macro to read. Then four features, a lesson each: key paths, which name a property; Codable, which turns your types into JSON and back; async/await, which replaces completion handlers; and Observation, which is how a screen hears that a model changed. Part 6 needs all of them.

You buildTinyUI's 2016-style data layer brought up to date, and a #Predicate expanded and read — then four small apps, one a day: Rosette, Postcards, Parcel and Tally.

You can explainWhat a macro writes; what a key path object holds; what the compiler writes for Codable; what await does to a thread; what @Observable adds to a class.

Lesson 12

A file from 2016, brought up to date

Macros. What @Observable and #Preview actually do, and how to read the code they generate.

Everything else that changed since 2016. The smaller changes to modern Swift you will be expected to use without thinking — and where the four big ones are taught.

Build-along tutorial →
Lesson 13

Rosette: the category is a key path

Key paths. A property as a value: what \.city is, what the object behind it holds, and where SwiftUI hands you one.

Build-along tutorial →
Lesson 14

Postcards: a document in, your types out, and back again

Codable: what the compiler writes. How a struct becomes JSON and back, which code the compiler writes for you, and how to take over when the JSON does not match.

Build-along tutorial →
Lesson 15

Track a parcel with async and await

async and await. A function that waits, a caller that waits for it, and where that waiting happens: the first lesson of concurrency.

Build-along tutorial →
Lesson 16

Tally: how a screen hears its model

Observation: how @Observable works. What a model has to do for a screen to hear that it changed — written by hand first, and then what the macro writes for you.

Build-along tutorial →
Part 6 · Lessons 17–20
✦

State and data flow

✦

Where data lives, who is allowed to change it, and how SwiftUI knows which views to update. This is the part interviews probe hardest.

You buildTrips: a planner that edits a draft, a star on every card that one store hears, values handed down the environment, and cards that keep their identity.

You can explain@State versus @Binding versus @Observable versus the environment; what view identity is.

Lesson 17

A trip of your own

@State and @Binding. Who owns a piece of data, and how another view gets to change it.

Build-along tutorial →
Lesson 18

Who hears about a change

@Observable models. Data shared across screens, and how SwiftUI knows which views to update.

Build-along tutorial →
Lesson 19

Handing things down

The environment. Passing values down the view tree without threading them through every initializer.

Build-along tutorial →
Lesson 20

The same card, or a new one

View identity and lifetime. How SwiftUI decides whether a view is "the same view" — the idea behind most surprises.

Then the interview problem for this part: The counter that resets

Build-along tutorial →
Part 7 · Lessons 21–24
✦

Lists and navigation

✦

Scrolling lists, moving between screens, and presenting sheets — all of it driven by state.

You buildTrips: a list with detail screens, an editing sheet, and search.

You can explainWhy navigation is data; why every row needs a stable id.

Lesson 21

Every trip, in a list

List and ForEach. Scrolling rows of data, and why every row needs a stable identity.

Build-along tutorial →
Lesson 22

A screen for each trip

NavigationStack and programmatic navigation. Pushing screens, and treating the navigation stack as plain data.

Build-along tutorial →
Lesson 23

Bigger screens, and screens on top

Split views and adapting to screen size. One layout that works on iPhone, iPad, and Mac — and at any window size.

Sheets, alerts, and popovers. Presenting a screen by changing state, rather than by calling present.

Build-along tutorial →
Lesson 24

Search, a menu, and an overflow

Toolbars, menus, and search. Bar buttons, pull-down menus, and a search field that filters your data.

Then the interview problem for this part: A deep link into a navigation stack

Build-along tutorial →
Part 8 · Lessons 25–29
✦

Drawing and animation

✦

Custom shapes, and animation — which in SwiftUI means changing state and letting the framework animate the difference.

You buildTrips: a custom-drawn header, a chart, and one animated transition.

You can explainThe difference between an animation and a transition.

Lesson 25

A boarding pass, drawn in code

Shapes and paths. Drawing with built-in shapes and with paths of your own.

Build-along tutorial →
Lesson 26

Color, glass and depth

Gradients, materials, and shadows. Color that changes across a view, translucent backgrounds, and depth.

Build-along tutorial →
Lesson 27

A year in one canvas

Canvas. Drawing thousands of things quickly, when one view per thing would be too slow.

Build-along tutorial →
Lesson 28

Things that move

Animation basics. You change state; SwiftUI animates the difference.

Build-along tutorial →
Lesson 29

Arriving, leaving, and growing

Transitions and matched geometry. How views appear and disappear, and making one view seem to become another.

Then the interview problem for this part: The card that fades instead of growing

Build-along tutorial →
Part 9 · Lessons 30–32
✦

Input and interaction

✦

Controls, text entry, gestures, and drag and drop.

You buildTrips: a settings form, and cards you can drag.

You can explainHow a control reports a change without target–action or a delegate.

Lesson 30

Settings, and the keyboard

Controls and forms. Buttons, toggles, pickers, and the standard settings-screen layout.

Text input and focus. Text fields, keyboards, and moving the cursor from field to field in code.

Build-along tutorial →
Lesson 31

A photo you can handle

Gestures. Taps, drags, pinches — and state that resets itself when the finger lifts.

Build-along tutorial →
Lesson 32

Drag, drop, and share

Drag and drop, and haptics. Moving data between views and apps, and feedback the user can feel.

Then the interview problem for this part: Testing a debounced search

Build-along tutorial →
Part 10 · Lessons 33–36
✦

Data, persistence, and networking

✦

Decoding JSON, loading from the network, and saving to disk.

You buildTrips: saved to disk as JSON, the weather from a web API with loading and error states, a packing list in SwiftData, and settings that survive a restart.

You can explainWhat .task ties work to; why loading state should be one enum.

Lesson 33

Trips that are still there tomorrow

Codable and JSON. Turning JSON into your own types, and reading the error when it fails.

Build-along tutorial →
Lesson 34

The weather, from the network

Loading data with async/await. Fetching from the network in a view, with honest loading and error states.

Build-along tutorial →
Lesson 35

A packing list that SwiftData keeps

SwiftData. Saving your model objects to disk and showing them in a view.

Build-along tutorial →
Lesson 36

Settings that are still there tomorrow

AppStorage and SceneStorage. Remembering small things: user preferences and where the user left off.

Then the interview problem for this part: The take-home, in miniature

Build-along tutorial →
Part 11 · Lessons 37–45
✦

Swift concurrency

✦

async/await, actors, and Swift 6's data-race checking, which together replace GCD. This is as large a change as SwiftUI itself, and interviews for senior roles lean on it.

You buildTrips: networking rebuilt with structured concurrency, an actor-based cache, and tests that never wait on real time — after Kitty, a small app of its own, shows how tests run and what they can see.

You can explainActor isolation and reentrancy; what Sendable means; how to migrate a codebase to Swift 6.

Lesson 37

The hive hums: what await really does

Tasks, await, and the main actor: what the words mean. What a task is, what await really does, and the one rule for the main actor: waiting is free, computing is not.

Build-along tutorial →
Lesson 38

Every trip's weather at once, a cache that is an actor, and an actor of your own

Structured concurrency. Running work in parallel with a clear owner, automatic cancellation, and errors that surface.

Actors and @MainActor. Protecting shared data without locks, and keeping UI work on the main thread.

Build-along tutorial →
Lesson 39

Moving a project to Swift 6

Sendable and Swift 6 strict concurrency. How the compiler rules out data races, and how to migrate an older codebase.

Build-along tutorial →
Lesson 40

City suggestions as you type

AsyncSequence and AsyncStream. Values that arrive over time, built by hand first — an async next() and a while let — then as the AsyncSequence that for await is rewritten into, then as an AsyncStream; wrapping old callback APIs so you can loop over them; then a continuation that can be resumed only once, and the ownership rules (~Copyable, consuming, borrowing) that make it so.

Build-along tutorial →
Lesson 41

Local time, from Combine to SwiftUI

Combine: reading legacy code. Where Combine stands in 2026, and how to migrate away from it.

Build-along tutorial →
Lesson 42

The weather in the city you are typing

Concurrency in SwiftUI. Starting async work from a view so that it is cancelled when the view goes away.

Build-along tutorial →
Lesson 43

Kitty: how tests run, and what they can see

Testing in SwiftUI. How tests run, how Swift Testing finds and reports them, and what a unit test can see of a view.

Build-along tutorial →
Lesson 44

Tests for the weather, without the network

Testing async code. Writing tests for async functions, and what makes them possible.

Build-along tutorial →
Lesson 45

Tests that do not wait for the clock

Controlling time in tests. Testing a debounce or a timeout without making the test wait.

Then the interview problem for this part: Two downloads for one image

Build-along tutorial →
Part 12 · Lessons 46–48
✦

Architecture, testing, and performance

✦

Keeping a growing app readable, testable, and fast.

You buildTrips: split into small views with previews, tested — its logic, a picture and a UI flow — and a performance pass that makes one star redraw one card.

You can explainHow you would test a SwiftUI view; what causes a view to re-render.

Lesson 46

Smaller views, and a stand-in for the network

Breaking a screen into small views. Why small, named views are easier to read and faster to update.

Passing dependencies. Giving a view what it needs so that it can be previewed and tested.

Build-along tutorial →
Lesson 47

Tests for the logic, a picture, and the flow

Previews and tests. What to check with a preview, what to check with a test, and how to "test a view".

Build-along tutorial →
Lesson 48

One star, one card

Performance. Why a view re-renders, and how to find the ones that re-render too often.

Then the interview problem for this part: Testing code that depends on today's date

Build-along tutorial →
Part 13 · Lessons 49–55
✦

Platform features and shipping

✦

Dark mode, Liquid Glass, accessibility, localization, widgets, shipping, scenes and the background — and using SwiftUI inside a UIKit app.

You buildTrips: dark mode, Liquid Glass, an accessibility audit, German, a widget, an App Intent and an archive — then Burrow, a warren of sleepy bunnies, one to a window, and a UIKit app that takes up SwiftUI.

You can explainWhat happens to an app in the background; how to adopt SwiftUI gradually in an existing UIKit codebase.

Lesson 49

Trips in the dark

Dark mode and theming. Colors that adapt to light and dark mode without extra code.

Build-along tutorial →
Lesson 50

Controls made of glass

Liquid Glass. The design language introduced in iOS 26, and how your own controls adopt it.

Build-along tutorial →
Lesson 51

Trips, by ear and by finger

Accessibility. VoiceOver, Dynamic Type, and what you have to add yourself.

Build-along tutorial →
Lesson 52

Trips, in German

Localization. Translating an app, handling plurals, and layouts that survive longer text.

Build-along tutorial →
Lesson 53

Trips, outside Trips

Widgets, App Intents, and Foundation Models. Your app outside your app: the Home Screen, Siri, and the on-device language model.

Shipping to the App Store. Archive, upload, test with TestFlight, and submit for review.

Build-along tutorial →
Lesson 54

Scenes: what a window lives through

Scenes: windows, phases and the background. What a scene is, what each phase means, what really happens in the background, and how each window keeps its own state.

Build-along tutorial →
Lesson 55

SwiftUI and UIKit, both ways

Using SwiftUI with UIKit. Mixing the two frameworks — the way most real apps adopt SwiftUI.

Then the interview problem for this part: Wrapping a UIKit view for SwiftUI

Build-along tutorial →
Lessons 56–57
✦

Mock interviews

✦

Three timed practice interviews, and a review of whatever is not yet solid.

Lesson 56

Mock interviews 1 and 2

Timed practice interviews, then a review of everything not yet solid.

Practice →
Lesson 57

Mock interview 3, and review

Timed practice interviews, then a review of everything not yet solid.

Practice →
✦

Cheat sheets

✦

Single pages to come back to: everything the course teaches on one subject, collected, with a flash card for each piece.

Cheat sheet

Expansions

What each piece of syntax really is, written out. 14 expansions, each with a flash card.

57 lessons in 13 parts, with 11 interview problems. Current to Xcode 27, iOS 27 and Swift 6.4.
Every piece of code on these pages was compiled before it was published, and every screenshot was taken from a running app.