@StateObject & @StateObject in Both Parent and ChildView
import SwiftUI
class Counter: ObservableObject {
@Published var count: Int = 0
func increment() {
count += 1
}
}
struct ContentView: View {
@StateObject private var counter : Counter = Counter()
var body: some View {
VStack {
Text("Parent Counter: \(counter.count)")
Button("Increment Child Counter") {
counter.increment()
}
ChildView(counter: counter)
}
}
}
struct ChildView: View {
@StateObject var counter: Counter
var body: some View {
VStack {
Text("Child Counter: \(counter.count)")
Button("Increment Child Counter") {
counter.increment()
}
}
}
}
struct RandomNumberView: View {
@State var randomNumber = 0
var body: some View {
VStack {
Text("Random number is: \(randomNumber)")
Button("Randomize number") {
randomNumber = (0..<1000).randomElement()!
}
}.padding(.bottom)
ContentView()
}
}
#Preview {
RandomNumberView()
}
@StateObject & @ObservedObject in Parent and ChildView
import SwiftUI
class Counter: ObservableObject {
@Published var count: Int = 0
func increment() {
count += 1
}
}
struct ContentView: View {
@StateObject private var counter : Counter = Counter()
var body: some View {
VStack {
Text("Parent Counter: \(counter.count)")
Button("Increment Child Counter") {
counter.increment()
}
ChildView(counter: counter)
}
}
}
struct ChildView: View {
@ObservedObject var counter: Counter
var body: some View {
VStack {
Text("Child Counter: \(counter.count)")
Button("Increment Child Counter") {
counter.increment()
}
}
}
}
struct RandomNumberView: View {
@State var randomNumber = 0
var body: some View {
VStack {
Text("Random number is: \(randomNumber)")
Button("Randomize number") {
randomNumber = (0..<1000).randomElement()!
}
}.padding(.bottom)
ContentView()
}
}
#Preview {
RandomNumberView()
}
Both Having the same Output View.
I have checked many of the articles and my own cases in @StateObject vs @ObservedObject in childView But I don't find anything useful. So what's the difference in this case. I want the explanation for this to code how it works when @StateObject & @ObservedObject used in childView.
When SwiftUI updates a
Viewthat has already been seen before, it restores the values of that view's@Stateand@StateObjectproperties to the values they had on the prior rendering.So, the first time SwiftUI asks
ContentViewfor itsbody,ContentViewcreates aChildViewand passes its (ContentView's)counterto initializeChildView'scounter.When
counter's@Publishedcountproperty changes, SwiftUI asksContentViewfor itsbodyagain.ContentView.bodycreates anotherChildViewand again passes itscounterdown to initializeChildView'scounter.However, SwiftUI recognizes that this
ChildViewrepresents an update to the priorChildView. So it throws away whatever was stored in this newChildView'scounterand sets it to theCounterthat was used the first time it saw thisChildView.This doesn't make a difference in your example, because you only ever create one
Counter.Let's write a example where it does matter.
First, here's a simple
Buttonthat displays a count and increments the count when tapped, just using aBinding:Now here's a view that uses a
StateObjectto hold aCounterand pass itscountto aCountButton:And here's a similar view that uses an
ObservedObjectinstead:And finally, here's a view that holds two
Counterinstances, each in aStateObject. It embeds both of the above views, and lets you choose which of itsCounterinstances to pass to both children:If you tap the top left
CountButtonin this example, you'll find that both children are updated as the count increases. And the buttons in the children also increment the same count. They are all usingcounter0.If you tap the top right
CountButton, only it updates. The other buttons are not affected, because only that top right button usescounter1.Next, tap the toggle. This makes
ContentViewpasscounter1to the children.Now, when you tap the top left button, both the top left button and the “View with StateObject” show the increment, but the “View with ObservedObject” button doesn't change. And if you tap the top right button, both it and the “View with ObservedObject” button show the increment, but the “View with StateObject” button doesn't change.
When you turn on the toggle,
ContentViewpassescounter1to the children, instead of passingcounter0. But, before SwiftUI asksViewWithStateObjectfor itsbody, SwiftUI discards the contents ofViewWithStateObject'scounterproperty and replaces it with the prior contents. So it discards the new reference tocounter1and restores the old reference tocounter0.ViewWithStateObjectwill never switch tocounter1.