I have created an ObservableObject in a View.
@ObservedObject var selectionModel = FilterSelectionModel()
I put a breakpoint inside the FilterSelectionModel
's init
function and it is called multiple times. Because this View is part of a NavigationLink
, I understand that it gets created then and along with it, the selectionModel. When I navigate to the View, the selectionModel is created again.
In this same View I have a "sub View" where I pass the selectionModel as an EnvironmentObject
so the sub-view can change it.
AddFilterScreen().environmentObject(self.selectionModel)
When the sub view is dismissed, the selectionModel is once more created and the changes made to it have disappeared.
Interesting Note: At the very top level is a NavigationView
. IF I add
.navigationViewStyle(StackNavigationViewStyle())
to this NavigationView
, my selectionModel's changes disappear. BUT if I do not add the navigationStyle
, the selectionModel's changes made in the sub view remain!! (But I don't want a split nav view, I want a stacked nav view)
In both cases - with or without the navigationStyle
, the selectionModel is created multiple times. I can't wrap my head around how any of this is supposed to work reliably.
Latest SwiftUI updates have brought solution to this problem. (iOS 14 onwards)
@StateObject
is what we should use instead of @ObservedObject
, but only where that object is created and not everywhere in the sub-views where we are passing the same object.
For eg-
class User: ObservableObject {
var name = "mohit"
}
struct ContentView: View {
@StateObject var user = User()
var body: some View {
VStack {
Text("name: \(user.name)")
NameCount(user: self.user)
}
}
}
struct NameCount: View {
@ObservedObject var user
var body: some View {
Text("count: \(user.name.count)")
}
}
In the above example, only the view responsible (ContentView) for creating that object is annotating the User
object with @StateObject
and all other views (NameCount) that share the object is using @ObservedObject
.
By this approach whenever your parent view(ContentView) is re-created, the User
object will not be re-created and it will persist its @State, while your child views just observing
to the same User
object doesn't have to care about its re-creation.
You can instantiate the observable object in the init method, in this way you will be able to hold its value or the value won't disappear.
Instantiate this way in the view file.
@ObservedObject var selectionModel : FilterSelectionModel
init() {
selectionModel = FilterSelectionModel(value : "value to be saved from disappearing")
}
Instantiate this way in the viewModel file.
class FilterSelectionModel : ObservableObject {
@Published var value : String
init(value : String) {
self.value = value
}
}
This is a workaround that I found, but still, the init method is called multiple times and I didn't get any success with this issue.
In order to stop multiple initializing of the ViewModels as the view is declared in the Navigation View and SwiftUI uses struct which is a value type, so eventually these are initialized before the view is presented, therefore you can convert that view into a LazyView, so that it will only be initialized once the view is about to be presented or shown.
// Use this to delay instantiation when using `NavigationLink`, etc...
struct LazyView<Content: View>: View {
var content: () -> Content
var body: some View {
self.content()
}
}
You can call it like this...
NavigationLink(destination: LazyView { ViewTobePresented() })
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With