Note Wisdom
These annotated notes for CS193p Lecture 10 explain building complex SwiftUI multi‑screen UIs, covering List, NavigationStack, protocol conformance, plus struct‑vs‑class model trade‑offs with live‑coding examples.
Institution: Stanford
Original Course:
Instructor Bio: This lecture is delivered by Paul Hegarty, Lecturer in Computer Science at Stanford University and the principal instructor of CS 193P since 2010. Paul Hegarty is a veteran software engineer and educator with deep roots in the Apple developer ecosystem. Earlier in his career he worked at NeXT Computer, where he contributed to the Objective-C language and the foundational tools that would eventually become Apple's modern development platform. He has taught iOS application development at Stanford for over fifteen years, guiding thousands of students through the transition from earlier UIKit frameworks to the modern SwiftUI declarative paradigm. He is widely recognized for his methodical teaching style, his emphasis on clean architectural patterns such as MVVM, and his ability to explain complex systems concepts through hands-on, live-coding demonstrations.
Course Description: This lecture moves beyond basic layouts to tackle the construction of complex, data-driven user interfaces. It covers SwiftUI's List and Form views for displaying structured data, navigation stacks and navigation links for multi-screen workflows, and sheet presentations for modal content. The lecture also discusses how to efficiently render large datasets using lazy containers, how to customize list row appearances, and how to manage navigation state in a clean, maintainable way.
Code struct conform to Hashable. Swift can auto‑synthesize protocol conformance for structs, but only if every stored property inside also meets the protocol requirements. The compile error pointed to an inner enum Kind, which supported Equatable but not Hashable. Updating Kind to Hashable fixed that dependency chain (4:20). Even after compiling, there was still a functional flaw: duplicate guess values could still get created. So they added a check before accepting new guesses, rejecting any guess whose peg sequence already existed in the attempts array.Code type to be Hashable. The pegs array inside each attempt already satisfied uniqueness, stability and Hashable. They could just explicitly point For‑Each to use pegs as the identifier, instead of making the whole struct conform.Code conform to Identifiable. Swift cannot auto‑synthesize Identifiable for structs. Unlike classes, structs have no built‑in sense of inherent identity. You have to manually define the id computed property. The Xcode fix‑it tool auto‑generates a placeholder id variable, and the demo set id to return the pegs array. Even though the syntax looked nice, the lecturer said they would not actually write this for real‑world code..id(\.pegs) on the For‑Each makes your intent explicit: you are choosing pegs as the identifier for this particular view loop, rather than claiming pegs represent the universal identity of the struct everywhere in your app.GameChooser. This view holds an array of Codebreaker game instances as local state. A List view displays the collection. The lecturer described List as a pre‑built combination of VStack, ScrollView, dividers, and For‑Each. It appears everywhere across iOS system apps like Settings, and you will reach for it most times you need to show users a selectable list of items (14:50)..onAppear modifier, populating the list with mastermind‑style colours, earth‑tone colours, and underwater blue‑toned colour sets. Next they pulled the row content into its own standalone subview named GameSummary. Breaking row content out to a separate view keeps the List body clean and also makes writing previews simpler. The preview can wrap GameSummary inside a List to replicate real runtime layout, instead of previewing it in isolation (28:00).GameSummary, they demonstrated Swift’s built‑in pluralization text inflection. Instead of writing ternary logic to manually add an “s” for plural nouns, Swift has locale‑aware inflection. This matters because not every human language forms plurals by simply adding an s character (23:30).Section, which divides a list into labelled groups, like how Settings separates categories. They noted they would not use sections for this app, but showed the syntax so the class knew how it works (30:04).NavigationLink. A common gotcha they demonstrated: NavigationLink does nothing if it is not nested inside a NavigationStack. Without that container, all links are inert and taps do not register (33:55).CodebreakerView owned its own game model with @State. The game chooser needs to hand over one of its existing game objects to that view, and any edits made while playing need to flow back to the chooser screen so the summary view updates attempt counts. They converted the view’s stored property to @Binding. This required adjusting previews, since previews have special rules when working with @State and @Binding (35:30).List with a binding‑based collection, you can use the dollar‑sign syntax directly in List’s initializer. This lets you get bindings to each individual element inside your array state.
All contents below are exclusive to the paid Word file, NOT available on this web page
.toolbar() modifier attaches toolbar content to the currently‑active view inside the navigation stack. That means different screens can show different toolbar buttons. When you navigate away from a view, its toolbar items disappear automatically. They moved the game’s restart button and elapsed‑time timer into the toolbar. ToolbarItem lets you control placement, using semantic placement values instead of hard‑coding left or right positions. Semantic placement lets SwiftUI adapt automatically for different device sizes like iPhone versus iPad (46:50).mutating keyword. Classes do not need mutating markers. Every method on a class can modify its stored properties, because you work with a reference pointing to heap memory. Changing to a class broke how SwiftUI observed changes. Simply passing the class instance around is not enough. SwiftUI has no way to detect mutations happening inside heap‑allocated objects unless you mark the class with @Observable. Without @Observable, UI would not refresh even when model data changed. The selection state held inside the view’s @State variables would still update, because that state belongs to the view layer, not the model (55:10).ForEach with .onDelete and .onMove modifiers. Those handlers receive index sets, and you call array mutation functions to delete or reorder items in your source array (56:40).NavigationLink (the variant that takes a value parameter and uses a separate .navigationDestination(for:) modifier), your class also needs Hashable conformance.== and hash(into:). The implementation shown relies entirely on the built‑in id property that Identifiable synthesizes. Two instances are equal only if they are exactly the same object living at the same memory location. This is strict reference equality, not value equality. Two separate class objects holding identical game data would count as not‑equal, even if every property matches (1:03:20). The lecturer recommended putting these protocol conformances inside an extension on the class, to keep the main class definition cleaner..navigationDestination(for:) modifiers on the same NavigationStack. In the demo they added a cheat button link. One link navigates to a Codebreaker view given a Codebreaker instance. A second separate link navigates to a simple peg‑display view given just an array of pegs. SwiftUI matches the type of value passed in NavigationLink against each navigation destination, and renders the matching view (1:07:30).Skip hours of watching lectures. Get organized notes, exam prep materials and problem solutions all in one Word file.
Click to see everything included
All contents below are exclusive to the paid Word file, NOT available on this web page

