Swift
03 / 05

SwiftUI Fundamentals

Swift: SwiftUI Fundamentals

SwiftUI is Apple's declarative UI framework (iOS 13+). Views are value types (structs) — the framework diffs and redraws efficiently. State changes drive UI updates automatically.

Views & Layout

import SwiftUI

// Every SwiftUI view is a struct conforming to View
struct ContentView: View {
    var body: some View {
        VStack(alignment: .leading, spacing: 16) {
            Text("Hello, World!")
                .font(.title)
                .fontWeight(.bold)
                .foregroundColor(.primary)

            HStack {
                Image(systemName: "star.fill")
                    .foregroundColor(.yellow)
                Text("4.8 rating")
            }

            Spacer()

            Button("Get Started") {
                // action
            }
            .buttonStyle(.borderedProminent)
            .frame(maxWidth: .infinity)
        }
        .padding()
        .background(Color(.systemBackground))
    }
}

// List view
struct UserListView: View {
    let users: [User]

    var body: some View {
        List(users, id: \.id) { user in
            NavigationLink(destination: UserDetailView(user: user)) {
                Label(user.name, systemImage: "person.circle")
            }
        }
        .navigationTitle("Users")
    }
}

State & Data Flow

// @State — local view state
struct CounterView: View {
    @State private var count = 0

    var body: some View {
        VStack {
            Text("Count: \(count)")
            Button("+") { count += 1 }
        }
    }
}

// @Binding — pass state to child view
struct ToggleView: View {
    @Binding var isOn: Bool

    var body: some View {
        Toggle("Enable", isOn: $isOn)
    }
}

// @StateObject — own an ObservableObject
// @ObservedObject — receive an ObservableObject from parent
// @EnvironmentObject — inject from ancestor

class CartStore: ObservableObject {
    @Published var items: [CartItem] = []

    func add(_ item: CartItem) {
        items.append(item)
    }
}

struct ShopView: View {
    @StateObject private var cart = CartStore()

    var body: some View {
        NavigationStack {
            ProductListView()
                .environmentObject(cart)
        }
    }
}

struct ProductListView: View {
    @EnvironmentObject var cart: CartStore
    // ...
}

Navigation & Sheets

// NavigationStack (iOS 16+ — replaces NavigationView)
struct AppView: View {
    @State private var path = NavigationPath()

    var body: some View {
        NavigationStack(path: $path) {
            HomeView()
                .navigationDestination(for: User.self) { user in
                    UserDetailView(user: user)
                }
                .navigationDestination(for: Post.self) { post in
                    PostDetailView(post: post)
                }
        }
    }
}

// Sheet (modal)
struct MainView: View {
    @State private var showingSheet = false
    @State private var showingFullScreen = false

    var body: some View {
        Button("Show Sheet") { showingSheet = true }
            .sheet(isPresented: $showingSheet) {
                DetailSheet()
                    .presentationDetents([.medium, .large])  // iOS 16+
            }
            .fullScreenCover(isPresented: $showingFullScreen) {
                FullView()
            }
    }
}

// Alert
.alert("Delete item?", isPresented: $showingAlert) {
    Button("Delete", role: .destructive) { deleteItem() }
    Button("Cancel", role: .cancel) { }
}

Async Image, Task & ViewModifiers

// AsyncImage (built-in async image loading)
AsyncImage(url: URL(string: user.avatarURL)) { image in
    image
        .resizable()
        .aspectRatio(contentMode: .fill)
        .frame(width: 60, height: 60)
        .clipShape(Circle())
} placeholder: {
    ProgressView()
        .frame(width: 60, height: 60)
}

// Task — run async work tied to view lifecycle
struct PostsView: View {
    @State private var posts: [Post] = []
    @State private var isLoading = false

    var body: some View {
        List(posts, id: \.id) { Text($0.title) }
            .task {
                isLoading = true
                posts = await PostService.fetchAll()
                isLoading = false
            }
    }
}

// Custom ViewModifier
struct CardStyle: ViewModifier {
    func body(content: Content) -> some View {
        content
            .padding()
            .background(Color(.secondarySystemBackground))
            .cornerRadius(12)
            .shadow(radius: 2)
    }
}

extension View {
    func cardStyle() -> some View {
        modifier(CardStyle())
    }
}

// Usage
Text("Hello").cardStyle()

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free