Aren’t you tired of massive view model with complex dependencies?
Introducing MVC
Nothing fancy. Just follow Apple SDK, but with some refactoring in mind.
Now let’s refactor the supposed-to-be refactor of MVC: Refactor MVC iOS App to MVVM with RxSwift in Minutes
First let’s get a broad overview of the example app. It’s a tableview that displays a list fetched from networking.
OK. Let’s see his fetch implementation:
class MovieListViewViewModel {
...
init(endpoint: Driver<Endpoint>, movieService: MovieService) {
self.movieService = movieService
endpoint
.drive(onNext: { [weak self] (endpoint) in
self?.fetchMovies(endpoint: endpoint)
}).disposed(by: disposeBag)
}
func viewModelForMovie(at index: Int) -> MovieViewViewModel? {
guard index < _movies.value.count else {
return nil
}
return MovieViewViewModel(movie: _movies.value[index])
}
func fetchMovies(endpoint: Endpoint) {
self._movies.accept([])
self._isFetching.accept(true)
self._error.accept(nil)
movieService.fetchMovies(from: endpoint, params: nil,
successHandler: {[weak self] (response) in
self?._isFetching.accept(false)
self?._movies.accept(response.results)
}) { [weak self] (error) in
self?._isFetching.accept(false)
self?._error.accept(error.localizedDescription)
}
}
}
Let’s break down all of it into several main points:
- Coupling
View model is coupled with networking. Your view model is now a model with hidden state and side effects associated with networking. You may argue that this is the whole of refactoring with view model. But as you would see, there are far simpler ways.
2. Over-generalization
Endpoint in restful sense should be treated as a constant, rather than variable. Driver parameter is moronic, because you ever only need one. You are making things complicated than it needs to be. It’s extra moronic if the attempt is to prepare the view model to handle all kinds of endpoints and driver combination. You never need them.
3. Hidden state
I don’t see the name “Controller” in the term “view model”. If anything, it should be called “view state”, which is still ambiguous because it can refer to view properties or control states like networking. So either it becomes a proxy to access view properties, which is redundant glue code or it becomes a controller object disguised as a “model”. Either way it’s retarded.
4. RxSwift
I don’t even want to go into it. Let’s just say 99% of the time you don’t need it or you can find alternative. A JSON library is what you really need.
How do you refactor this mess with Apple MVC?
Remove view model, get rid of RxSwift. Use dedicated network service object. Wrap endpoint to be a resource type for single-source-of-truth. E.g.;
var json = JSON() { didSet { updateUI() }
var endpoint = Resource("URL")...endpoint.get().onSuccess {json in self.json = json}
You should be able to hand-craft JSON and Resource type from what it is supposed to do. I do it under 500 lines.
If you can’t, you really should not play around with RxSwift. Learn to walk before you run.
And in case you didn’t notice, we are done. The rest is to assign delegate and datasource for tableview, and implement respective protocols.
Configure tableview cell in usual SDK way. View model is retarded. Use customized cell if needed. I don’t know what drug MVVM devs are smoking, “easier to test” my ass, you know if you don’t have view model then there’s one fewer thing to test right? You can reuse custom cell too.
Readers are encouraged to construct a mental picture of what we’ve done so far and compare with that in the linked article above.
Now tell me,
Do you realize how much boilerplate / generalization you don’t need?
Do you realize you don’t need to observe shit and it’s far simpler?
And tell me,
Do you think you have massive view model problem?
I’m going to draw conclusion by modifying his conclusion.
Conclusion
Conclusion
Building an app with MVVM as an architecture is n̶o̶t̶ really that complex. In the long run, ViewModel really helps the View Controller become more l̶i̶g̶h̶t̶w̶e̶i̶g̶h̶t̶ massive and encapsulate our data transformation. T̶e̶s̶t̶i̶n̶g̶ ̶o̶n̶ ̶t̶h̶e̶ ̶V̶i̶e̶w̶ ̶M̶o̶d̶e̶l̶ ̶i̶s̶ ̶a̶l̶s̶o̶ ̶p̶r̶e̶t̶t̶y̶ ̶s̶i̶m̶p̶l̶e̶ ̶a̶n̶d̶ ̶s̶t̶r̶a̶i̶g̶h̶t̶f̶o̶r̶w̶a̶r̶d̶ ̶w̶i̶t̶h̶o̶u̶t̶ ̶w̶o̶r̶r̶y̶i̶n̶g̶ ̶a̶b̶o̶u̶t̶ ̶t̶h̶e̶ ̶V̶i̶e̶w̶ ̶C̶o̶n̶t̶r̶o̶l̶l̶e̶r̶.̶ If you can test the massive view model coupled with networking and Rx-observer, you can test view controller too.
Although RxSwift can become quite complex and has steep learning curve for developers that has just exposed to reactive programming, it is also has powerful feature like throttle and many more to explore which you don’t need most of the time. Next time, let’s explore about how to perform unit testing on V̶i̶e̶w̶ ̶M̶o̶d̶e̶l̶ ̶w̶i̶t̶h̶ ̶R̶x̶S̶w̶i̶f̶t̶’̶s̶ ̶R̶x̶T̶e̶s̶t̶ ̶&̶ ̶R̶x̶B̶l̶o̶c̶k̶i̶n̶g̶g view controller using POP. So until then, let’s keep the lifelong learning, keep growing and building. Happy Swifting 😋.