State Management in SwiftUI

Jun 20 2024 · Swift 5.9, iOS 17.2, Xcode 15.2

Lesson 01: Introduction to State Management with @State

Passing @State to Subviews Demo

Episode complete

Play next episode

Next
Transcript

Welcome to the “Passing @State to Subviews” demo. In this demo, you’ll learn how to share state between views in SwiftUI. You’ll use the simple counter app to demonstrate this concept. Time to get started!

First, open the starter Counter Xcode project in the 07-Passing-@State-to-Subviews-Demo/Starter directory. This project contains all the starter code you’ll need for this demo.

In the CounterApp.swift file, you’ll find the CounterView struct. This view displays the current count and includes buttons to increment and decrement the count:

struct CounterView: View {
    @State private var count: Int = 0

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

In the same file, you’ll see the ContentView struct. It currently looks like this:

struct ContentView: View {
    var body: some View {
        VStack {
            CounterView()
        }
    }
}

In this setup, ContentView simply displays CounterView. Run the app to see the counter in action. Tap the Increment and Decrement buttons to change the count.

To practice sharing state, modify CounterView to receive the count as a parameter instead of managing its own state. This change will demonstrate how to pass state from a parent view to a subview.

Replace the @State property: In the original CounterView, there’s a line defining a @State property for the count. Replace this line with a constant property, as CounterView will now receive the count value as a parameter from its parent view:

struct CounterView: View {
    let count: Int

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

Add an initializer: To set the count property when creating an instance of CounterView, add an initializer to the struct. This initializer takes the count value as a parameter and assigns it to the count property:

struct CounterView: View {
    let count: Int

    init(count: Int) {
        self.count = count
    }

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

Update the body property: In the body of the CounterView, remove the Button views. You should have a Text view that displays the count:

struct CounterView: View {
    let count: Int

    init(count: Int) {
        self.count = count
    }

    var body: some View {
        VStack {
            Text("Count: \(count)")
        }
    }
}

By following these steps, you’ve updated CounterView to receive the count as a parameter from its parent view and display it in a Text view.

Notice that CounterView no longer has a @State property. It now has a constant property count that it uses to display the count value. CounterView is stateless.

Move @State to the parent view: Update ContentView to manage the state and pass it to CounterView. Then, add a count @State property:

struct ContentView: View {
    @State private var count: Int = 0

    var body: some View {
        VStack {
            CounterView()
        }
    }
}

Next, pass the count into the child subview CounterView:

struct ContentView: View {
    @State private var count: Int = 0

    var body: some View {
        VStack {
            CounterView(count: count)
        }
    }
}

Lastly, add the increment and decrement buttons to ContentView:

struct ContentView: View {
    @State private var count: Int = 0

    var body: some View {
        VStack {
            CounterView(count: count)
            Button("Increment") {
                count += 1
            }
            Button("Decrement") {
                count -= 1
            }
        }
    }
}

In this updated version, ContentView has a @State property count that it passes to its child CounterView. ContentView also includes the buttons to increment and decrement the count. When you tap the buttons, ContentView updates its state, and CounterView automatically reflects the changes.

Run the app again to see the behavior. You’ll notice that the count updates as before, but now the state is managed by ContentView and passed to CounterView.

This demo illustrates how to pass state from a parent view to a child subview in SwiftUI. That’s it for this demo. Next is the lesson’s conclusion, which wraps up everything you’ve learned about @State in SwiftUI.

See forum comments
Cinema mode Download course materials from Github
Previous: Passing @State to Subviews Next: Conclusion