Category: 05. SwiftUI Basics

https://cdn3d.iconscout.com/3d/premium/thumb/ui-ux-3d-icon-png-download-9831982.png

  • SwiftUI Data Flow @State

    @State

    @State is a property wrapper that stores a value locally in a view and updates the UI when the value changes.

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    struct CounterView: View {
      @State private var count = 0
      var body: some View {
    
    VStack(spacing: 12) {
      Text("Count: \(count)")
      Button("Increment") { count += 1 }
    }
    .padding()
    } }
  • SwiftUI Data Flow

    SwiftUI Data Flow

    Manage state with @State and @Binding, and share data using ObservableObject, @StateObject, and @EnvironmentObject.


    @State and @Binding

    Use @State for local view state and @Binding to expose it to child views.

    Syntax:

    • @State private var name = ""
    • struct Child { @Binding var name: String }
    • pass with Child(name: $name)

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    struct Child: View {
      @Binding var text: String
      var body: some View {
    
    TextField("Enter", text: $text)
      .textFieldStyle(.roundedBorder)
    } } struct Parent: View { @State private var text = "Hello" var body: some View {
    VStack {
      Text(text)
      Child(text: $text)
    }
    .padding()
    } }

    This example shows a parent exposing local state to a child with @Binding so edits update the parent’s value.



    ObservableObject and StateObject

    Share data across views with ObservableObject.

    Own it with @StateObject or consume with @ObservedObject.

    Syntax:

    • class VM: ObservableObject { @Published var count = 0 }
    • @StateObject private var vm = VM()
    • @ObservedObject var vm: VM

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    class CounterModel: ObservableObject {
      @Published var count = 0
      func increment() { count += 1 }
    }
    
    struct ChildView: View {
      @ObservedObject var model: CounterModel
      var body: some View {
    
    HStack {
      Text("Count: \(model.count)")
      Button("Inc") { model.increment() }
    }
    } } struct ParentView: View { @StateObject private var model = CounterModel() var body: some View {
    VStack(spacing: 12) {
      ChildView(model: model)
    }
    .padding()
    } }

    The parent owns the model with @StateObject, while the child observes it with @ObservedObject to reflect changes.


    @EnvironmentObject

    Provide shared app state from a root view using .environmentObject(), then consume it in descendants with @EnvironmentObject.

    Syntax:

    • Root().environmentObject(model)
    • @EnvironmentObject var model: Model

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    final class AppSettings: ObservableObject {
      @Published var theme = "Light"
    }
    
    struct Root: View {
      @StateObject private var settings = AppSettings()
      var body: some View {
    
    VStack(spacing: 8) {
      Button("Toggle Theme") { settings.theme = (settings.theme == "Light") ? "Dark" : "Light" }
      Child()
    }
    .environmentObject(settings)
    .padding()
    } } struct Child: View { @EnvironmentObject var settings: AppSettings var body: some View { Text("Theme: \(settings.theme)") } }
  • SwiftUI Data Flow

    SwiftUI Data Flow

    Manage state with @State and @Binding, and share data using ObservableObject, @StateObject, and @EnvironmentObject.


    @State and @Binding

    Use @State for local view state and @Binding to expose it to child views.

    Syntax:

    • @State private var name = ""
    • struct Child { @Binding var name: String }
    • pass with Child(name: $name)

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    struct Child: View {
      @Binding var text: String
      var body: some View {
    
    TextField("Enter", text: $text)
      .textFieldStyle(.roundedBorder)
    } } struct Parent: View { @State private var text = "Hello" var body: some View {
    VStack {
      Text(text)
      Child(text: $text)
    }
    .padding()
    } }

    This example shows a parent exposing local state to a child with @Binding so edits update the parent’s value.



    ObservableObject and StateObject

    Share data across views with ObservableObject.

    Own it with @StateObject or consume with @ObservedObject.

    Syntax:

    • class VM: ObservableObject { @Published var count = 0 }
    • @StateObject private var vm = VM()
    • @ObservedObject var vm: VM

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    class CounterModel: ObservableObject {
      @Published var count = 0
      func increment() { count += 1 }
    }
    
    struct ChildView: View {
      @ObservedObject var model: CounterModel
      var body: some View {
    
    HStack {
      Text("Count: \(model.count)")
      Button("Inc") { model.increment() }
    }
    } } struct ParentView: View { @StateObject private var model = CounterModel() var body: some View {
    VStack(spacing: 12) {
      ChildView(model: model)
    }
    .padding()
    } }

    The parent owns the model with @StateObject, while the child observes it with @ObservedObject to reflect changes.


    @EnvironmentObject

    Provide shared app state from a root view using .environmentObject(), then consume it in descendants with @EnvironmentObject.

    Syntax:

    • Root().environmentObject(model)
    • @EnvironmentObject var model: Model

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    final class AppSettings: ObservableObject {
      @Published var theme = "Light"
    }
    
    struct Root: View {
      @StateObject private var settings = AppSettings()
      var body: some View {
    
    VStack(spacing: 8) {
      Button("Toggle Theme") { settings.theme = (settings.theme == "Light") ? "Dark" : "Light" }
      Child()
    }
    .environmentObject(settings)
    .padding()
    } } struct Child: View { @EnvironmentObject var settings: AppSettings var body: some View { Text("Theme: \(settings.theme)") } }

    The root injects a shared AppSettings; any descendant can read updates via @EnvironmentObject.

  • SwiftUI Navigation Toolbar & Bar Items

    Basic toolbar

    Add leading and trailing bar buttons using .toolbar with ToolbarItem placements.

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    struct ToolbarBasicDemo: View {
      var body: some View {
    
    NavigationStack {
      Text("Content")
        .navigationTitle("Toolbar")
        .toolbar {
          ToolbarItem(placement: .navigationBarLeading) { Button("Cancel") {} }
          ToolbarItem(placement: .navigationBarTrailing) { Button("Add") {} }
        }
    }
    } }

    In the example above, the .toolbar modifier adds a leading Cancel and trailing Add button.



    Placements

    Place items at the bottom bar or show keyboard-anchored actions.

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    struct ToolbarPlacementsDemo: View {
      @State private var text = ""
      var body: some View {
    
    NavigationStack {
      VStack(spacing: 12) {
        TextField("Type...", text: $text).textFieldStyle(.roundedBorder)
      }
      .padding()
      .navigationTitle("Placements")
      .toolbar {
        ToolbarItem(placement: .bottomBar) { Button("Edit") {} }
        ToolbarItemGroup(placement: .keyboard) { Spacer(); Button("Done") { } }
      }
    }
    } }
  • SwiftUI Navigation navigationDestination

    SwiftUI Navigation: navigationDestination

    Define typed destinations for values pushed onto a NavigationStack path using .navigationDestination(for:).


    Typed destinations

    Typed destinations allow you to define destinations for values pushed onto a NavigationStack path using .navigationDestination(for:).

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    struct NumberDetail: View { let n: Int; var body: some View { Text("Number: \(n)") } }
    struct TextDetail: View { let s: String; var body: some View { Text("Text: \(s)") } }
    
    struct DestinationTypedDemo: View {
      @State private var intPath: [Int] = []
      @State private var stringPath: [String] = []
      var body: some View {
    
    NavigationStack(path: $intPath) {
      VStack(spacing: 12) {
        Button("Push Int 7") { intPath.append(7) }
        NavigationLink("Push Text 'Hi'", value: "Hi")
      }
      .navigationDestination(for: Int.self) { n in NumberDetail(n: n) }
      .navigationDestination(for: String.self) { s in TextDetail(s: s) }
      .navigationTitle("Typed Destinations")
    }
    } }

    In the example above, the NumberDetail and TextDetail views are used as destinations for values pushed onto a NavigationStack path using .navigationDestination(for:).



    Enum-based routing

    Enum-based routing allows you to define destinations for values pushed onto a NavigationStack path using .navigationDestination(for:).

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    enum Route: Hashable { case number(Int), text(String) }
    
    struct DestinationMultiDemo: View {
      @State private var path: [Route] = []
      var body: some View {
    
    NavigationStack(path: $path) {
      VStack(spacing: 12) {
        Button("Go to number 5") { path.append(.number(5)) }
        Button("Go to text 'Hello'") { path.append(.text("Hello")) }
      }
      .navigationDestination(for: Route.self) { r in
        switch r {
        case .number(let n): Text("Number: \(n)")
        case .text(let s): Text("Text: \(s)")
        }
      }
      .navigationTitle("Enum Destinations")
    }
    } }
  • SwiftUI Navigation Programmatic Navigation

    SwiftUI Navigation: Programmatic Navigation

    Control navigation in code using NavigationPath and typed destinations.


    Path controls

    NavigationPath controls the navigation path.

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    struct NumberDetail: View { let n: Int; var body: some View { Text("Number: \(n)") } }
    
    struct ProgrammaticPathDemo: View {
      @State private var path: [Int] = []
      var body: some View {
    
    NavigationStack(path: $path) {
      VStack(spacing: 12) {
        Button("Go to 42") { path.append(42) }
        Button("Back") { if !path.isEmpty { _ = path.removeLast() } }
      }
      .navigationDestination(for: Int.self) { n in NumberDetail(n: n) }
      .navigationTitle("Programmatic")
    }
    } }

    In the example above, the NavigationPath is used to control the navigation path.



    Reset & multi-push

    Reset the path and push multiple destinations.

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    struct ResetMultiPushDemo: View {
      @State private var path: [Int] = []
      var body: some View {
    
    NavigationStack(path: $path) {
      VStack(spacing: 12) {
        Button("Reset to root") { path.removeAll() }
        Button("Go to 1 → 2 → 3") { path.append(contentsOf: [1,2,3]) }
      }
      .navigationDestination(for: Int.self) { n in Text("Screen #\(n)") }
      .navigationTitle("Reset & Multi-Push")
    }
    } }
  • SwiftUI Navigation Deep Links

    SwiftUI Navigation: Deep Links

    Handle incoming URLs to navigate directly to content using onOpenURL.


    Basic onOpenURL

    onOpenURL is used to handle incoming URLs.

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    struct ItemDetail: View { let id: Int; var body: some View { Text("Item #\(id)") } }
    
    struct DeepLinkBasicDemo: View {
      @State private var path: [Int] = []
      var body: some View {
    
    NavigationStack(path: $path) {
      Text("Home")
        .onOpenURL { url in
          if url.scheme == "myapp", url.host == "item", let id = Int(url.lastPathComponent) {
            path.append(id)
          }
        }
        .navigationDestination(for: Int.self) { id in ItemDetail(id: id) }
        .navigationTitle("Deep Links")
    }
    } }

    In the example above, the onOpenURL modifier is used to handle incoming URLs.



    Route mapping

    Handle multiple hosts and route to different destinations.

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    struct ProfileView: View { let user: String; var body: some View { Text("Profile: \(user)") } }
    struct ArticleView: View { let slug: String; var body: some View { Text("Article: \(slug)") } }
    
    enum Route: Hashable { case item(Int), profile(String), article(String) }
    
    struct DeepLinkRoutesDemo: View {
      @State private var path: [Route] = []
      var body: some View {
    
    NavigationStack(path: $path) {
      Text("Home")
        .onOpenURL { url in
          guard url.scheme == "myapp" else { return }
          switch url.host {
          case "item": if let id = Int(url.lastPathComponent) { path.append(.item(id)) }
          case "profile": path.append(.profile(url.lastPathComponent))
          case "article": path.append(.article(url.lastPathComponent))
          default: break
          }
        }
        .navigationDestination(for: Route.self) { route in
          switch route {
          case .item(let id): Text("Item #\(id)")
          case .profile(let u): ProfileView(user: u)
          case .article(let s): ArticleView(slug: s)
          }
        }
        .navigationTitle("Routes")
    }
    } }
  • SwiftUI Navigation Sheets & Popovers

    SwiftUI Navigation: Sheets & Popovers

    Present modals using .sheet.fullScreenCover, and popovers.


    Basic Sheet

    Present a modal using .sheet.

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    struct SheetsBasicDemo: View {
      @State private var show = false
      var body: some View {
    
    Button("Show Sheet") { show = true }
      .sheet(isPresented: $show) {
        VStack(spacing: 12) {
          Text("Modal")
          Button("Close") { show = false }
        }
        .padding()
      }
    } }

    In the example above, the .sheet modifier is used to present a modal.



    Full Screen Cover

    Present a full screen modal using .fullScreenCover.

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    struct FullScreenCoverDemo: View {
      @State private var show = false
      var body: some View {
    
    Button("Present Full Screen") { show = true }
      .fullScreenCover(isPresented: $show) {
        CoverView(onClose: { show = false })
      }
    } } struct CoverView: View { let onClose: () -> Void var body: some View {
    ZStack {
      Color.black.opacity(0.85).ignoresSafeArea()
      VStack(spacing: 12) {
        Text("Full Screen").foregroundStyle(.white)
        Button("Dismiss") { onClose() }
          .tint(.white)
      }
      .padding()
    }
    } }
  • SwiftUI Navigation TabView

    SwiftUI Navigation: TabView

    Switch between sections with TabView and .tabItem.


    Basic TabView

    Use TabView to switch between sections.

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    struct TabBasicDemo: View {
      var body: some View {
    
    TabView {
      Text("Home").tabItem { Label("Home", systemImage: "house") }
      Text("Settings").tabItem { Label("Settings", systemImage: "gear") }
    }
    } }

    In the example above, the TabView is used to switch between sections.



    Selection & Badges

    Track the active tab with @State and add .badge().

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    enum Tab: Hashable { case home, inbox }
    
    struct TabSelectionDemo: View {
      @State private var selection: Tab = .home
      var body: some View {
    
    TabView(selection: $selection) {
      Text("Home").tabItem { Label("Home", systemImage: "house") }.tag(Tab.home)
      Text("Inbox").tabItem { Label("Inbox", systemImage: "tray") }.badge(3).tag(Tab.inbox)
    }
    } }
  • SwiftUI Navigation NavigationLink

    SwiftUI Navigation: NavigationLink

    Use NavigationLink to push a destination view when tapped.


    Basic destination

    The basic NavigationLink is used to push a destination view when tapped.

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    struct Detail: View { var body: some View { Text("Detail") } }
    
    struct NavLinkBasicDemo: View {
      var body: some View {
    
    NavigationStack {
      NavigationLink("Go to Detail", destination: Detail())
        .navigationTitle("Home")
    }
    } }

    In the example above, the NavigationLink is used to push a view onto the stack and the .navigationDestination is used to specify the view to display when a link is selected.



    Value-based link

    The value-based NavigationLink is used to push a destination view when tapped.

    Use NavigationLink(value:) with a typed navigationDestination(for:).

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    struct NumberDetail: View { let n: Int; var body: some View { Text("Number: \(n)") } }
    
    struct NavLinkValueDemo: View {
      var body: some View {
    
    NavigationStack {
      VStack(spacing: 12) {
        NavigationLink("Go to 99", value: 99)
      }
      .navigationDestination(for: Int.self) { n in NumberDetail(n: n) }
      .navigationTitle("Value Links")
    }
    } }