I have a MVVM SwiftUI app that will navigate to another view based on the value of a @Published property of a view model:
class ViewModel: ObservableObject {
@Published public var showView = false
func doShowView() {
showView = true
}
}
struct MyView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
NavigationView {
MySubView().environmentObject(viewModel)
}
}
}
struct MySubView: View {
@EnvironmentObject private var viewModel: ViewModel
var body: some View {
VStack {
Button(action: {
viewModel.doShowView()
}) {
Text("Button")
}
NavigationLink(
destination: SomeOtherView(),
isActive: $viewModel.showView,
label: {
EmptyView()
})
}
}
}
The problem is sometimes when I run the app it will work only every other time and sometimes it works perfectly as expected.
The cause seems to be that sometimes when the property is set in the view model (in doShowView()) SwiftUI will immediately render my view with the old value of showView and in the working case the view is rendered on the next event cycle with the updated value.
Is this a feature (due to the fact @Published is calling objectWillChange under the hood and the view is rendering due to that) or a bug?
If it is a feature (and I just happen to get lucky when it works as I want it to) what is the best way to guarantee it renders my view after the new value is set?
Note this is only a simple example, I cannot use a @State variable in the button action since in the real code the doShowView() method may or may not set the showView property in the view model.