LearnThatStack Ace your next interview

SwiftUI.
Interview cheat sheet.

Quick reference for SwiftUI - sectioned for fast scanning. Skim the part you're shaky on, walk in confident.

Mobile Development 14-section reference ~7 min read

Overview

SwiftUI is Apple's modern declarative UI framework introduced at WWDC 2019. It represents a paradigm shift from UIKit's imperative approach, enabling developers to build user interfaces across all Apple platforms with less code and a more intuitive syntax. SwiftUI leverages Swift's power to provide a declarative, functional, and reactive approach to UI development.

Why SwiftUI is Revolutionary

  • Declarative Syntax: Describe what your UI should look like, not how to build it
  • Single Source of Truth: Automatic UI updates when data changes
  • Cross-Platform: One codebase for iOS, macOS, watchOS, tvOS
  • Live Preview: See changes instantly without building
  • Built-in Animations: Smooth transitions with minimal code
  • Accessibility: Automatic support for VoiceOver, Dynamic Type
  • Dark Mode: Automatic adaptation to user preferences
  • Less Code: 10x less code than UIKit for many interfaces

Basic App Structure

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

Basic View

struct ContentView: View {
    var body: some View {
        Text("Hello, SwiftUI!")
    }
}

Views and Modifiers

Common Views

// Text
Text("Hello").font(.title).foregroundColor(.blue)

// Image
Image(systemName: "star.fill")
Image("photo").resizable().aspectRatio(contentMode: .fit)

// Button
Button("Tap me") { print("Tapped") }
Button(action: { }) { Text("Custom") }

// TextField
TextField("Enter name", text: $name)

// Toggle
Toggle("Enable", isOn: $isEnabled)

// Slider
Slider(value: $value, in: 0...100)

// Picker
Picker("Select", selection: $selected) {
    Text("Option 1").tag(1)
    Text("Option 2").tag(2)
}

Common Modifiers

.padding()
.background(Color.blue)
.foregroundColor(.white)
.font(.headline)
.frame(width: 200, height: 100)
.cornerRadius(10)
.shadow(radius: 5)
.opacity(0.8)
.scaleEffect(1.2)
.rotationEffect(.degrees(45))
.overlay(Text("Overlay"))
.border(Color.red, width: 2)

Layout System

Stack Views

// Vertical Stack
VStack(alignment: .leading, spacing: 10) {
    Text("Item 1")
    Text("Item 2")
}

// Horizontal Stack
HStack(spacing: 20) {
    Image(systemName: "star")
    Text("Rating")
}

// Z-Stack (Depth)
ZStack {
    Rectangle().fill(Color.blue)
    Text("On Top")
}

Spacer and Divider

HStack {
    Text("Left")
    Spacer()
    Text("Right")
}

VStack {
    Text("Top")
    Divider()
    Text("Bottom")
}

GeometryReader

GeometryReader { geometry in
    Text("Width: \(geometry.size.width)")
        .frame(width: geometry.size.width / 2)
}

LazyStacks (iOS 14+)

ScrollView {
    LazyVStack {
        ForEach(0..<1000) { i in
            Text("Row \(i)")
        }
    }
}

State Management

@State

  • Local state for value types
  • View owns the data
struct CounterView: View {
    @State private var count = 0
    
    var body: some View {
        Button("Count: \(count)") {
            count += 1
        }
    }
}

@Binding

  • Two-way connection to state
  • Child views modify parent's state
struct ChildView: View {
    @Binding var isOn: Bool
    
    var body: some View {
        Toggle("Switch", isOn: $isOn)
    }
}

struct ParentView: View {
    @State private var isEnabled = false
    
    var body: some View {
        ChildView(isOn: $isEnabled)
    }
}

@StateObject (iOS 14+)

  • Reference type state
  • View creates and owns the object
class ViewModel: ObservableObject {
    @Published var text = "Hello"
}

struct ContentView: View {
    @StateObject private var viewModel = ViewModel()
    
    var body: some View {
        Text(viewModel.text)
    }
}

@ObservedObject

  • Reference type state
  • View observes but doesn't own
struct DetailView: View {
    @ObservedObject var viewModel: ViewModel
    
    var body: some View {
        Text(viewModel.text)
    }
}

@EnvironmentObject

  • Shared data across view hierarchy
// Set in parent
ContentView().environmentObject(UserSettings())

// Use in any child
struct ChildView: View {
    @EnvironmentObject var settings: UserSettings
    
    var body: some View {
        Text(settings.username)
    }
}

@Environment

  • System values and settings
struct ContentView: View {
    @Environment(\.colorScheme) var colorScheme
    @Environment(\.presentationMode) var presentationMode
    
    var body: some View {
        Text(colorScheme == .dark ? "Dark" : "Light")
    }
}

Lists and Collections

List

// Static
List {
    Text("Row 1")
    Text("Row 2")
}

// Dynamic
List(items) { item in
    Text(item.name)
}

// With sections
List {
    Section("Header") {
        ForEach(items) { item in
            Text(item.name)
        }
    }
}

ForEach

ForEach(0..<5) { index in
    Text("Item \(index)")
}

ForEach(items, id: \.self) { item in
    Text(item)
}

List Operations

List {
    ForEach(items) { item in
        Text(item.name)
    }
    .onDelete(perform: deleteItems)
    .onMove(perform: moveItems)
}

ScrollView

ScrollView(.horizontal, showsIndicators: false) {
    HStack {
        ForEach(items) { item in
            ItemView(item: item)
        }
    }
}

Gestures and Animations

Basic Animations

@State private var scale = 1.0

Text("Tap me")
    .scaleEffect(scale)
    .onTapGesture {
        withAnimation(.spring()) {
            scale = scale == 1.0 ? 1.5 : 1.0
        }
    }

Animation Types

.animation(.default, value: scale)
.animation(.linear(duration: 1), value: offset)
.animation(.easeInOut, value: rotation)
.animation(.spring(), value: position)

Transitions

if showView {
    Text("Hello")
        .transition(.slide)
        .transition(.scale)
        .transition(.opacity)
        .transition(.move(edge: .leading))
}

Gesture Recognizers

// Tap
.onTapGesture { print("Tapped") }

// Long Press
.onLongPressGesture { print("Long pressed") }

// Drag
.gesture(
    DragGesture()
        .onChanged { value in
            offset = value.translation
        }
)

// Magnification
.gesture(
    MagnificationGesture()
        .onChanged { value in
            scale = value
        }
)

Data Flow

ObservableObject Protocol

class DataModel: ObservableObject {
    @Published var items = [Item]()
    
    func addItem(_ item: Item) {
        items.append(item)
    }
}

Combine Integration

class ViewModel: ObservableObject {
    @Published var searchText = ""
    @Published var results = [String]()
    
    private var cancellables = Set<AnyCancellable>()
    
    init() {
        $searchText
            .debounce(for: .seconds(0.5), scheduler: RunLoop.main)
            .sink { [weak self] text in
                self?.performSearch(text)
            }
            .store(in: &cancellables)
    }
}

Lifecycle Methods

View Lifecycle

struct ContentView: View {
    var body: some View {
        Text("Hello")
            .onAppear {
                print("View appeared")
            }
            .onDisappear {
                print("View disappeared")
            }
            .task {
                // Async work when view appears
                await loadData()
            }
    }
}

Scene Phase

@Environment(\.scenePhase) var scenePhase

var body: some View {
    ContentView()
        .onChange(of: scenePhase) { newPhase in
            switch newPhase {
            case .active:
                print("App is active")
            case .inactive:
                print("App is inactive")
            case .background:
                print("App is in background")
            @unknown default:
                break
            }
        }
}

Performance Optimization

Best Practices

  1. Use LazyVStack/LazyHStack for large lists
  2. Avoid complex calculations in body
  3. Use @StateObject for view-owned objects
  4. Minimize view updates with proper state management
  5. Use equatable() for expensive views

View Identity

// Stable identity
ForEach(items, id: \.id) { item in
    ItemView(item: item)
}

// Explicit identity
Text("Hello")
    .id(UUID())

Lazy Loading

LazyVGrid(columns: [GridItem(.adaptive(minimum: 100))]) {
    ForEach(items) { item in
        ItemView(item: item)
    }
}

Advanced Topics

Custom ViewModifier

struct TitleModifier: ViewModifier {
    func body(content: Content) -> some View {
        content
            .font(.largeTitle)
            .foregroundColor(.blue)
            .padding()
    }
}

extension View {
    func titleStyle() -> some View {
        modifier(TitleModifier())
    }
}

PreferenceKey

struct SizePreferenceKey: PreferenceKey {
    static var defaultValue: CGSize = .zero
    static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
        value = nextValue()
    }
}

// Usage
.background(GeometryReader { geo in
    Color.clear.preference(key: SizePreferenceKey.self, value: geo.size)
})
.onPreferenceChange(SizePreferenceKey.self) { size in
    print("Size: \(size)")
}

Custom Container Views

struct Card<Content: View>: View {
    let content: Content
    
    init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }
    
    var body: some View {
        content
            .padding()
            .background(Color.white)
            .cornerRadius(10)
            .shadow(radius: 5)
    }
}

Environment Values

private struct ThemeKey: EnvironmentKey {
    static let defaultValue = Theme.light
}

extension EnvironmentValues {
    var theme: Theme {
        get { self[ThemeKey.self] }
        set { self[ThemeKey.self] = newValue }
    }
}

Common Interview Questions

1. What's the difference between @State and @StateObject?

  • @State: For value types, view owns the data
  • @StateObject: For reference types (ObservableObject), view creates and owns the object

2. When to use @ObservedObject vs @StateObject?

  • @StateObject: When the view creates the object
  • @ObservedObject: When the object is passed in from parent

3. How does SwiftUI update views?

  • SwiftUI uses a diffing algorithm
  • Only updates views whose state has changed
  • Structural identity helps optimize updates

4. What is @ViewBuilder?

  • Function builder that constructs views from closures
  • Allows multiple child views without explicit container
  • Used in custom container views

5. How to pass data between views?

  • @Binding: Parent to child, two-way
  • @EnvironmentObject: Across view hierarchy
  • @Environment: System values
  • Initializer: Direct passing

6. What are Property Wrappers in SwiftUI?

  • Special attributes that add behavior to properties
  • Examples: @State, @Binding, @Published, @ObservedObject

7. How to handle async operations?

.task {
    await fetchData()
}
.refreshable {
    await refreshData()
}

8. Key Performance Tips:

  • Use lazy stacks for large lists
  • Implement Equatable for complex views
  • Avoid heavy computations in body
  • Use @StateObject correctly
  • Profile with Instruments

Quick Reference

State Management Decision Tree

Is it a value type? → @State
Is it passed from parent? → @Binding
Is it a reference type I create? → @StateObject  
Is it a reference type passed in? → @ObservedObject
Is it shared across many views? → @EnvironmentObject
Is it a system value? → @Environment

Common Patterns

// Conditional Views
if condition {
    TrueView()
} else {
    FalseView()
}

// Optional Binding
if let value = optionalValue {
    Text(value)
}

// Switch Statement
switch value {
case .option1:
    View1()
case .option2:
    View2()
}

Debugging Tips

  • Use .border(Color.red) to visualize frames
  • Print in .onAppear to track view lifecycle
  • Use Random colors for layout debugging
  • Check Xcode Memory Graph for retain cycles
  • Use SwiftUI Preview variants
Found this useful? Pass it on.
Pro · $10/mo

The sheet is free. Pro goes deeper.

Pro opens the full question library behind every sheet, every refresher and a monthly AI allowance. One subscription, all formats.

Full question library All refreshers Cancel anytime