I have recently run into a few problems with our MVVM architecture, which we’ve been using for the past 2 years. They are known weak points of MVVM, namely the navigation and global app state. Despite these and other shortcomings, MVVM has been growing in popularity, but other competing architectures have also been gaining ground (mostly because everyone is running away from MVC). Just like two years ago, I felt like it was time to look around and assess the state of iOS app architectures and the best practices of 2016.
Most of the materials I’ve used are linked in this awesome repo.
In 2016 MVC is dead, and it wasn’t killed by someone cleverly calling it the “Massive View Controller” either. There has been a lot of effort invested into alleviating some of the shortcomings of MVC, mostly by dividing the ViewController into more controller layer objects. While these help code manageability a readability, they don’t address the underlying problem – the controller layer being too big and having too many responsibilities.
Apple itself isn’t ignorant of this. They have been pushing hard for the adoption of many modern trends. Swift value types are great for immutability, we’ve been seeing hints of reactiveness with NSOperations and KVO for some time, and Swift protocol extensions open up a whole new world of possibilities. Apple just isn’t in a position to completely ditch MVC, so they still rely mostly on managing the mutable state imperatively in the controller layer. Having some of the best software engineers in the world, perhaps they can afford to do this too. The AAPLLoadableContentStateMachine from this WWDC2014 talk is impressive, but I wouldn’t want to write that code, … and you shouldn’t either. That is, unless the situation requires it.
The biggest advantage of MVC in my view has always been the freedom. Define the thin model and view layers, then go wild in the view controller. There is no problem you can’t solve using MVC, you just can’t solve most of them very elegantly, quickly, and without bugs. When choosing among the more constrained app architectures, one of the criteria by which to judge them should be: If we reach a dead end, can we easily run back to MVC and solve the one complicated case there?
During the two years we’ve been using it, MVVM has done wonders for our iOS applications. Of course, it’s not the architecture alone, but also the adoption of reactive technologies (ReactiveSwift) that play so well with MVVM. When you really get to know an architecture, you start to better understand its shortcomings. We’ve had to deal with the problems of ViewModel navigation and we’ve produced ViewModel’s on both ends of the “vaguely defined” responsibility spectrum that this article talks about. It might also be true that MVVM doesn’t do enough to properly fix MVC and is “a band-aid over a problem that will continue to come up.” It is a significant improvement over MVC, but can we do better? Is VIPER the way to go, because it has five letters instead of four? In ten years, will we use the ABCDEFGHIJKLMNOPQRSTUVWXYZ architecture? Read on to find out why I think that the set of best practices that comprises the MVVM architecture is still relevant today for most of our projects and what new trends we can adopt to make it better.
MVP isn’t as popular on iOS as it is on other platforms. Our android team uses it, and as far as I know, they are satisfied with it. Some time ago, they added the Interactor object to their MVP modules, instantly making it more appealing to iOS developers. Afterall, who wouldn’t want to write their apps using iMVP :). I have never used MVP, but from my limited knowledge about it, it seems like an incomplete step towards VIPER. I will leave it to others to prove me wrong on this.
VIPER (i.e. Clean iOS Architecture)
View-Interactor-Presenter-Entity-Router, that’s a mouthful, but remembering five words isn’t as bad as creating 5 blank files anytime you want to add a VIPER module. Sure, you can use code generators like Generamba, but is it really desirable to have an app architecture this complicated? Furthermore, with all the modern features of Swift, is it desirable to use the Java-like approach to solving any problem, i.e. “Let me just spawn a couple of classes and see where we go from there”? The answer is: ‘It depends’. This article does a great job of summarizing the pros and cons. One point in particular makes VIPER very unattractive for us: “VIPER architecture should be used for applications whose requirements are very well defined at the beginning.” This goes directly against the flexibility of agile development which we try to offer to our customers.
There are definitely strong reasons to choose VIPER for some projects, but we shouldn’t jump on the VIPER bandwagon unless it makes sense in a particular situation.
Unidirectional Data Flow, FLUX
Lately, everyone has been going crazy about React, and for good reasons. There’s nothing more rewarding than writing code that works on the first compile. Statelessness and unidirectional data flow are trends that cannot be ignored, and we’ve been trying to utilize them wherever possible. Does that mean that we should switch to Facebook’s FLUX architecture for all of our project’s tomorrow? I don’t think so, and Facebook doesn’t seem to think so either. There are serious tradeoffs that come with using FLUX and all the pros and cons are very well summarized in this talk. I would add that we’ve always been skeptical of diverting too much from the “native”, “Apple” way of doing things. Sure, we use all the modern tools like ReactiveSwift, but we still make native apps using native API’s. Switching to one of the FLUX inspired tools is in principle the same as going full ReactNative, although not as extreme. We just don’t think it makes sense for us, at least not yet. That said, the transition to stateless programming is well underway and I’m already looking forward to solving some of the complicated edge cases in MVVM by adopting more of the ideas of unidirectional data flow.
Two years after we started using it, we haven’t found a compelling argument to ditch MVVM. We will continue to look for ways to improve our code, but we are not really looking for a silver-bullet, we realize that there are none. What we want is a set of best practices, rather than a fixed one-size-fits-all app architecture. This article does a great job of explaining how MVVM does just that. Unlike VIPER or FLUX, its vague definition allows it to be used on most projects and to be easily combined with other programming paradigms and modern trends. Constraining the programmer’s hands is definitely a reasonable approach towards making their job easier, but we believe that these constraints should come in the form of recommendations, not be enforced by a strict framework of rules. That’s why we use MVVM.