Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI View Property willSet & didSet property observers not working

I have a SwiftUI (Beta 5) view with an attached ViewModel. I want to navigate to it via a navigationLink and pass in a simple parameter (called FSAC in this case)

I navigate using

NavigationLink("Next", destination: MyTestView(FSAC: "testFsac"))

The view has an FSAC Property with willSet and didSet property observer

struct MyTestView: View {
    @ObservedObject var vm = MyTestViewModel()

    var FSAC: String {
        willSet {
            print("will set fsac")
        }
        didSet {
            print("did set fsac")
            vm.FSAC = FSAC
        }
    }
        
    var body: some View {
        VStack {
            Text("FSAC: \(FSAC)")
            Text("VM FSAC: \(vm.FSAC)")
        }
    }
}

The print statements are never called. The first text box displays the parameter correctly; the second is blank.

How can I get the Property Observers to fire?

More generally, is there a "correct" way to use a navigationLink to pass parameters to a View that has a ViewModel?

like image 295
Andy Thomas Avatar asked Aug 13 '19 20:08

Andy Thomas


2 Answers

EDIT: On iOS 14 property observers work the same as they did in iOS 13. But, we now have the .onChange(of:perform:) as a replacement. Docs

Text(self.myString).onChange(of: self.myString) { newValue in print("myString changed to: \(newValue)") }

Property observers on basic vars technically work in SwiftUI. If you do something like var view = MyTestView(...) and then view.FSAC = "updated" the the observers will fire (I've verified this).

However, typically with SwiftUI you construct the View (which is a struct not a class) within body during each layout pass. In your case var body: some View { MyTestView(FSAC: "FSAC Value") }.

Property observers do not fire during init, and therefore they aren't usually useful in SwiftUI Views.

If you would like to update some sort of State during init, take a look at this answer.

like image 185
arsenius Avatar answered Oct 13 '22 07:10

arsenius


Thanks to arsenius for an explanation as to why the Property Observers did not fire in this instance.

As a fix, I removed the property in the view, and replaced it with an init function with a signature which included the required data to be passed from the NavigationLink call. Within the init, I called a function on the ViewModel directly.

struct MyTestView: View {

    @ObservedObject var vm = MyTestViewModel()

    init(FSAC: String) {
        if(FSAC != "") {
            vm.SetFsac(FSAC: FSAC)
        }
    }
...
like image 43
Andy Thomas Avatar answered Oct 13 '22 06:10

Andy Thomas