SwiftUI
Updated: May 22, 2026Categories: Languages, Apple, Mobile
Printed from:
SwiftUI Comprehensive Cheatsheet
1. Installation and Setup
Xcode and Swift Requirements
- Xcode 16+ recommended
- Swift 6+
- Targets: iOS 18+, iPadOS 18+, macOS 15+ (Sequoia), watchOS 11+, tvOS 18+, visionOS 2+
Project Creation
Swift
1234// Create a new SwiftUI project in Xcode
// File > New > Project > App
// Interface: SwiftUI, Language: Swift
Basic Project Structure
Swift
123456789101112131415161718192021222324import SwiftUI
// App entry point
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
// Main view
struct ContentView: View {
var body: some View {
Text("Hello, SwiftUI!")
}
}
// Modern preview macro (Xcode 15+)
#Preview {
ContentView()
}
2. Basic Views and Controls
Text
Swift
12345Text("Hello, World!")
.font(.title)
.foregroundStyle(.blue) // .foregroundColor is soft-deprecated
.bold()
Button
Swift
12345678910111213141516// Simple button
Button("Click Me") {
print("Button tapped")
}
// Button with custom styling
Button {
// Action
} label: {
Label("Favorite", systemImage: "star.fill")
}
.buttonStyle(.bordered)
// Roles convey intent and platform-appropriate styling
Button("Delete", role: .destructive) { /* ... */ }
Image
Swift
123456789101112131415// System image (SF Symbols)
Image(systemName: "star")
.symbolRenderingMode(.multicolor)
.symbolEffect(.bounce) // iOS 17+
// Local image
Image("myImage")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
// Circular image
Image("profile")
.clipShape(Circle())
TextField
Swift
1234567@State private var username = ""
TextField("Username", text: $username)
.textFieldStyle(.roundedBorder)
.textInputAutocapitalization(.never) // replaces .autocapitalization
.autocorrectionDisabled()
3. Layout and Containers
Stack Views
Swift
123456789101112131415161718// Vertical Stack
VStack(alignment: .leading, spacing: 10) {
Text("First")
Text("Second")
}
// Horizontal Stack
HStack {
Image(systemName: "globe")
Text("Hello")
}
// Depth Stack
ZStack {
Rectangle().fill(.blue)
Text("Overlaid Text")
}
Grid (iOS 16+) and LazyVGrid
Swift
12345678910111213141516171819202122232425// Static Grid for table-like layouts
Grid {
GridRow {
Text("A"); Text("B")
}
GridRow {
Text("C"); Text("D")
}
}
// LazyVGrid for many items
let columns = [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible())
]
ScrollView {
LazyVGrid(columns: columns, spacing: 20) {
ForEach(0..<12) { index in
Text("Item \(index)")
}
}
}
4. Modifiers
Styling Modifiers
Swift
123456Text("Hello")
.font(.headline)
.foregroundStyle(.green)
.padding()
.background(.gray.opacity(0.2), in: .rect(cornerRadius: 10))
Layout Modifiers
Swift
12345Rectangle()
.frame(width: 100, height: 100)
.padding()
.border(.black)
5. State Management
@State (Observation, iOS 17+)
With the Observation framework, @State works directly with @Observable model objects — @ObservedObject / @StateObject are no longer needed for these types.
Swift
1234567891011struct CounterView: View {
@State private var count = 0
var body: some View {
VStack {
Text("Count: \(count)")
Button("Increment") { count += 1 }
}
}
}
@Binding
Swift
12345678910111213141516struct ChildView: View {
@Binding var isOn: Bool
var body: some View {
Toggle("Switch", isOn: $isOn)
}
}
struct ParentView: View {
@State private var toggleState = false
var body: some View {
ChildView(isOn: $toggleState)
}
}
@Observable Models (iOS 17+)
Swift
123456789101112131415import Observation
@Observable
final class UserData {
var username = ""
}
struct UserProfileView: View {
@State private var userData = UserData()
var body: some View {
TextField("Username", text: $userData.username)
}
}
Environment Injection
Swift
123456789// Inject
ContentView().environment(userData)
// Read in any descendant
struct ChildView: View {
@Environment(UserData.self) private var userData
var body: some View { Text(userData.username) }
}
Legacy: ObservableObject (still supported)
Swift
123456789final class LegacyUserData: ObservableObject {
@Published var username = ""
}
struct LegacyView: View {
@StateObject private var userData = LegacyUserData()
// Use @ObservedObject when the object is owned elsewhere
}
6. Navigation
NavigationStack (iOS 16+)
NavigationView is deprecated; use NavigationStack (or NavigationSplitView for multi-column layouts).
Swift
123456789101112NavigationStack {
List(items) { item in
NavigationLink(value: item) {
Text(item.name)
}
}
.navigationTitle("Items")
.navigationDestination(for: Item.self) { item in
DetailView(item: item)
}
}
NavigationSplitView
Swift
123456NavigationSplitView {
SidebarView()
} detail: {
DetailView()
}
Sheets and Alerts
Swift
1234567891011121314151617struct ContentView: View {
@State private var showSheet = false
@State private var showAlert = false
var body: some View {
Button("Show Sheet") { showSheet = true }
.sheet(isPresented: $showSheet) {
DetailView()
.presentationDetents([.medium, .large]) // iOS 16+
.presentationDragIndicator(.visible)
}
.alert("Warning", isPresented: $showAlert) {
Button("OK", role: .cancel) {}
}
}
}
7. Lists and Forms
List with ForEach
Swift
123456789101112131415struct Item: Identifiable, Hashable {
let id = UUID()
let name: String
}
struct ListView: View {
let items = [Item(name: "Apple"), Item(name: "Banana")]
var body: some View {
List(items) { item in
Text(item.name)
}
}
}
Form
Swift
1234567891011121314Form {
Section("Personal Info") {
TextField("Name", text: $name)
Picker("Gender", selection: $gender) {
ForEach(genders, id: \.self) { Text($0) }
}
}
Section {
Toggle("Newsletter", isOn: $subscribeNewsletter)
}
}
.formStyle(.grouped)
8. Data Flow & Concurrency
Async/await in Views
Swift
12345678910struct FeedView: View {
@State private var items: [String] = []
var body: some View {
List(items, id: \.self, rowContent: Text.init)
.task { items = await loadItems() }
.refreshable { items = await loadItems() }
}
}
SwiftData (iOS 17+) — modern persistence
Swift
1234567891011121314151617181920212223242526272829import SwiftData
@Model
final class Note {
var title: String
var createdAt: Date
init(title: String, createdAt: Date = .now) {
self.title = title
self.createdAt = createdAt
}
}
@main
struct NotesApp: App {
var body: some Scene {
WindowGroup { NotesList() }
.modelContainer(for: Note.self)
}
}
struct NotesList: View {
@Query(sort: \Note.createdAt, order: .reverse) private var notes: [Note]
@Environment(\.modelContext) private var context
var body: some View {
List(notes) { Text($0.title) }
}
}
9. Animations and Transitions
Implicit Animation
Swift
12345678@State private var scale = 1.0
Circle()
.fill(.blue)
.scaleEffect(scale)
.animation(.spring, value: scale)
.onTapGesture { scale += 0.5 }
Explicit Animation
Swift
123456Button("Animate") {
withAnimation(.easeInOut(duration: 1)) {
// State changes
}
}
Phase & Keyframe Animations (iOS 17+)
Swift
12345Image(systemName: "heart.fill")
.phaseAnimator([1.0, 1.4, 1.0]) { content, scale in
content.scaleEffect(scale)
}
10. Drawing and Graphics
Path and Shape
Swift
1234567891011struct Triangle: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: rect.midX, y: rect.minY))
path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
path.closeSubpath()
return path
}
}
Canvas and TimelineView
Swift
123456789TimelineView(.animation) { timeline in
Canvas { context, size in
let t = timeline.date.timeIntervalSinceReferenceDate
let rect = CGRect(x: size.width / 2 + CGFloat(sin(t)) * 50 - 25,
y: size.height / 2 - 25, width: 50, height: 50)
context.fill(Path(ellipseIn: rect), with: .color(.blue))
}
}
11. Gestures
Basic Gestures
Swift
123456789Circle()
.onTapGesture { /* tap */ }
.onLongPressGesture { /* long press */ }
.gesture(
DragGesture()
.onChanged { _ in }
.onEnded { _ in }
)
12. Preview and Testing
#Preview Macro (Xcode 15+)
Swift
12345678#Preview {
ContentView()
}
#Preview("Compact", traits: .sizeThatFitsLayout) {
ContentView()
}
PreviewProvider still works but #Preview is now preferred.
13. UIKit / AppKit Integration
UIViewRepresentable
Swift
12345678910struct MyUIKitView: UIViewRepresentable {
func makeUIView(context: Context) -> UIView {
UIView()
}
func updateUIView(_ uiView: UIView, context: Context) {
// Update view
}
}
NSViewRepresentable is the macOS equivalent.
14. Platform-specific Features
Platform Conditionals
Swift
12345678#if os(iOS)
// iOS-specific code
#elseif os(macOS)
// macOS-specific code
#elseif os(visionOS)
// visionOS-specific code
#endif
visionOS (Spatial)
Swift
123// Open an immersive space
@Environment(\.openImmersiveSpace) private var openImmersiveSpace
15. Common Patterns and Best Practices
- Prefer
@ObservableoverObservableObjecton iOS 17+ - Use
NavigationStack/NavigationSplitViewinstead ofNavigationView - Use
foregroundStyle,background(_:in:), and shape style APIs over color-only equivalents - Keep views small; factor subviews and use
@ViewBuilderhelpers - Mark long-lived model containers and async loading with
.task
16. Performance Optimization
- Use
LazyVStack,LazyHStack,LazyVGrid,LazyHGridfor large collections - Adopt
@Observableto minimize unnecessary re-renders (granular tracking) - Use stable
IdentifiableIDs inForEach - Profile with Instruments' SwiftUI template and the View Body signposts
17. Accessibility
Swift
123456789Text("Important")
.accessibilityLabel("Accessibility Label")
.accessibilityHint("Accessibility Hint")
.accessibilityAddTraits(.isHeader)
// Dynamic Type
Text("Body").font(.body)
.dynamicTypeSize(.medium ... .accessibility3)
18. Debugging Tips
- Use
print()orLogger(os.Logger) for structured logging - Leverage Xcode's view debugger and the SwiftUI Instruments template
- Use
_printChanges()insidebodyto diagnose re-render causes - Check view hierarchy, state ownership, and environment values
Note: Always refer to the latest Apple developer documentation for the most up-to-date information.
Continue Learning
Discover more cheatsheets to boost your productivity