SwiftUI view update problem

I have encountered an issue with nested view updates that I don't understand. Maybe someone can explain what is happening.

In the code below the ContentView loads 2 views in succession. MyView1 followed by MyView2. MyView displays a button while MyView2 displays the value of its first argument. When the button is pressed MyView1 changes the value of its bound first argument. The ContentView is reloaded because of the change to its first argument. This results in MyView1 and MyView2 both being loaded again. Looked at from a procedural point of view this isn't what I was expecting.

import SwiftUI
@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct ContentView: View {
    @State var mydata1:Int = 0
    @State var mydata2:Int = 1
    var body: some View {
        VStack {
            Text("Hello world \(mydata1)")
            MyView1(v1:$mydata1, v2:$mydata2)
            Text(" myData1 = \(mydata1) myData2 = \(mydata2) ")
            MyView2(v1:$mydata1, v2:$mydata2)
            Text("Bye bye \(mydata1)")
        }
    }
}

struct MyView1:View {
    @Binding var v1:Int
    @Binding var v2:Int
    var body: some View {
        Text("MyView1")
        if $v1.wrappedValue == 0 {
            Button(action: {
                $v1.wrappedValue = 10
            }, label: {
                Text("OK")
            })
        }
    }
}

struct MyView2:View {
    @Binding var v1:Int
    @Binding var v2:Int
    var body: some View {
        Text("MyView2")
        if $v1.wrappedValue == 0 {
            Text("v1 = \(v1) v2 = \(v2) ")
        }
        else {
            Text("???")
        }
    }
}

Answered by Claude31 in 785285022

That's how SwiftUI works.

With the Binding on v1 in MyView1 and MyView2, each time you change the State variable v1 (through Bing in MyView1), that propagates to MyView2 through its own Binding.

Note: no need to use wrappedValue. This works:

            Button(action: {
                v1 = 10
            }, label: {
                Text("OK")
            })

as well as

        if v1 == 0 {
            Text("v1 = \(v1) v2 = \(v2) ")
        }
        else {
            Text("???")
        }
Accepted Answer

That's how SwiftUI works.

With the Binding on v1 in MyView1 and MyView2, each time you change the State variable v1 (through Bing in MyView1), that propagates to MyView2 through its own Binding.

Note: no need to use wrappedValue. This works:

            Button(action: {
                v1 = 10
            }, label: {
                Text("OK")
            })

as well as

        if v1 == 0 {
            Text("v1 = \(v1) v2 = \(v2) ")
        }
        else {
            Text("???")
        }

In fact, same occurs without Binding in MyView2. That's because when a State var changes, SwiftUI reevaluates everything that depends on this State var (and does not reevaluate the full body per se).

See details here:

https://stackoverflow.com/questions/66635304/how-swiftui-detects-there-are-state-variable-values-passed-to-subviews

I posted an example based on your code. May be we'll get even deeper insight into SwiftUI.

SwiftUI view update problem
 
 
Q