One. Course Details
This is the fourth 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 Xcode demo work with no slides, focusing entirely on implementing the complete Model layer for the CodeBreaker game and connecting it to the existing UI prototype.
The lecture walks students through designing a clean, UI-independent Model architecture, implementing core game logic, handling user tap input, managing mutable state with @State, and adding basic animations. By the end of the session, the CodeBreaker app is fully playable, supporting guess submission, match feedback, and automatic UI updates.
The next lecture will cover UI improvements including a dedicated peg selection keyboard, hiding the master code during gameplay, and refining the game's visual design and animations. Assignment 2 builds directly on this implementation, challenging students to extend the game to support emoji pegs in addition to colored pegs.
Two. Key Learning Takeaways
The Model layer contains all business logic and data and must be completely UI-independent, importing only Foundation (or minimal core frameworks) and never SwiftUI. @State is the only way to create mutable state inside a SwiftUI View, storing the actual value in the heap behind a class pointer to work around the immutable nature of View structs. Value type semantics prevent accidental state sharing: when you copy a struct, you get a completely independent instance, eliminating entire classes of bugs common with reference types. Enum associated data is ideal for storing type-specific information, allowing only the .attempt case of Code.Kind to carry match result data. SwiftUI is inherently reactive: any change to the Model automatically triggers a UI update, with the system efficiently redrawing only the affected Views. The contentShape() modifier defines the touch area of a View, ensuring transparent Views still receive touch events. Swift can automatically synthesize Equatable conformance for enums and structs if all their stored properties are themselves Equatable. Initializers must initialize every stored property of a struct before returning, and self. is used to disambiguate between instance properties and initializer parameters with the same name.
Three. Course Gold Quotes
"The Model is what your app really is at its heart. The UI is just a visual manifestation of the Model." "Once you have the right primitives, there's almost no code in SwiftUI. The rest is just putting those primitives together." "@State is the only way to make mutable state in a SwiftUI View. Nothing else works. That's by design." "Value types are your best friend. They make your code predictable, safe, and easy to reason about." "Enum associated data lets you attach exactly the information you need to exactly the cases that need it. No more unused variables floating around." "SwiftUI reacts to changes in your Model automatically. You never have to tell it to redraw. It just knows." "The contentShape() modifier is a lifesaver for transparent Views. It lets you define exactly where touches should be detected."
Four. Layered Learning Notes
Module 1: Model Layer Design and Implementation
The Model layer is the foundation of any SwiftUI application, containing all data and business logic with no dependencies on the UI. For CodeBreaker, the Model is implemented as three core types:-
CodeBreaker: The main game struct that manages the master code, current guess, list of attempts, and available peg choices. It contains all game logic including changing pegs and processing guesses. -
Code: Represents a sequence of four pegs, with an embeddedKindenum distinguishing between master codes, active guesses, and completed attempts. -
Peg: Initially defined as atypealiasforColorfor simplicity, though students will refactor this toStringin Assignment 2 to support emoji pegs.
-
Namespacing: Embedding the
Kindenum inside theCodestruct creates a clear namespace (Code.Kind) and signals the tight relationship between the two types. -
Type aliases: Improve code readability by giving semantic meaning to existing types (
Peginstead ofColor). -
Immutability by default: All properties are declared as
letunless explicitly needed to be mutable.
Module 2: Connecting the Model to the Reactive UI
To turn the static UI prototype into a dynamic representation of the Model, the View needs to hold an instance of the CodeBreaker struct. However, since View structs are immutable, any mutable state must be marked with the @State property wrapper.
@State works under the hood:
-
It creates a hidden
Statestruct that stores a pointer to the actual value in the heap. -
This allows the value to persist even as the View struct is repeatedly recreated and discarded.
-
Any change to a
@Statevariable automatically triggers a refresh of the View's body.
mutating functions in a View. The View's body is a read-only computed property, and mutating functions will never work correctly. @State is the only valid way to manage mutable state in a View.
Module 3: Handling User Input and State Updates
The lecture implements tap-to-cycle peg selection using the.onTapGesture View modifier. When a peg is tapped:
-
The UI detects the tap and calls a method on the Model.
-
The Model handles the business logic of cycling to the next available peg color.
-
The Model updates its state, triggering an automatic UI refresh.
contentShape(Rectangle()) modifier fixes this by defining a rectangular touch area that matches the View's bounds, even if the View itself is completely transparent.
Module 4: Implementing Core Game Logic
The core game logic is implemented in two key methods on theCodeBreaker struct:
-
changeGuessPeg(at index: Int): Cycles the peg at the specified index through the available peg choices, usingfirstIndex(of:)to find the current position and modulo arithmetic to wrap around to the start. -
attemptGuess(): Creates a copy of the current guess, changes its kind to.attempt, calculates the match results against the master code, and appends it to the attempts list.
This demonstrates the power of value types: copying the guess to create an attempt leaves the original guess completely unchanged, avoiding unintended side effects.
Module 5: Advanced Enum Usage with Associated Data
To store match results only for completed attempts (and not for master codes or active guesses), the lecture adds associated data to the.attempt case of the Code.Kind enum:swift
enum Kind: Equatable {
case master
case guess
case attempt([Match])
}A convenience computed property matches is added to the Code struct that extracts the match array only for attempt codes, returning an empty array otherwise. This cleanly encapsulates the logic for accessing match results.
Module 6: UI Optimization and Basic Animation
Several UI improvements are implemented to create a better user experience:-
ScrollView: Wraps the list of attempts to prevent content from overflowing the screen and fixes the issue of the guess button jumping when new attempts are added. -
Reversed attempts: Displays the most recent attempt closest to the current guess, reducing the need for scrolling.
-
withAnimation: Adds smooth fade and slide animations when new attempts are added, previewing the animation system that will be covered in depth later. -
overlay: Places the guess button directly over the match marker area for the current guess, improving ergonomics.
Module 7: Swift Language Features in Practice
The lecture demonstrates several important Swift language features in a real-world context:-
Equatableprotocol: Adding: Equatableto theKindenum automatically synthesizes equality checks, even for cases with associated data. -
Initializers: Custom initializers are used to randomize the master code when a new game starts and to allow customization of the available peg choices.
-
selfkeyword: Used to disambiguate between instance properties and initializer parameters with the same name. -
Optional handling:
if letand the nil-coalescing operator (??) are used to safely handle optional values returned from methods likefirstIndex(of:)andrandomElement(). -
May you build clean, well-architected SwiftUI apps as you continue your iOS development journey. May your Model layers be pure and UI-independent, your state management be bug-free, and your CodeBreaker game turn out perfectly. Good luck with Assignment 2 and happy coding!


