Reputation: 335
I am relatively new to RxSwift and am trying to implement best practices as I develop.
On my home view controller, I have to present a custom alert view controller where the user enters text into a textfield and taps confirm. Assuming the text is valid, the alert is dismissed and a new view controller is pushed.
To avoid using a callback or delegate, I present the alert view controller, then my home view controller subscribes to the alert view controller's textfield and confirm button.
Is it bad practice to subscribe to a different view controller?
Pseudocode:
let alert = viewModel.textFieldAlert()
present(alert)
alertSubscriptions(alert)
alertSubscriptions:
alert.textField.rx.text.subscribe(onNext: { [weak self] text in
self?.viewModel.numberOfItems.value = text ?? ""
}).addDisposableTo(disposeBag)
alert.confirmButton.rx.tap.subscribe(onNext: { [weak self] _ in
guard self != nil else { return }
if !self!.viewModel.validText { return }
alert.dismiss()
self!.alertConfirmed()
}).addDisposableTo(disposeBag)
I have tested this code and it works without any problems.
Upvotes: 1
Views: 1850
Reputation: 33979
I happen to have written an article about this very subject: https://medium.com/@danielt1263/encapsulating-the-user-in-a-function-ec5e5c02045f The article uses Promises, but the same procedure would, and IMHO should, be done when using Rx.
I think something like this would be better:
extension UIViewController {
func infoAlert(title: String, message: String, isValidInput: @escaping (String) -> Bool) -> Observable<String> {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
let confirm = PublishSubject<Void>()
let confirmed = UIAlertAction(title: "OK", style: .default) { (action) in
confirm.onNext()
}
let text = PublishSubject<String>()
alert.addTextField { textField in
textField.placeholder = "Enter Data"
_ = textField.rx.text.orEmpty.bind(to: text)
}
_ = text.map(isValidInput)
.bind(to: confirmed.rx.isEnabled)
alert.addAction(confirmed)
present(alert, animated: true, completion: nil)
return confirm.withLatestFrom(text)
}
}
By containing all the code in a sequence emitter function (i.e., a function that returns an observable,) you open the door for adding the function to a chain of observables.
For example, the function above could be flatMapped off of a button press in your view controller, or otherwise added to a more involved Observable pipeline.
Upvotes: 2