You can see the effect in following code.
There are two viewModels, one using ObservableObject and second using @Observable observation framework.
The tabView content views owns the viewModel, however when the parent TabView is redrawed, the ownership of @Observation is teared down and new object is recreated on every redraw of TabView
@StateObject with ObservableObject maintains its view ownership and the object is not deallocated or recreated as would be expected.
@State with @Observable do not follow the ownership, and new object is created every time the parent is redrawed.
Following is the code:
import Observation
import SwiftUI
/// ObservableObject, via @StateObject.
class ViewModelObservable: ObservableObject {
@Published public var title: String
deinit {
print("deinit ViewModelObservable")
}
init(title: String) {
self.title = title
print("init ViewModelObservable")
}
}
/// New Observation framework object.
@Observable
class ViewModelObseration {
public var title: String
deinit {
print("deinit ViewModelObseration")
}
init(title: String) {
self.title = title
print("init ViewModelObseration")
}
}
struct SecondTabContentView: View {
/// View Owns object (Observation Framework)
@State var viewModelObservation: ViewModelObseration
/// View Owns object (Observable Object)
@StateObject var viewModelObservable: ViewModelObservable
init() {
_viewModelObservable = StateObject(wrappedValue: ViewModelObservable(title: "SecondTabContentView Observable"))
_viewModelObservation = State(wrappedValue: ViewModelObseration(title: "SecondTabContentView Observation"))
}
var body: some View {
VStack {
Text(viewModelObservable.title)
Text(viewModelObservation.title)
}
}
}
struct FirstTabContentView: View {
/// View Owns object (Observation Framework)
@State var viewModelObservation: ViewModelObseration
/// View Owns object (Observable Object)
@StateObject var viewModelObservable: ViewModelObservable
init() {
_viewModelObservable = StateObject(wrappedValue: ViewModelObservable(title: "FirstTabContentView Observable"))
_viewModelObservation = State(wrappedValue: ViewModelObseration(title: "FirstTabContentView Observation"))
}
var body: some View {
VStack {
Text(viewModelObservable.title)
Text(viewModelObservation.title)
}
}
}
struct ContentView: View {
@State var tabSelection: Int = 1
@State var redrawTrigger: Bool = false
var body: some View {
TabView(selection: $tabSelection) {
FirstTabContentView()
.tag(0)
.tabItem { Label("First \(redrawTrigger ? "true" : "false") ", systemImage: "pen") }
SecondTabContentView()
.tag(1)
.tabItem { Label("Second \(redrawTrigger ? "true" : "false")", systemImage: "pen") }
}
.task {
try? await Task.sleep(for: .seconds(3))
self.redrawTrigger = true
try? await Task.sleep(for: .seconds(3))
self.redrawTrigger = false
}
}
}
#Preview {
ContentView()
}