skip to Main Content

I am executing a SwiftUI playground that contains 2 labels and 2 buttons that modified the value of these labels.

I’ve stored the value of these labels in a @ObservableObject. Whene I modify the value of any of these properties, both views CustomText2 and CustomText3 are reinitialized, even the one that his values has not changed.

Code:

final class ViewModel: ObservableObject {

    @Published var title: Int
    @Published var title2: Int

    init(title: Int = 0, title2: Int = 0) {
        self.title = title
        self.title2 = title2
    }
}

struct ContentView: View {

    @StateObject var viewModel = ViewModel()

    var body: some View {
        VStack {
            Button(
                action: {
                    viewModel.title += 1
                }, label: {
                    Image(systemName: "globe")
                        .imageScale(.large)
                        .foregroundColor(.accentColor)
                }
            )
            CustomText1(
                    title: $viewModel.title
                )
            Button(
                action: {
                    viewModel.title2 += 1
                }, label: {
                    Image(systemName: "globe")
                        .imageScale(.large)
                        .foregroundColor(.accentColor)
                }
            )
            CustomText2(
                title: $viewModel.title2
            )
        }
        .padding()
    }
}

struct CustomText1: View {

    @Binding var title: Int

    init(
        title: Binding<Int>
    ) {
        self._title = title
    }

    var body: some View {
        Text("(title)")
            .foregroundColor(.black)
    }
}

However if I store both properties as @State in the view and I modify them, the CustomTexts are not reinitialized, they just update their value in the body without executing an init.

Why are they getting reinitialized when I store both properties in the ViewModel?

I’ve tried to make the views conforming Equatable but they’re reinitialized.
Can be a performance problem if the views are initialized many times?

I am interested in not having the subviews reinitialized because I want to perform custom stuff in the init of some subviews.

2

Answers


  1. @ObservableObject is for model data, not view data.

    The reason is when using lets or @State vars, SwiftUI uses dependency tracking to decide if body needs to be called and in your case body doesn’t use the values anywhere so there is no need to call it.

    It can’t track objects in the same way, if there is a @StateObject declared then body is called regardless if any properties are accessed, so it’s best to start with @State value types and only change to @StateObject when you really need features of a reference type. Not very often now we have .task which is the place to put your custom async work.

    Login or Signup to reply.
  2. When you have one StateObject that encompasses multiple State variables, change in one will redraw the entire view. In your case, any change in any variable in viewModel will trigger the publisher of viewModel and reload ContentView

    Also we are not supposed to make any assumptions on when a View will be redrawn, as this might change with different versions of SwiftUI. Its better to move this custom stuff you are doing in the init of views to some other place(if it can be). Init should only do work needed to redraw the view with the new state parameters and nothing else.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search