One. Course Details
This is the eleventh 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, focusing on transforming the iPhone-only CodeBreaker app into a fully cross-platform application that works natively on iPad and Mac.
The lecture covers implementing split-view navigation with NavigationSplitView, programmatic List selection, context menus for cross-platform interaction, code refactoring best practices, and the beginning of building a game editor view using SwiftUI's Form component. The lecture intentionally ends mid-implementation, with the remaining editor functionality and SwiftData persistence scheduled for the next two sessions. Assignment 4 is available, and students are guided on implementing settings screens using the patterns demonstrated.
Two. Key Learning Takeaways
NavigationSplitView enables adaptive split-screen navigation that automatically behaves as a stack on iPhone and split view on iPad.
Size classes, not device types, determine UI behavior, allowing consistent cross-platform experiences with minimal code.
List selection binding lets you control and monitor the currently selected item, enabling automatic navigation and state management.
Context menus provide a unified interaction model for right-click on Mac and long-press on iOS. @Bindable grants permission to create bindings to properties of @Observable classes, essential for form editing.
Form is the standard SwiftUI component for building input interfaces, automatically handling keyboard management and layout.
Avoid fixed .frame(width:height:) modifiers entirely; use maxHeight/maxWidth to maintain responsive, adaptive layouts.
Extract complex views into dedicated "helicopter views" to keep your codebase clean, maintainable, and testable.
Add role: .destructive to buttons that perform irreversible actions to provide clear visual feedback to users.
Wrap state changes in withAnimation to ensure smooth, consistent transitions for all user interactions.
Three. Course Gold Quotes
"NavigationSplitView is magic. It automatically does the right thing on iPhone and iPad with zero extra code." "Never use .frame(width:) or .frame(height:). Challenge yourself to never use them for the rest of the quarter." "@Bindable is just permission to use the $ sign on @Observable objects. That's all it is." "Form is what you use when you want to collect information from the user. It's exactly what the Settings app uses on every screen." "Clean code is maintainable code. Extract complex views into their own helicopter views." "Always add withAnimation when you modify your data. Users expect smooth transitions." "Destructive actions should look destructive. Use role: .destructive to make them stand out." "Size classes, not devices, determine how your UI behaves. That's how Apple makes cross-platform work."
Four. Layered Learning Notes
Module 1: Adaptive Navigation with NavigationSplitView
NavigationStack works well for iPhone's compact screen, but iPad's larger display benefits from side-by-side list and detail views. NavigationSplitView provides this adaptive behavior automatically:-
On devices with regular horizontal size class (iPad, large iPhone in landscape), it displays two or three resizable columns
-
On devices with compact horizontal size class (small iPhone, any iPhone in portrait), it automatically falls back to stack-based navigation
-
No platform-specific code is required for this core behavior
-
columnVisibility: Controls which columns are visible on launch. Use
.constant(.all)to show all columns by default while still allowing users to toggle the sidebar. -
navigationSplitViewStyle(.balanced): Evenly distributes space between columns and keeps the sidebar permanently visible, rather than showing it as an overlay.
-
Detail placeholder: Provide a default view (e.g., "Choose a game!") that appears when no item is selected.
.navigationTitle() modifier on the view that should display the title. The navigation system automatically places the title in the appropriate column's navigation bar. Use .navigationBarTitleDisplayMode(.inline) to save vertical space by placing the title inside the navigation bar.
Module 2: List Selection and Programmatic Navigation
By default, List manages its selection state internally. To access and control selection from your code, add aselection: parameter that binds to an @State variable:
-
The type of the selection variable must match the type of the
value:parameter in your NavigationLinks -
Tapping a NavigationLink automatically updates the selection variable to its corresponding value
-
Setting the selection variable programmatically automatically navigates to the corresponding view
-
Replace the
.navigationDestination()modifier with conditional logic in the NavigationSplitView's detail parameter -
Use
if let selectionto display the appropriate detail view when an item is selected -
Set the initial selection value to automatically navigate to a default item on app launch (e.g., the first game or the user's most recent game)
Module 3: Mac Adaptation and Cross-Platform Interaction
The simplest way to bring your iOS app to Mac is using the "Mac (Designed for iPad)" target. This runs your iPad app natively on macOS with almost no code changes, while still supporting Mac-specific features like resizable windows and menus.
Key interaction differences between platforms:-
Mac: Supports right-click for context menus, drag-and-drop reordering without edit mode, but does not support swipe-to-delete
-
iOS: Supports long-press for context menus, swipe-to-delete, and requires edit mode for reordering
-
Add a
.contextMenumodifier to your list rows -
Include a delete button with
role: .destructiveto visually indicate the destructive action -
Wrap the deletion code in
withAnimationto add a smooth transition -
Use
onChange(of: games)to automatically clear the selection when the selected game is deleted, preventing invalid state
Module 4: Code Refactoring and Componentization
As your app grows, regular refactoring is essential to maintain code quality and prevent technical debt:-
Extract dedicated views: Break large views into smaller, single-responsibility "helicopter views"
-
Move the game list into a separate
GameListview -
Extract repeated UI elements like delete buttons into helper methods
-
Group related logic into dedicated functions (e.g.,
addSampleGames())
-
-
Manage data ownership clearly: Place state in the view that most needs it, and use @Binding to share mutable state between views
-
Fix common lifecycle issues: Guard against duplicate data by checking
games.isEmptybefore adding sample games inonAppear, which may be called multiple times as views appear and disappear
Module 5: Form Editing and the @Bindable Property Wrapper
SwiftUI'sForm component is designed specifically for building input interfaces to collect information from users. It automatically handles:
-
Consistent layout and styling matching system conventions
-
Automatic scrolling to keep active text fields visible above the keyboard
-
Section grouping and dividers to organize related fields
-
TextField: For text input. The first parameter is a placeholder that appears when the field is empty, and the second parameter is a binding to the string being edited.
-
ColorPicker: A built-in component for selecting colors, supporting spectrum sliders, RGB input, and preset colors. It can bind directly to individual elements in an array.
-
When you need to create a binding to a property of an @Observable class, you must mark the variable holding the class instance as @Bindable
-
@Bindable is not the same as @Binding. It simply enables the
$syntax for accessing properties of @Observable objects -
It supports deep binding, allowing you to bind to nested properties, array elements, and other complex structures
-
May you master adaptive SwiftUI development and build beautiful, responsive applications that work seamlessly across iPhone, iPad, and Mac. May your forms be intuitive, your navigation be fluid, and your Assignment 4 word game shine with professional polish. As you continue your journey, may you embrace clean code practices and create maintainable applications that scale effortlessly with your vision. Happy coding!


