Reputation: 2427
So I have a button inside my ViewController
which is connected to ViewModel
and than whenever the button is tapped, in my coordinator I navigate to another screen. The code is like this:
VC
btnShowShopsMap.rx.tap
.bind(to: viewModel.selectShowMap)
VM
let selectShowMap: AnyObserver<Void>
let showShopMap: Observable<Void>
//Inside init
let _selectShowMap = PublishSubject<Void>()
selectShowMap = _selectShowMap.asObserver()
showShopMap = _selectShowMap.asObservable()
Coordinator
viewModel.showShopMap
.subscribe(onNext: { _ in self.showShopMap()})
.disposed(by: userShopVC.disposeBag)
Is it possible to refactor above code? rather than using PublishSubject
is there any other way to do what i am doing using PublishSubject
My VC, VM & Coordinator Flow
Coordinator
func showLoginScreen(logout: Bool = false) {
guard let viewController = LoginViewController.instantiate(storyboard: .main) else { return }
viewController.viewModelFactory = { inputs in
let viewModel = LoginViewModel(inputs: inputs)
viewModel.showHome
.subscribe(onNext: { isLogged in
if isLogged {
self.showHomeScreen()
}
})
.disposed(by: viewController.disposeBag)
inputs.showOnboarding
.subscribe(onNext: { _ in
self.showOnboardingScreen()
})
.disposed(by: viewController.disposeBag)
return viewModel
}
navController.pushViewController(viewController, animated: true)
VC
var viewModelFactory: (LoginViewModel.UIInputs) -> LoginViewModel = { _ in fatalError("factory not set")}
let inputs = LoginViewModel.UIInputs(userNumber: txtUserNumber.rx.text.orEmpty.asDriver(),
password: txtPassword.rx.text.orEmpty.asDriver(),
loginTapped: btnLogin.rx.tap.asSignal(),
userNumberLostFocus: txtUserNumber.rx.controlEvent(.editingDidEnd).asSignal(),
passwordLostFocus: txtPassword.rx.controlEvent(.editingDidEnd).asSignal(),
indicator: indicator,
showOnboarding: btnShowOnboarding.rx.tap.asObservable())
VM
struct UIInputs {
let userNumber: Driver<String>
let password: Driver<String>
let loginTapped: Signal<Void>
let userNumberLostFocus: Signal<Void>
let passwordLostFocus: Signal<Void>
let indicator: ActivityIndicator
let showOnboarding: Observable<Void>
}
init(inputs: UIInputs) {}
Upvotes: 1
Views: 2861
Reputation: 2427
I found very easy and simple way to solve my issue and avoid using Subject
, As there was no logic related to my button in VM, I don't need pass my Button tap to my VM either by using Observable
or using Subject
. Instead I directly accessed my button in my Coordinator like this:
viewController.btnShowOnboarding.rx.tap
.subscribe(onNext: { _ in
self.showOnboardingScreen()
})
.disposed(by: viewController.disposeBag)
Upvotes: 0
Reputation: 10056
Assuming the view controller owns and instantiates the view model, you could pass the tap control event as an observable to the view model initializer, which then exposes it as an observable for the coordinator to subscribe to:
// VC:
let viewModel = ViewModel(..., showShopMap: btnShowShopMap.rx.tap.asObservable())
// VM:
let showShopMap: Observable<Void>
init(..., showShopMap: Observable<Void>) {
self.showShopMap = showShopMap
}
I try not to use subjects whenever possible and instead just expose transformed observables that were passed in.
Upvotes: 1