Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is ti possible to `map` an enum to Binding<Bool> in SwiftUI?

Tags:

ios

swift

swiftui

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

like image 586
EvZ Avatar asked Oct 28 '19 08:10

EvZ


1 Answers

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

like image 99
Vlad Avatar answered Oct 02 '22 23:10

Vlad