iOS & SwiftUI Quick Reference
This reference condenses the SwiftUI APIs you reach for most often into one place — property wrappers for state, the core layout containers, navigation primitives, and the concurrency tools used for async work — so you can look something up mid-task without re-reading full topic write-ups. It assumes you already understand the concepts elsewhere in this course and is organized for scanning rather than deep explanation.
Cricket analogy: This reference is like a fielding-positions cheat sheet a captain like Rohit Sharma keeps in his pocket during a match, quick to glance at mid-over rather than re-reading the entire coaching manual.
Property wrappers for state
@State — view-owned, value-type source of truth; SwiftUI persists it outside the struct across re-renders. @Binding — a read/write reference to state owned elsewhere, typically passed from parent to child with $. @Observable — macro (iOS 17+) marking a reference-type model as observable with per-property change tracking; no @Published needed. @Environment(\.keyPath) — reads a value from the environment, such as \.scenePhase, \.colorScheme, or a custom @Observable model injected with .environment(). @Bindable — wraps an @Observable reference to produce bindings to its properties (e.g., $model.name) inside a view. @AppStorage / @SceneStorage — persist small values to UserDefaults or per-scene state restoration respectively.
Cricket analogy: @State is like a scorer's personal tally sheet kept for one match, @Binding is like sharing that tally with the commentary box, and @Observable is like a modern digital scoreboard that auto-updates every viewer without manual signals.
@Observable
final class SettingsModel {
var username: String = ""
var notificationsEnabled: Bool = true
}
struct SettingsView: View {
@Bindable var model: SettingsModel
@AppStorage("hasSeenOnboarding") private var hasSeenOnboarding = false
var body: some View {
Form {
TextField("Username", text: $model.username)
Toggle("Notifications", isOn: $model.notificationsEnabled)
}
}
}Layout containers at a glance
VStack / HStack / ZStack — fixed-axis stacking, alignment and spacing parameters, evaluate all children eagerly. LazyVStack / LazyHStack — same axes, but children are created on demand as they scroll into view, typically inside a ScrollView, for large content. List — a scrollable, lazily-loaded, styled row container with built-in selection, swipe actions, and section support. Grid / LazyVGrid / LazyHGrid — two-dimensional layouts with explicit or adaptive column/row definitions. ScrollView — the scrollable container that LazyVStack/LazyHStack and Grid variants typically live inside.
Cricket analogy: VStack is like batsmen listed top to bottom on a scorecard, HStack is like fielders arranged left to right around the boundary, and LazyVStack is like a stadium only rendering seats as spectators scroll into that section.
Navigation and presentation
NavigationStack — push/pop navigation with a path you can drive programmatically via a bound array or NavigationPath. .navigationDestination(for:destination:) — declares what view to push for a given data type appended to the path. .sheet(isPresented:) / .sheet(item:) — modal presentation, dismissed by toggling the bound Bool or clearing the bound item. .fullScreenCover — like .sheet but covers the full screen without the sheet's drag-to-dismiss affordance by default. TabView — tab-based root navigation, with .tabItem {} per child (or Tab value-based API on newer OS versions).
Cricket analogy: NavigationStack is like a tournament bracket you push deeper into with each match won, .sheet is like a rain-delay overlay that pops up and dismisses when play resumes, and TabView is like switching between the batting and bowling stats tabs.
Concurrency essentials
.task { } — attaches async work to a view's lifetime, auto-cancelled on disappearance. Task { } — an unstructured, manually-managed unit of async work, cancel it yourself via a stored reference if needed. async let — starts a child task inline for structured concurrent execution of independent async calls. TaskGroup / withTaskGroup — dynamic structured concurrency for a variable number of child tasks. @MainActor — isolates a type/function/closure to the main thread; SwiftUI view bodies are implicitly main-actor. await — suspends until an async call completes, without blocking the thread.
Cricket analogy: .task {} is like a scoreboard operator's live feed that starts when the match screen appears and auto-cancels when the innings display closes, while async let starts the run-rate and required-rate calculations concurrently.
As a rule of thumb for choosing a stack container: use plain VStack/HStack for small, always-visible content; switch to Lazy variants or List once the number of rows is large or comes from a dynamically-sized data source.
This page is a lookup aid, not a substitute for understanding why each API behaves the way it does — misapplying a Lazy container assuming it behaves exactly like its eager counterpart (e.g., expecting GeometryReader-based sizing to work identically) is a common source of subtle layout bugs.
Common modifiers cheat sheet
.frame(width:height:alignment:) — explicit or flexible sizing. .padding(_:) — inset content, optionally on specific edges. .background(_:in:) — layer a shape or material behind content. .foregroundStyle(_:) — sets foreground color/material for text and symbols (successor to .foregroundColor). .onChange(of:) { old, new in } — reacts to a value changing. .disabled(_:) / .opacity(_:) — control interactivity and visibility without removing the view from the hierarchy.
Cricket analogy: .frame sizes a scorecard widget explicitly like fixing boundary-rope dimensions, .padding insets the team logo from the card's edge, and .onChange(of:) reacts the instant a wicket falls, updating the display without removing it.
- @State, @Binding, @Observable, @Environment, and @Bindable cover the core state-management vocabulary.
- Choose eager stacks for small content and Lazy variants or List for large/dynamic collections.
- NavigationStack with .navigationDestination(for:) is the modern push/pop navigation approach.
- .task auto-cancels with the view; Task { } is unstructured and needs manual cancellation.
- @MainActor isolates code to the main thread; SwiftUI view bodies are implicitly main-actor already.
- .foregroundStyle supersedes .foregroundColor and supports materials and gradients.
Practice what you learned
1. Which property wrapper produces bindings (e.g., $model.property) to properties of an @Observable reference type inside a view?
2. What is the main behavioral difference between VStack and LazyVStack?
3. Which modifier declares what view to push onto a NavigationStack for a given data type appended to its path?
4. How does .task differ from a manually created Task { } inside .onAppear?
5. What is .foregroundStyle generally considered relative to .foregroundColor?
Was this page helpful?
You May Also Like
@State and @Binding
Understand how @State owns local view state and how @Binding lets child views read and write that state without owning it themselves.
NavigationStack Basics
Understand how NavigationStack replaced NavigationView, how navigationDestination drives push navigation, and how to structure hierarchical screens in SwiftUI.
TabView and Sheets
Compare SwiftUI's two main top-level presentation patterns — persistent tab-based navigation with TabView and modal, dismissible content with sheets.
Task and Structured Concurrency
Explore how Swift's Task type creates units of asynchronous work, how structured concurrency establishes parent-child relationships, and how cancellation propagates.