Deepak Sharma
Deepak Sharma

Reputation: 6581

SwiftUI view getting reinitialized on autorotation

I have the following SwiftUI code:

struct TestContentView: View {
    @Environment(\.verticalSizeClass) var verticalSizeClass
    @Environment(\.horizontalSizeClass) var horizontalSizeClass
    
    var body: some View {
        TestView()
            .ignoresSafeArea()
    }
}

struct TestView: View { 
    @State var model:TestModel = TestModel()
    
    var body: some View {
        Text("Count \(model.counter)")
    }
}

@Observable
class TestModel {
    var counter:Int = 0
    
    deinit {
        print("Calling deinit on TestModel")
    }
}

My problem is that TestView and TestModel are getting reinitialized on device autorotation, despite TestModel being a @State variable. This is not I want and I understand issue is I have declared size class environment variables that is causing TestView to be reset to new value. One way to solve the problem is by moving the testModel to superview and initializing it, but I am not sure that is always a scalable approach if you have deeper view hierarchies. What is the right approach for solving this issue, especially if one can have horizontalSizeClass or verticalSizeClass deep up in the view hierarchy?

Upvotes: 0

Views: 90

Answers (1)

lorem ipsum
lorem ipsum

Reputation: 29614

Some Environment properties make the View re-instantiate. Developers don’t have any control over that so the solution is to use optional.

A State property always instantiates its default value when SwiftUI instantiates the view.

Instead, you can defer the creation of the object using the task(priority:_:) modifier, which is called only once when the view first appears

struct ContentView: View {
    @State private var library: Library?

    var body: some View {
        switch library {
        case .some(let library):
            //@Bindable var library = library //Uncomment this for Binding - $library.someVariable
            LibraryView(library: library) //This represents the code in your View.
        case .none:
            ProgressView() //Won’t ever be visible since it happens so fast.
                .task {
                    self.library = Library()
                }
         }
    }
}

Delaying the creation of the observable state object ensures that unnecessary allocations of the object doesn’t happen each time SwiftUI initializes the view. Using the task(priority:_:) modifier is also an effective way to defer any other kind of work required to create the initial state of the view, such as network calls or file access.

https://developer.apple.com/documentation/swiftui/state

Upvotes: 1

Related Questions