One. Course Details
This is the tenth lecture of Stanford University's CS193p iOS Application Development course for Spring 2025, taught by Paul Hegarty. The entire session is dedicated to hands-on demos, building on the protocol concepts from Lecture Nine to transform the single-screen CodeBreaker app into a full-featured multi-game application with navigation between views.
The lecture walks through refactoring ForEach identifiers for stability, building a scrollable game selection list, implementing hierarchical navigation with NavigationStack, and converting the model from a struct to a class using the @Observable macro. It also covers list editing features (swipe to delete, reorder items), toolbar customization, and performance-optimized navigation with navigationDestination. The lecture concludes with a preview of upcoming topics: iPad and Mac compatibility, creating new games, and data persistence for Assignment 5.
Two. Key Learning Takeaways
ForEach identifiers must be unique, stable, and Hashable to ensure SwiftUI correctly tracks and animates views in dynamic collections.
Avoid using indices as identifiers unless you only append items to the end of an array; reordering or inserting items will break view matching.
List is the primary container for scrollable, selectable content in iOS, combining VStack, ScrollView, dividers, and ForEach into a single powerful component.
NavigationStack enables hierarchical navigation between views, automatically managing the back button and navigation bar.
@Binding shares value types between views, while classes use reference semantics to share data without bindings.
The @Observable macro automatically generates observation code for classes, making them work seamlessly with SwiftUI's reactive system.
Use semantic toolbar placements instead of hardcoded positions to ensure consistent behavior across devices and platforms.
navigationDestination improves performance by lazy-loading destination views only when they are tapped, rather than creating all views upfront.
Classes automatically synthesize Identifiable conformance using their memory address, making them ideal for items in collections.
Organize protocol conformances in extensions to keep your class definitions clean and focused on core functionality.
Three. Course Gold Quotes
"Identity is everything in SwiftUI. ForEach needs stable, unique identifiers to track views correctly." "Never use indices as identifiers unless you only add items to the end of your array. Reordering will break everything." "List is the most powerful view in iOS. You see it everywhere in every app." "@Observable is magic. It makes classes work just like structs in SwiftUI with almost no extra code." "Use semantic placements for toolbar items. The system knows better than you where things should go." "Structs are for values. Classes are for things that have identity and need to be shared across your app." "navigationDestination is the right way to do navigation. It only creates views when you tap on them." "Clean code makes clean apps. Extract complex list rows into their own helicopter views."
Four. Layered Learning Notes
Module 1: ForEach Identifiers and Hashable Conformance
The lecture opens by refactoring the ForEach loop that displays game attempts to use proper identifiers instead of indices. Indices are unstable because they change when items are reordered, inserted, or removed from anywhere except the end of the array.
Key steps for proper ForEach identifiers:-
Choose a natural identifier: For game attempts, the pegs array is a perfect identifier because it is unique (we prevent duplicate guesses), stable (pegs never change after a guess is made), and Hashable.
-
Implement Hashable conformance: Swift automatically synthesizes Hashable for structs and enums if all their properties are also Hashable. For the Code struct, we only needed to make the Kind enum Hashable to get full Hashable conformance.
-
Consider Identifiable carefully: While making Code conform to Identifiable results in cleaner ForEach code (no id: parameter), it should only be done if the identity is semantically clear and consistent across all contexts. The instructor recommends explicitly specifying id: .pegs for clarity in this case.
Module 2: Building Lists with SwiftUI
List is the fundamental component for displaying scrollable collections of items in iOS. It automatically handles scrolling, dividers, selection, and accessibility.
Best practices for building lists:-
Extract list rows into separate views: Create a dedicated GameSummary view for each row in the game list to keep your code organized and reusable.
-
Use maxHeight/maxWidth instead of fixed dimensions: Avoid .frame(width:height:) to ensure your UI adapts to different screen sizes. Use .frame(maxHeight: 50) to limit the size of elements while maintaining flexibility.
-
Customize list appearance with view modifiers: Use .listStyle() to switch between plain and grouped styles, .listRowSeparator() to hide dividers, and .listRowBackground() to set custom backgrounds for rows.
-
Add sections for organization: Use the Section view to group related items in your list, with optional headers and footers.
Module 3: Navigation with NavigationStack
NavigationStack enables hierarchical navigation between views, creating a stack of views that users can navigate back through.
Core navigation concepts:-
Wrap your root view in NavigationStack: This creates the navigation environment where NavigationLinks can work.
-
Use NavigationLink to navigate between views: Each NavigationLink has a label (the view displayed in the list) and a destination (the view to navigate to).
-
Pass data between views with @Binding: When navigating to a view that needs to modify shared data, pass a @Binding to the data. For lists of bindable items, use the $ syntax in the List initializer to get bindable elements.
-
Customize the navigation bar with toolbars: Use the .toolbar() view modifier to add buttons, text, and other elements to the navigation bar. Use ToolbarItem with semantic placements (.primaryAction, .automatic) instead of hardcoded positions.
Module 4: Class-Based Models with @Observable
While structs are ideal for most value types in SwiftUI, classes are better suited for top-level model objects that need to be shared across multiple views.
Converting a struct model to a class:-
Change struct to class: This switches from value semantics to reference semantics.
-
Remove mutating keywords: All functions in a class are implicitly mutating because you have a reference to the object in the heap.
-
Add the @Observable macro: This generates all the code needed to make SwiftUI react to changes in class properties. Without @Observable, SwiftUI will not update the UI when class properties change.
-
Implement list editing manually: When using classes, you cannot use the built-in editActions parameter of List. Instead, use the .onDelete() and .onMove() view modifiers on ForEach to handle editing.
Module 5: Advanced Navigation with navigationDestination
The standard NavigationLink initializer creates all destination views upfront, which can be inefficient for large lists. The navigationDestination modifier solves this by lazy-loading destination views only when they are tapped.
Benefits of navigationDestination:-
Better performance: Only creates views when the user taps on a NavigationLink, rather than creating all views at once.
-
Cleaner code: Separates the navigation link from the destination view definition.
-
Multiple destination types: You can add multiple navigationDestination modifiers to handle different types of values, allowing different views to be shown for different types of data.
-
Use the NavigationLink(value:) initializer, which only takes a label and a value to navigate to.
-
Add a .navigationDestination(for: Type.self) modifier that specifies which view to show for values of that type.
-
Ensure the value type conforms to Hashable, as SwiftUI uses a hash table to map values to views.
-
May you build beautiful, responsive multi-screen iOS applications with smooth navigation and robust data models. May your lists be performant, your identifiers be stable, and your Assignment 4 word game shine with professional polish. As you continue your SwiftUI journey, may you master the balance between structs and classes to create clean, maintainable code that scales with your projects. Happy coding!


