As an experiment, I refactored RxTodo a bit to use a somewhat different style of MVVM, would welcome your thoughts on this and its pros and cons.
The core change is to modify ViewModels into two structs representing inputs and outputs (this can also be done with protocols, but I've found that structs work fine for all cases I've seen so far):
struct ViewModelInputs {
var button: Observable<Void>
}
struct ViewModelOutputs {
var label: Observable<String>
}
Then we can define a ViewModel as a function:
typealias ViewModel = (ViewModelInputs) -> ViewModelOutputs
We avoid Subjects by passing in the inputs directly in the view controller:
let inputs = ViewModelInputs(button: button.rx.tap.asObservable())
let viewModel = myViewModel(inputs)
let outputs = viewModel(inputs)
outputs.label
.bindTo(...)
.addDisposableTo(disposeBag)
There are various benefits to this approach, including not needing to bind each observable separately, a very clear input/output structure, and no need for Subjects which can be an avenue for abuse. There were some additional details I changed along the way:
- Removed the need for deallocated events and subscriptions within view models (at least explicitly)
- Added a couple of mock structs for view model inputs to get back Subjects for testing
Overall I think it retains the original structure of RxTodo while making the architecture a bit more purely functional. I'm submitting a pull request as a way to stimulate discussion, I might write a blog post about this if it turns out to be interesting to people.