SwiftUI View Property willSet & didSet property observers not working
Asked Answered
W

3

15

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?

Wariness answered 13/8, 2019 at 20:39 Comment(4)
Probably a stupid question on my part, but why are you setting properties like that in a View? You already have MyTestViewModel - shouldn't things like this be done there, and just have MyTestView react to the model's state change?Droop
@dfd I am passing the the parameter because, as I see it, the NavigationLink requires me to pass it in (see my first code snippet: MyTestView(FSAC: "testFsac") ). I could instantiate and pass in a whole ViewModel class in the NavigationLink, but that feels like going outside the Single Responsibility for the calling view.Wariness
Like I said, probably a stupid question. I don't use a NavigationLink - no need. But, thinking it's a SwiftUI view, I'd think you could create two "test" variables... make them @State... and see what works. When I've done this, the @State was the key piece. If so - and a big if since I don't know NavigationLink, wouldn't this mean you can use your ObservableObject? "More generally, is there a "correct" way to use a navigationLink to pass parameters to a View that has a ViewModel?" Has this been answered but you are searching on the wrong term? I do this often - call it bullgodding!Droop
try this, it works! https://mcmap.net/q/503600/-swiftui-call-function-on-variable-changeDecedent
H
31

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.

Hoofbeat answered 13/8, 2019 at 23:19 Comment(0)
W
3

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)
        }
    }
...
Wariness answered 14/8, 2019 at 14:41 Comment(0)
R
1

Managed to solve my problem too, I had a toggle that was in the View and didn't get triggered. Moved the binding variable for toggle to observed object that is my (view) model and exposed published property from there. Using Andy's and arsenius' info.

Rabbit answered 1/3, 2020 at 20:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.