@StateObject vs. @ObservedObject
Views in SwiftUI can be redrawn frequently. Therefore, it’s important to understand why and when to use the @StateObject and the @ObservedObject property wrappers when observing an observed object. Both property wrappers tell a SwiftUI View to redraw when the object they are observing updates. The difference between them can be seen when a View is redrawn.
This is a general concept that and not specific to Ditto.
When a View is redrawn the entire View struct is initialized all over again. This means that all of the variables that were created in the View that are not marked with the @State property wrapper also get initialized all over again and set to their default values, including objects marked with @ObservedObject.
When a View is redrawn objects marked with the @StateObject property wrapper do not get re-instantiated. This means that an object marked with @StateObject, the first instance of this object that we create will persist and be used each time the View is redrawn.
To better understand the difference between these two property wrappers try running the code below. We have two counters, the first is created in the ContentView View and the second is created in the CounterTwoViewModel class which is being observed by the CounterTwoView. The ContentView is calling to the CounterTwoView to display the second counter on the screen.
Watch what happens to the second counter value when you increment the first counter and note the difference when you change the viewModel object in the CounterTwoView to use @StateObject rather than @ObservedObject.
When using @ObservedObject on the viewModel you will notice the second counter resets to 0 every time the first counter is incremented.
This is because the ContentView gets redrawn when you increment the first counter causing the CounterTwoView to be re-initialized; which means, the counter in the viewModel gets re-initialized and set to its default value of 0.
When using @StateObject on the viewModel object the viewModel object does not get re-initialized every time the View is redrawn; therefore, our counter does not reset to its default value of 0.
Avoiding side effects when using Ditto
A common issue we see in reactive apps is a failure to dispose of resources as the view is re-drawn.
If you use an ObservableObject for your ditto live queries, call liveQuery.stop() to cancel them; otherwise, your app may see a large accumulation of publishers that infinitely grow.
In general, if your View has a high redraw rate or when an observable object is being used in the same View that initialized that object then you should use @StateObject.
If the observable object is initialized outside of the view that is using it, use @ObservedObject, and clean up any long-running tasks when the view is disposed.
- Currently, there's no official documentation on initializing a @StateObject property with parameters; however, consider this example: https://swiftui-lab.com/random-lessons/#data-10