iOS is a mobile operating system developed and distributed by Apple Inc. It was originally released in 2007 for the iPhone, iPod Touch, and Apple TV. iOS is derived from OS X, with which it shares the Darwin foundation. iOS is Apple’s mobile version of the OS X operating system used in Apple computers.
iOS architecture
- The iOS architecture is layered. It contains an intermediate layer between the applications and the hardware so they do not communicate directly. The lower layers in iOS provide the basic services and the higher layers provide the user interface and sophisticated graphics.
- The layered architecture of iOS is given as follows −
Layers in iOS Architecture
- The different layers as shown in the above diagram are given as follows −
Core OS
- All the iOS technologies are built on the low level features provided by the Core OS layer. These technologies include Core Bluetooth Framework, External Accessory Framework, Accelerate Framework, Security Services Framework, Local Authorization Framework etc.
Core Services
There are many frameworks available in the cure services layer. Details about some of these are given as follows −
- Cloudkit Framework:The data can be moved between the app iCloud using the Cloudkit Framework.
- Core Foundation Framework:This provides the data management and service features for the iOS apps.
- Core Data Framework:The data model of the model view controller app is handled using the Core Data Framework.
- Address Book Framework:The address book framework provides access to the contacts database of the user.
- Core Motion Framework:All the motion based data on the device is accessed using the core motion framework.
- Health kit Framework:The health related information of the user can be handled by this new framework.
- Core Location Framework:This framework provides the location and heading information to the various apps.
Media
The media layer enables all the graphics, audio and video technology of the system. The different frameworks are:
- UIKit Graphics: This provides support for designing images and animating the view content.
- Core Graphics Framework: This provides support for 2-D vector and image based rendering and is the native drawing engine for iOS apps.
- Core Animation: The Core Animation technology optimizes the animation experience of the apps.
- Media Player Framework: This framework provides support for playing playlists and enables the user to use their iTunes library.
- AV Kit: This provides various easy to use interfaces for video presentation.
- Cocoa Touch: The cocoa touch layer provides the following frameworks −
- EventKit Framework: This shows the standard system interfaces using view controllers for viewing and changing calendar related events.
- GameKit Framework: This provides support for users to share their game related data online using Game center.
- MapKit Framework: This provides a scrollable map which can be included into the app user interface.
Features
- Balanced distribution of responsibilities among entities with strict roles.
- Testability usually comes from the first feature (and don’t worry: it is easy with appropriate architecture).
- Ease of use and a low maintenance cost.
MV(X) essentials
- MVC
- MVP
- MVVM
- VIPER
First three of them assume putting the entities of the app into one of 3 categories:
- Models — responsible for the domain data or a data access layer which manipulates the data, think of ‘Person’ or ‘PersonDataProvider’ classes.
- Views — responsible for the presentation layer (GUI), for iOS environment think of everything starting with ‘UI’ prefix.
- Controller/Presenter/ViewModel — the glue or the mediator between the Model and the View, in general responsible for altering the Model by reacting to the user’s actions performed on the View and updating the View with changes from the Model.
Having entities divided allows us to:
- understand them better (as we already know)
- reuse them (mostly applicable to the View and the Model)
- test them independently
MVC
- Before discussing Apple’s vision of MVC let’s have a look on the traditional one.
- In this case, the View is stateless. It is simply rendered by the Controller once the Model is changed. Think of the web page completely reloaded once you press on the link to navigate somewhere else. Although it is possible to implement the traditional MVC in iOS application, it doesn’t make much sense due to the architectural problem — all three entities are tightly coupled, each entity knows about the other two. This dramatically reduces reusability of each of them — that is not what you want to have in your application.
Apple’s MVC
- Expectation
- The Controller is a mediator between the View and the Model so that they don’t know about each other. The least reusable is the Controller and this is usually fine for us, since we must have a place for all that tricky business logic that doesn’t fit into the Model.
- Reality
- Cocoa MVC encourages you to write Massive View Controllers, because they are so involved in View’s life cycle that it’s hard to say they are separate. Although you still have ability to offload some of the business logic and data transformation to the Model, you don’t have much choice when it comes to offloading work to the View, at most of times all the responsibility of the View is to send actions to the Controller. The view controller ends up being a delegate and a data source of everything, and is usually responsible for dispatching and cancelling the network requests and… you name it.
- The cell, which is the View configured directly with the Model, so MVC guidelines are violated, but this happens all the time, and usually people don’t feel it is wrong. If you strictly follow the MVC, then you are supposed to configure the cell from the controller, and don’t pass the Model into the View, and this will increase the size of your Controller even more.
- The problem might not be evident until it comes to the Unit Testing (hopefully, it does in your project). Since your view controller is tightly coupled with the view, it becomes difficult to test because you have to be very creative in mocking views and their life cycle, while writing the view controller’s code in such a way, that your business logic is separated as much as possible from the view layout code.
- struct Person { // Model
- let firstName: String
- let lastName: String
- }
- class GreetingViewController : UIViewController { // View + Controller
- var person: Person!
- let showGreetingButton = UIButton()
- let greetingLabel = UILabel()
- override func viewDidLoad() {
- super.viewDidLoad()
- self.showGreetingButton.addTarget(self, action: “didTapButton:”, forControlEvents: .TouchUpInside)
- }
- func didTapButton(button: UIButton) {
- let greeting = “Hello” + ” ” + self.person.firstName + ” ” + self.person.lastName
- self.greetingLabel.text = greeting
- }
- // layout code goes here
- }
- // Assembling of MVC
- let model = Person(firstName: “David”, lastName: “Blaine”)
- let view = GreetingViewController()
- view.person = model;
MVP
- In terms of the MVP, the UIViewController subclasses are in fact the Views and not the Presenters. This distinction provides superb testability, which comes at cost of the development speed, because you have to make manual data and event binding, as you can see from the example:
- import UIKit
- struct Person { // Model
- let firstName: String
- let lastName: String
- }
- protocol GreetingView: class {
- func setGreeting(greeting: String)
- }
- protocol GreetingViewPresenter {
- init(view: GreetingView, person: Person)
- func showGreeting()
- }
- class GreetingPresenter : GreetingViewPresenter {
- unowned let view: GreetingView
- let person: Person
- required init(view: GreetingView, person: Person) {
- self.view = view
- self.person = person
- }
- func showGreeting() {
- let greeting = “Hello” + ” ” + self.person.firstName + ” ” + self.person.lastName
- self.view.setGreeting(greeting)
- }
- }
- class GreetingViewController : UIViewController, GreetingView {
- var presenter: GreetingViewPresenter!
- let showGreetingButton = UIButton()
- let greetingLabel = UILabel()
- override func viewDidLoad() {
- super.viewDidLoad()
- self.showGreetingButton.addTarget(self, action: “didTapButton:”, forControlEvents: .TouchUpInside)
- }
- func didTapButton(button: UIButton) {
- self.presenter.showGreeting()
- }
- func setGreeting(greeting: String) {
- self.greetingLabel.text = greeting
- }
- // layout code goes here
- }
- // Assembling of MVP
- let model = Person(firstName: “David”, lastName: “Blaine”)
- let view = GreetingViewController()
- let presenter = GreetingPresenter(view: view, person: model)
- view.presenter = presenter
Features of the MVP:
- Distribution — we have the most of responsibilities divided between the Presenter and the Model, with the pretty dumb View (in the example above the Model is dumb as well).
- Testability — is excellent, we can test most of the business logic due to the dumb View.
- Easy to use — in our unrealistically simple example, the amount of code is doubled compared to the MVC, but at the same time, idea of the MVP is very clear.
MVP
- With Bindings and Hooters
- There is the other flavor of the MVP — the Supervising Controller MVP. This variant includes direct binding of the View and the Model while the Presenter (The Supervising Controller) still handles actions from the View and is capable of changing the View.
- Same as with the traditional MVC, I don’t see a point in writing an example for the flawed architecture.
MVVM
- The latest and the greatest of the MV(X) kind
- The MVVM is the newest of MV(X) kind thus, let’s hope it emerged taking into account problems MV(X) was facing previously.
- It is pretty similar to the MVP:
- the MVVM treats the view controller as the View
- There is no tight coupling between the View and the Model
- In addition, it does binding like the Supervising version of the MVP; however, this time not between the View and the Model, but between the View and the View Model.
Bindings
I briefly mentioned them in the MVP part, but let’s discuss them a bit here. Bindings come out of box for the OS X development, but we don’t have them in the iOS toolbox. Of course we have the KVO and notifications, but they aren’t as convenient as bindings.
we have two options:
- One of the KVO based binding libraries like the RZDataBinding or the SwiftBond
- The full scale functional reactive programming beasts like ReactiveCocoa, RxSwift or PromiseKit.
- There is one bitter truth about reactive frameworks: the great power comes with the great responsibility. It’s really easy to mess up things when you go reactive. In other words, if you do something wrong, you might spend a lot of time debugging the app, so just take a look at this call stack.
Feature assessment:
- Distribution — it is not clear in our tiny example, but, in fact, the MVVM’s View has more responsibilities than the MVP’s View. Because the first one updates its state from the View Model by setting up bindings, when the second one just forwards all events to the Presenter and doesn’t update itself.
- Testability — the View Model knows nothing about the View, this allows us to test it easily. The View might be also tested, but since it is UIKit dependent you might want to skip it.
- Easy to use — it has the same amount of code as the MVP in our example, but in the real app where you’d have to forward all events from the View to the Presenter and to update the View manually, MVVM would be much skinnier if you used bindings.
VIPER
LEGO building experience transferred into the iOS app design
- VIPER is our last candidate, which is particularly interesting because it doesn’t come from the MV(X) category.
- By now, you must agree that the granularity in responsibilities is very good. VIPER makes another iteration on the idea of separating responsibilities, and this time we have five layers.
- Interactor — contains business logic related to the data (Entities) or networking, like creating new instances of entities or fetching them from the server. For those purposes you’ll use some Services and Managers which are not considered as a part of VIPER module but rather an external dependency.
- Presenter — contains the UI related (but UIKit independent) business logic, invokes methods on the Interactor.
- Entities — your plain data objects, not the data access layer, because that is a responsibility of the Interactor.
- Router — responsible for the segues between the VIPER modules.
Differences of the distribution of responsibilities:
- Model (data interaction) logic shifted into the Interactor with the Entities as dumb data structures.
- Only the UI representation duties of the Controller/Presenter/ViewModel moved into the Presenter, but not the data altering capabilities.
- VIPER is the first pattern which explicitly addresses navigation responsibility, which is supposed to be resolved by the Router.
Features:
- Distribution — undoubtedly, VIPER is a champion in distribution of responsibilities.
- Testability —no surprises here, better distribution — better testability.
- Easy of use — finally, two above come in cost of maintainability as you already guessed. You have to write a huge amount of interface for classes with very small responsibilities.
Conclusion
- We went through several architectural patterns, and I hope you have found some answers to what bothered you, but I have no doubt that you realized that there is no silver bullet so choosing architecture patterns is a matter of weighting trade offs in your particular situation.
- Therefore, it is natural to have a mix of architectures in the same app. For example: you’ve started with MVC, then you realized that one particular screen became too hard to maintain efficiently with the MVC and switched to the MVVM, but only for this particular screen. There is no need to refactor other screens for which the MVC actually does work fine, because both of architectures are easily compatible.