@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 ?

@Lontronix would you mind providing the direct link to the feedback you mentioned (FB11936191)? I am experiencing this issue on a project and curious to see if there are any recommendations on a workaround here. Thanks in advance!

Unless you work for Apple there's no way to link to a Feedback issue unless you're the original reporter. If the original reporter also linked the issue with OpenRadar, you may find it there, but it's not always possible to post the follow up messages.

I don't think its possible for me to share the actual feedback with you but I have uploaded a copy to open radar: rdar://FB11936191: StateObject proprety initalized twice when nested in TabView

I also link to the sample project I submitted with the original radar at the bottom of the open radar.

Unfortunately I have not yet received a response yet :cry:

I suspect it's not an issue with StateObject but due to the fact the view might be created twice (as pointed out by @tgrapperon too). I think the behavior might be considered as SwiftUI implementation details and it's harmless in this case (it's a brand new StateObject so creating it twice doesn't lose any thing). If the view was recreated after the state object was modified (e.g. user interacted with the UI), that would be an issue. But I haven't seen such an issue myself.