, rx FormElement, , ViewModel.
FormElement, textInput Variable button Driver. , , , - .
enum FormElement {
case textInput(placeholder: String, text: Variable<String?>)
case button(title: String, enabled:Driver<Bool>, tapped:PublishRelay<Void>)
}
tapped, -, , !
ViewModel, , View, :
class FormViewModel {
let formElementsVariable: Variable<[FormElement]>
let registerObservable: Observable<Bool>
init() {
let username = Variable<String?>(nil)
let password = Variable<String?>(nil)
let passwordConfirmation = Variable<String?>(nil)
let enabled: Driver<Bool>
let tapped = PublishRelay<Void>.init()
let usernameValidObservable = username
.asObservable()
.map { text -> Bool in !(text?.isEmpty ?? true) }
let passwordValidObservable = password
.asObservable()
.map { text -> Bool in text != nil && !text!.isEmpty && text!.count > 5 }
let passwordConfirmationValidObservable = passwordConfirmation
.asObservable()
.map { text -> Bool in text != nil && !text!.isEmpty && text!.count > 5 }
let passwordsMatchObservable = Observable.combineLatest(password.asObservable(), passwordConfirmation.asObservable())
.map({ (password, passwordConfirmation) -> Bool in
password == passwordConfirmation
})
enabled = Observable.combineLatest(usernameValidObservable, passwordValidObservable, passwordConfirmationValidObservable, passwordsMatchObservable)
.map({ (usernameValid, passwordValid, passwordConfirmationValid, passwordsMatch) -> Bool in
usernameValid && passwordValid && passwordConfirmationValid && passwordsMatch
})
.asDriver(onErrorJustReturn: false)
formElementsVariable = Variable<[FormElement]>([
.textInput(placeholder: "username", text: username),
.textInput(placeholder: "password", text: password),
.textInput(placeholder: "password, again", text: passwordConfirmation),
.button(title: "create account", enabled: enabled, tapped: tapped)
])
registerObservable = tapped
.asObservable()
.flatMap({ value -> Observable<Bool> in
NSLog("Create account!!")
return Observable.just(true)
})
}
}
, View:
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
private let disposeBag = DisposeBag()
var formViewModel: FormViewModel = FormViewModel()
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: "TextInputTableViewCell", bundle: nil), forCellReuseIdentifier: "TextInputTableViewCell")
tableView.register(UINib(nibName: "ButtonTableViewCell", bundle: nil), forCellReuseIdentifier: "ButtonTableViewCell")
formViewModel.registerObservable.subscribe().disposed(by: disposeBag)
formViewModel.formElementsVariable.asObservable()
.bind(to: tableView.rx.items) {
(tableView: UITableView, index: Int, element: FormElement) in
let indexPath = IndexPath(row: index, section: 0)
switch element {
case .textInput(let placeholder, let defaultText):
let cell = tableView.dequeueReusableCell(withIdentifier: "TextInputTableViewCell", for: indexPath) as! TextInputTableViewCell
cell.textField.placeholder = placeholder
cell.textField.text = defaultText.value
cell.textField.rx.text.asObservable().bind(to: defaultText).disposed(by: self.disposeBag)
return cell
case .button(let title, let enabled, let tapped):
let cell = tableView.dequeueReusableCell(withIdentifier: "ButtonTableViewCell", for: indexPath) as! ButtonTableViewCell
cell.button.setTitle(title, for: .normal)
enabled.drive(cell.button.rx.isEnabled).disposed(by: self.disposeBag)
cell.button.rx.tap.asObservable().bind(to: tapped).disposed(by: self.disposeBag)
return cell
}
}.disposed(by: disposeBag)
}
}
}
, !
PS. Android, ( ) , , , (rx) swift