Working with MVVM
in SwifUI. My aim is to have an enum
state property in the ViewModel
so the View
could adjust it self according to the state property. States could be: idle
, busy
, done
and error
. On done
I want to navigate to another screen using NavigationLink
, however the problem is that it is expecting a Binding<Bool>
and I could not figure out a way to map my enum state to bool.
Here is the simplified code:
struct LoginView: View {
@ObservedObject private var viewModel: LoginViewModel
@ViewBuilder
var body: some View {
...
// success state
NavigationLink(destination: HomeFactory().make(), isActive: self.$viewModel.state /* <---- some sort of mapping should come here */){ EmptyView() }
...
}
}
Hope that I am missing something really basic and it could be easily achieved in an elegant way.
EDIT:
Seems like it should be possible with the next method:NavigationLink(destination: HomeFactory().make(), tag: .done, selection: self.$viewModel.viewState, label: { EmptyView() })
However I get an error and I can't figure out what is wrong: Cannot convert value of type 'Binding<ViewState>' to expected argument type 'Binding<_?>'
Here is the code:
final class LoginViewModel: ObservableObject {
@Published var viewState: ViewState = .idle
func begin() {
..
self.viewState = .done
..
}
}
struct LoginView: View {
@ObservedObject private var viewModel: LoginViewModel
@ViewBuilder
var body: some View {
..
NavigationLink(destination: HomeFactory().make(), tag: .done, selection: self.$viewModel.viewState, label: { EmptyView() })
..
}
UPDATE:
I was very close. The ViewState
in the vm should be optional:@Published var viewState: ViewState? = .idle
There isn't an elegant way to map it in the view.
However, in your LoginViewModel
you can have an @Published variable that gets set when the state is updated.
Here is an example:
class LoginViewModel: ObservableObject {
@Published var shouldNavigate = false
var state: State = .idle {
didSet {
self.shouldNavigate = state == .done
}
}
}
Then change your NavigationLink
to:
NavigationLink(destination: HomeFactory().make(), isActive: self.$viewModel.shouldNavigate){ EmptyView() }
EDIT:
You can navigate based on state, or some other enum using a NavigationLink like this:
NavigationLink(destination: HomeFactory().make(), tag: State.done, selection: self.$state){ EmptyView() }
And update your vm state definition to:@Published var state: State = .idle
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With