@StateObject wrongly initialized twice?

Hi

I’ve recently encountered a weird instantiation behaviour where the wrapped value of a @StateObject could be created twice. It happens when the @StateObject is instantiated in the init function of a View. Only if the View is embedded in a NavigationStack AND there is some state driven component inside the View.

Here’s a self contained code sample that demonstrates that. Does anybody know what’s going on?

Thanks.

import SwiftUI

@main
struct MyApp: App {
  var body: some Scene {
    WindowGroup {
      MainView(resolver: makeDependency)
    }
  }
}

func makeDependency() -> MyDependency {
  print("makeDependency") // printed twice
  return MyDependency(value: "5")
}

class MyDependency: ObservableObject {
  @Published
  var value: String

  init(value: String) {
    self.value = value
  }
}

struct MainView: View {
  var resolver: () -> MyDependency

  @State var text = ""

  init(resolver: @escaping () -> MyDependency) {
    self.resolver = resolver
  }

  var body: some View {
    NavigationStack { // if commented, no double dep resolution
      WithDependencyView(resolver: resolver) { dependency in
        VStack {
          Text(dependency.value)
          TextField("Input", text: $text, axis: .horizontal) // if commented, no double dep resolution
        }
      }
    }
  }
}

// equivalent to WithViewStore
struct WithDependencyView<Content: View>: View {
  @StateObject var dependency: MyDependency
  let content: (MyDependency) -> Content

  init(
    resolver: @escaping () -> MyDependency,
    @ViewBuilder content: @escaping (MyDependency) -> Content
  ) {
    self._dependency = StateObject(wrappedValue: resolver())
    self.content = content
  }

  var body: some View {
    self.content(self.dependency)
  }
}
1 Like

I actually ran into the same issue a few days ago. In my case, nesting a view in a TabView is what caused the issue.

I'm pretty sure this is a bug, I ended up filing a feedback about it (FB11936191) with this sample project where you can clearly see the ViewModel, MyDependancy in your case is initialized multiple times when nested in a TabView and only initialized once when it is not nested in a TabView.

I'm pretty sure this is a bug since the documentation say

SwiftUI creates a new instance of the object **only once** for each instance of the structure that declares the object. When published properties of the observable object change, SwiftUI updates the parts of any view that depend on those properties:

Sorry I don't have a solution for you but at least you'll know that you aren't the only one running into this :slightly_smiling_face:

Made a mistake and deleted my response while fixing a typo, so I'm reposting it:
From my experience, NavigationStack sometimes scouts the view tree ahead and on its own (independently of view invalidation), probably to look for navigationDestination or NavigationLinks' values. So this is maybe what you're experiencing.

Thanks a lot for all your nice answers. I assume it is a bug then. I'll try to fill a radar as well.

I can't think of a workaround for now. I can't change my view hierarchy as the path of NavigationStack is bound to my dependency :-(

Maybe someone from the SwiftUI team can chime in :slight_smile: and help me on that ?