Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS SwiftUI: ObservableObject from parent to child

Problem:

Passing down the view model and modifying it from the children, won't refresh the parent.

What I want:

Whenever I modify the view model from the child view, the parent refresh too.

Problem description:

I'm looking for a way to pass down a ViewModel ObservedObject to some child views.

This is my struct:

struct Parent: View {
    @ObservedObject viewModel: ViewModel

     var body: some View {
            NavigationView {
                Form {
                    ScrollView(showsIndicators: false) {
                         Section {
                            ChildViewOne(model: viewModel)
                        }
                        
                        Section {
                            ChildViewTwo(model: viewModel)
                        }
                        
                        Section {
                            ChildViewThree(model: viewModel)
                        }
                    }
                }
            }
        }
}

struct AnyChild: View {
    @ObservedObject viewModel: ViewModel

     var body: some View {
            // some stuff
        }
}

In this way, whenever I modify from the child views the view model, the children rebuild correctly, but the parent won't, and the scrollview does not resize correctly.

I would like to pass down to the children the viewModel like a Binding object, so the parent will refresh too.

But I don't know how to do. Any help?

Solution:

I had a specific problem with the Form when I've removed everything worked fine.

But still the correct solution as @Alladinian wrote is to use the @EnvironmentObject

like image 775
Andrea Miotto Avatar asked Nov 26 '19 11:11

Andrea Miotto


1 Answers

Instead of passing the same model to each subview, you can use @EnvironmentObject instead which is specifically designed for this.

You essentially have to inject your ObservableObject in your parent view with something like this (typically in your appdelegate before presenting the parent view, or in your previews setup for live previewing):

Parent().environmentObject(ViewModel())

then you can automatically get a reference in each view with something like this:

struct AnyChild: View {
    @EnvironmentObject var model: ViewModel
    //...
}

and use it with your bindings

Here is a brief article about the use of environment

Finally, regarding why your solution is not working, you must consider a single source of truth (the observable object in your Parent) and a @Binding for each subview that will eventually change the state of this object. I hope that this makes sense...

like image 91
Alladinian Avatar answered Nov 06 '22 19:11

Alladinian