The Best Architecture for The iOS App, Does it even exist? , suggested reference to the original reading, can also see here
A while back, I stumbled upon an article on iOS architectural patterns with the provocative title: “The Only iOS Architecture That Works.” The answer to the question in the title is actually MVC. In short, MVC is the only viable and best architecture for iOS applications.
The main idea of this article is that people just understand MVC the wrong way. The ViewController is actually part of the presentation layer, and the Model part represents the entire Domain Model, not just some data entities. In general, I agree with the idea of that post, but if I agreed with every statement of that post, I wouldn’t be writing this article, would I?
I noticed that the authors largely left out one very important aspect of well-formed application architecture: the use of unit tests (UT) to override application business logic (BL). To me, this is one of the most important elements of smart application architecture. If you can’t extract the application BL and implement UT with sufficient coverage, the architecture sucks.
In addition, if an application does not have UT, then it is not feasible to prove the above point, so its architecture is most likely to be problematic. You can promise yourself that UT can be easily implemented during a short break from your intense work, or simply because you have dedicated “Tests” goals in your XCode project along with some template UT. Trust me, but it’s a fantasy. I firmly believe that unit tests will not be implemented if they are not implemented with functionality or soon after delivery.
With this statement as an axiom, the application architecture must provide the capability to separate BL and UI representations and make them testable. Obviously, the UIViewController subclass is not the best candidate for this role due to its lifecycle dependence. This means that the class responsible for BL must be located between UIViewController and Services, or in other words, between View and Domain Model.
It’s worth noting that “Services” refers to the logic responsible for networking and communicating with databases, sensors, Bluetooth, keychains, third-party Services, etc. In other words, a shared part of multiple locations, pages, within an application. The business logic part of the diagram corresponds only to a page or a page component represented by a view controller. In the MVC article mentioned at the beginning, the author combines the BL and Domain Model parts, while accepting that THE UIViewController is part of the presentation logic (that is, the view).
Now, when you’ve identified the need to separate presentation from business logic, let’s consider how the two parts talk to each other. This is where the famous architectural patterns emerge.
MVP
In MVP mode, Presenter and View are linked to each other by protocol. Presenter is injected with an instance of the View protocol, and vice versa. A View protocol must have sufficient interfaces to render raw data in the UI, and a Presenter protocol must have interfaces to transmit events (such as touches, gestures, shakes, etc.) received from the user or system. UIViewController subclasses represent the View part here, whereas Presenter classes cannot rely on UIKit (for example, sometimes you need to import UIKit to operate on data classes like UIImage).
On iOS, because of the way the UIViewController lifecycle works, it must have a strong reference to the Presenter instance, and the last one must be a weak reference to avoid circular references. This configuration is reminiscent of the delegate pattern. In most cases, UIViewController may have a direct typed reference to Presenter, but in some cases the last role can also be injected into the first by protocol. This can be useful if the presentation is used for different business logic. Presenter links to UIViewController must be protocol emulated and overridden with UT. I won’t go into too much detail in the Service section, but in order to test Presenter, it must also be injected along with the protocol.
For more details on MVP and basic examples, see here 1.
MVVM
In the MVVM pattern, the presentation and business parts communicate with each other using reactive bindings called the View and ViewModel, respectively. In iOS, reactive binding is typically done using ReactiveCocoa, RxSwift, or the modern Combine framework, which is typically in the ViewModel class and is also used by the ViewController via protocol. MVPS are not much different in the part where they communicate with Services or Domain Models, but people might prefer to use binding or reactive events here. As with the previous pattern, dependencies must be injected into the protocol in order to emulate them in UT.
You can find more details about MVVM and basic examples here 2.
MVVM+Router
The independent topic here is routing. In iOS, displaying a new screen modally or pushing to the navigation stack is done via a UIViewController subclass. However, these operations may be part of the BL and may be overridden by UT, such that the screen must be turned off if a specific event occurs. In this case, it makes sense to divide this part of the application logic into a class called Router. Therefore, the pattern changes to MVP+R or MVVM+R. In some sources, you might find this section named Coordinator and MVVP+C or MVVM+C, respectively. Although the coordinator may have some logic other than routing, I prefer to conceptually equate them. The link between the ViewModel and the Router must be through protocol, and the last one must only be responsible for screen operations, and all BLS must still be centralized in the first one. Therefore, Router is not the subject of UT.
A sample project with an implementation of the MVVM+R architectural pattern can be found on my GitHub3.
other
VIPER iOS Architecture pattern is an extension of MVVM+R, where the ViewModel is divided into two parts: Interactor and Presenter. The first is responsible for communicating with entities, the domain model. The second part prepares the model classes to be rendered in the view. To be honest, I’ve never used this model because it seems too fragmented and complex to me. MVVM+R separation of concerns is always sufficient for me.
In MVVM+R, each module (screen) must display at least three classes: ViewController, ViewModel, and Router. And there must be a place to instantiate all these parts and link them to each other, the key to module construction. The best place to do this is the Router, because it is not coupled to the iOS UIViewController lifecycle and must know how to display the page to shut it down properly. However, in some cases it is more convenient to move this part into a separate class called Builder, which is what happens in RIB (Uber’s architectural pattern). The ViewModel is renamed Interactor and the rest remains the same. This model has some of the more interesting ideas and techniques introduced by Uber, which you can read on RIB Wiki. However, the most useful thing I’ve found in the RIBs code library is the XCode template, which helps avoid boilerplate coding when introducing new Riblets into a project. These templates can also be easily used with the MVVM+R class. For iOS engineers at Uber 👏.
Finally, a brief talk about the one-way data flow architecture on iOS. If you look at the scheme for the above pattern, they all have bidirectional connections between components. This is not the case in Redux. This architectural pattern initially migrated from the Web to mobile applications, particularly the React framework. This concept is one of the most popular implementations of iOS development in the ReSwift framework. I won’t go into detail because this architecture has not been used in production applications. However, it is clear that people moving into iOS from Web development find this architectural pattern the most intuitive and familiar.
conclusion
What is the best application architecture is always a hot topic, so for now I prefer the ideas John Sandel put forward in his recent talk:
The best architecture is one that you and your team create together and that fits your project by combining standard patterns and techniques with system design.
reference
[1]https://medium.com/@saad.eloulladi/ios-swift-mvp-architecture-pattern-a2b0c2d310a3
[2]https://medium.com/flawless-app-stories/practical-mvvm-rxswift-a330db6aa693
[3]https://github.com/OlexandrStepanov/MVVM-RouterDemo