Lifetime of @State values in SwiftUI - when to use @State
When using @State, it's important to be aware that the lifetime of the @State value can be tied to the view being included in the view hierarchy. For example, in the following example, the counter value will be reset when the toggle is turned off because the CounterView then drops from the view hierarchy:
struct TogglingCounterView: View {
@State var toggleActive = false
var body: some View {
VStack {
Toggle("Toggle", isOn: $toggleActive)
if toggleActive {
CounterView()
}
}
}
}
struct CounterView: View {
@State var value = 0
var body: some View {
Button("Increment \(value)") {
value += 1
}
}
}
So @State should be used exclusively for values that are tightly coupled to the appearance of Views - anything that can live longer than the appearance of a certain View should be held outside of the Views f.e. in ObservableObjects that are provided to the views as @EnvironmentObject.
A typical example where @State would be a good fit is an attribute like isExpanded for a container that can be expanded / collapsed - this is only relevant as long as this view is visible:
struct ExpandableView: View {
@State var isExpanded: Bool
var body: some View {
VStack {
Button(
action: {
isExpanded.toggle()
},
label: {
HStack {
Text("Headline")
Spacer()
Image(systemName: isExpanded ? "chevron.up" : "chevron.down")
}
}
)
}
if isExpanded {
Text("Content")
}
}
}
(By the way: If you need such a View, you don't need to build it yourself, there is ↗ DisclosureGroup)