Search code examples
navigationviewswiftuiobservableobject

SwiftUI - ObservableObject created multiple times


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.


Solution

  • 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.