0% found this document useful (0 votes)
60 views17 pages

iOS App Architecture Patterns Overview

Uploaded by

champsdload
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
60 views17 pages

iOS App Architecture Patterns Overview

Uploaded by

champsdload
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

A class should have one reason, and one reason only, to change.

That is, a class should only have one responsibility.

Open–Closed Principle (OCP)

We must be able to extend a class without changing its behavior. This is achieved by abstraction.

Liskov Substitution Principle (LSP)

In a program, any class should be able to be replaced by one of its subclasses without affecting its operation.

Interface Segregation Principle (ISP)

It is better to have different interfaces (protocols) that are specific to each client than to have a general interface.
Also, a client would not have to implement methods that it does not use.

Dependency Inversion Principle (DIP)

High-level classes should not depend on low-level classes. Both should depend on abstractions. Abstractions
should not depend on details. The details should depend on the abstractions. What is sought is to reduce depen-
dencies between modules and thus achieve less coupling between classes.

How to Choose the Right Architectural Pattern

We have just seen the advantages that our applications have a good architecture offers us. But how do we choose
the right architecture pattern for our project?

In the first place, we have to know some information about our project and the technology that we are going to use
since we have seen that some architecture patterns are better adapted to some projects and other patterns to oth-
ers.

Therefore, we must take into account, for example:

• The type of project


• The technologies used to develop it
• Support infrastructure (servers, clouds, databases…)
• User interface (usability, content, navigation…)
• Budget and development time
• Future scalability and the addition of new functionalities

If we take into account everything seen so far, the choice of a good architecture pattern (along with the use of
design patterns and SOLID principles) will allow us to have the following:

• A scalable application: A good architecture pattern should allow us to add new features and even change
some of the technologies used, without having to modify the entire application.
• Separation of interests: Each component should be independent of the code point of view. That is, to func-
tion correctly, a component should only be aware of those around it and nothing else. This will allow us, for ex-
ample, to reuse these components or simply change them for others.
• A code easy to maintain: Well-written, structured code without repetition makes it easier to understand, re-
view, or modify. Also, any new developer joining the project will require less time to get hold of.
• A testable code: The previous points result in the fact that it is easier to test a code if the functionalities are
correctly separated than if they are not.
• A solid, stable, reliable, and durable code over time.

Most Used Architecture Patterns

From a generic point of view of software development, there are numerous architecture patterns, but we will focus
From a generic point of view of software development, there are numerous architecture patterns, but we will focus
on the most used for the development of iOS applications:

• Model–View–Controller (MVC)
• Model–View–Presenter (MVP)
• Model–View–ViewModel (MVVM)
• View–Interactor–Presenter–Entity–Router (VIPER)
• View–Interactor–Presenter (VIP)

We will start with the best-known model and the one that every developer usually starts working with, the MVC.
From here we will work on models that derive from it, such as the MVP and the MVVM, to end up with much more
elaborate models of higher complexity, such as the VIPER and the VIP.

After these architectural patterns, we will see, in a more summarized way, some more patterns, perhaps not so
used or known, but that can give us a better perspective of how to structure an application. Examples of these
types of patterns are RIBs (developed by Uber) and Redux (based on an initial idea of Facebook for a one-way archi-
tecture).

In Search of a “Clean Architecture”

The “Clean Architecture” concept was introduced by Robert C. Martin in 2012,2 and it is not an architecture, but a
series of rules that, together with the SOLID principles, will allow us to develop software with responsibilities sepa-
rate, robust, easy to understand, and testable.

Clean Architecture Layers

According to this philosophy, for an architecture to be considered “clean” it must have at least the following three
layers: Domain Layer, Presentation Layer, and Data Layer (Figure 1-2).
Figure 1-2 Scheme of the layer structure in Clean Architecture. Dependency rule arrows show how the outermost layers depend on the innermost ones and not the

other way around

Domain Layer

It is the core of this architecture and contains the application logic and business logic. In this layer we find the Use
Cases or Interactors, the Entities, and the Interfaces of the Repositories:

• Use Cases or interactors: They are in charge of defining and implementing the business logic. They control the
flow of information to and from the Entities. They can work with one or more entities and access their methods.
• Entities: These are simple objects (which can be simple data structures or can also contain methods) that con-
tain the business rules.
• Repository interfaces: They contain the definition of the methods that will be implemented in the Repositories.
Repositories are responsible for obtaining and passing data from databases, servers, etc.

This layer has no external dependencies, so it is easily testable (Use Cases) and can be reused in other projects.

Presentation Layer

This layer contains all those elements that show information to the user or that receive their interaction.

The Presentation layer also includes those components, such as ViewModels or Presenters, that help prepare the
data to be displayed on the screen.

The ViewModels or Presenters are also in charge of executing the Use Cases. The Presentation Layer only depends
on the Domain Layer.

Data Layer

It contains the implementation classes of the Repositories and the data sources such as databases, user prefer-
ences, or access to servers. In the same way as the Presentation Layer, the Data Layer only depends on the Domain
Layer.

The Dependency Rule

For this type of architecture to work correctly, we must apply the so-called Dependency Rule. According to this
rule, the inner layers must not know the outer layers (i.e., no variable, method, etc., of an outer layer is mentioned
in a more inner layer).

Advantages of Applying a Clean Architecture

The application of a Clean Architecture in our projects will give us a series of advantages (some of which we have
already seen in the introduction to Software Architecture):

• Testable: The fact that the business logic is isolated in its layer, and that it does not depend on the rest of the
layers, makes it easily testable.

In addition, this same separation by layers allows them to be tested separately and more easily locate any
possible error.

• Independent of frameworks: The code must be independent of specific libraries.


In other words, we can change one library for another without the need for major changes in the code and
without the internal layers stopping working because of it. This is achieved by preventing our code from having
direct dependencies on these libraries.

• Independent of the user interface: The user interface is the outermost layer and only displays the data sup-
plied by the presenter.

Therefore, we must be able to modify it without affecting the most internal part, the business logic. In other
words, the user interface must adapt to changes in business logic and not the other way around.

• Independent of data sources: In a similar way to what was explained for the independence of the user inter-
face, we must be able to change the data sources (local, external databases…) without affecting the business
logic, since it is these sources that fit the business logic.
• Independent of external elements: The business logic must be independent of everything that surrounds it,
which must allow us to change any part of the rest of the system without affecting it.

MyToDos: A Simple App to Test Architectures

To work with the different architectures that we have mentioned before (MVC, MVP, MVVM, VIPER, and VIP), we
are going to create a simple task management application, which is, usually, one of the first applications that a de-
veloper usually does.

App Screens

Our MyToDos app will allow us to work the navigation between different screens (create lists, create tasks…); use a
database to save, update, or delete tasks; and, finally, manage user interactions on the different screens.

Launch Screen

It is the screen that appears when loading the application (Figure 1-3).
Figure 1-3 Launch screen

Home Screen

This screen shows the lists we have created. If we haven’t created any list, an “Empty State” will appear to tell us to
create our first list.

For each created list, we will be able to see the icon that we have associated with the list, the title of the list,
and the number of tasks that compose it (Figure 1-4).
Figure 1-4 Empty state on Home screen

The user can interact on this screen at three points:

• Using the Add List button, which will allow us to create a new list
• Selecting one of the lists to access its content (Figure 1-5)
• Deleting lists by a swipe gesture on the list (Figure 1-6)

Figure 1-5 Access tasks list on select cell


Figure 1-6 Delete list on swipe cell

Add List Screen

This screen is navigated through the Add List button on the Home screen. Here, the user has to indicate the title
that will be given to the list and select an icon from those that are shown (Figure 1-7).
Figure 1-7 Add List screen

When you select the Add List button, the entered information is saved in the database (Core Data) and you return to
Home.

If the user wants to return to Home without creating any list, they simply have to select the button with the return
arrow located at the top left of the screen.

Tasks List Screen

This screen is navigated by selecting one of the lists that appear in the Home. If we do not have any task created,
an “Empty State” will appear indicating how to create a task (Figure 1-8).
Figure 1-8 Tasks List empty state

The user can interact on this screen at three points:

• Using the Add Task button, which will allow us to create a new task (Figure 1-9).
Figure 1-9 Tasks List screen with an added task

• Selecting the circle to the right of each task will allow us to mark it as done (Figure 1-10).
• Deleting tasks using a swipe gesture in the list (Figure 1-11).

Figure 1-10 Task checked as done


Figure 1-11 Delete task

Any modification on this screen (change the status of a task or delete it) is automatically saved in the database.

If the user wants to return to Home without creating any task, they simply have to select the button with the return
arrow located at the top left of the screen.

Add Task Screen

This screen is navigated through the Add Task button on the Tasks List screen and is displayed as a modal. Here, the
user has to indicate the title that will be given to the task and select an icon from those that are displayed.

When you select the Add Task button, the entered information is saved in the database (Core Data) and you are re-
turned to the Tasks List screen.

If the user wants to return to the Tasks List screen without creating any tasks, they simply have to drag the
screen down (Figure 1-12).
Figure 1-12 Add Task screen

App Development

Before we start working with the different architectures in the next chapters, let’s first see how to prepare the ap-
plication for it.

Technologies Used

For the development of this application with each of the architectures, Xcode 13.3 and Swift 5.6 have been used.

The database used is Apple’s own Core Data, and the views and navigation between them have been developed di-
rectly with code, without using .xib or .storyboard files.

How to Remove Storyboard Dependence

To develop the application (with the different architectures) without using a storyboard, we have to do the
following:

First of all, we open Xcode and select the Create a new Xcode project option (Figure 1-13).
• First of all, we open Xcode and select the Create a new Xcode project option (Figure 1-13).

Figure 1-13 Welcome to Xcode screen

• Next, we select the App template in iOS (Figure 1-14).

Figure 1-14 App template selection screen

• The next step is to indicate the name of the application (Product Name), select the development team and
the organization identifier, and choose Storyboard as the interface, Swift as the language, and Include Tests
(Figure 1-15).
Figure 1-15 Choose project options screen

• After creating the project, its configuration screen will appear. On this screen we must remove the “Main”
option in the Deployment Info ➤ Main Interface section (Figure 1-16).

Figure 1-16 Project configuration screen

• Then we delete the [Link] file (Figure 1-17).


Figure 1-17 Remove [Link]

• Finally, we access the [Link] file, display its content, and remove the Storyboard Name line: Information
Property List ➤ Application Scene Manifest ➤ Scene Configuration ➤ Application Session Role ➤ Item 0 ➤
Storyboard Name (Figure 1-18).

Figure 1-18 Remove storyboard reference from [Link]

• When working with a storyboard, the window property is automatically initialized and the root view con-
troller is set as the initial view controller in the storyboard. When removing the storyboard, we will have to do it
ourselves.

This is done in the [Link] file, modifying the content of the function

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: [Link]) {
guard let _ = (scene as? UIWindowScene) else {
return
}
}
}

by this code (this part of the code can be modified depending on the needs of our application):

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: [Link]) {
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
[Link] = ViewController()
[Link]()
[Link] = window
}
}

Core Data Configuration

In this application we will use Apple’s database, Core Data, but we have not selected it when setting up the project
so that we can use our own database management class.

How to Create Database and Entities

To do this we will start by creating the database model and its entities:

• In the main menu of Xcode, we select File ➤ New ➤ File…. In the templates menu that appears, descend to the
Core Data section and select Data Model (Figure 1-19).
• Next, we give the file a name (in this case ToDoList) and create it (Figure 1-20).

Figure 1-19 Select Data Model file template


Figure 1-20 Create a Data Model file

• Now we are going to create the entities that we will use in the application, TasksList, and Task. To do this, we
select the [Link] file and add the two entities mentioned with the properties shown in Figure
1-21 and Figure 1-22.

Figure 1-21 Creation of the TasksList entity

Common questions

Powered by AI

Developing a task management application like 'MyToDos' offers a practical opportunity to apply and compare different architecture patterns, such as MVC, MVP, and MVVM, in a real-world context . Each pattern can be used to design different parts of the app, allowing developers to see firsthand how these architectures manage aspects like data handling, UI interaction, and separation of concerns . The educational benefits of this approach include gaining an understanding of the strengths and weaknesses of each pattern, improving problem-solving skills through direct application, and experiencing the challenges of shifting between architectures when project requirements evolve . Additionally, integrating various patterns provides insight into structuring scalable, maintainable apps and appreciating how theoretical knowledge translates into functional software systems, preparing developers for diverse project demands.

The Domain Layer in Clean Architecture acts as the core of an application, containing business logic and application rules. It includes use cases or interactors, entities, and repository interfaces, all of which define and implement business rules, control data flow to and from entities, and ensure independence from external data sources . This modular organization allows the Domain Layer to be highly testable because the business logic is isolated and does not depend on other layers, making it easier to test individually and identify defects . Additionally, it can be reused across different projects due to its lack of external dependencies and focus solely on business logic, which supports the principle of writing adaptable and clear code . This separation ensures that logic can be maintained independently from changes in UI or data sources, thereby enhancing long-term maintainability and reusability.

The Dependency Inversion Principle in software development states that high-level modules should not depend on low-level modules, but both should depend on abstractions. This principle reduces coupling by limiting dependencies between concrete components and encouraging reliance on abstract interfaces or classes instead . Consequently, changes to low-level components do not affect the high-level modules as long as the abstraction remains consistent, allowing components to evolve independently . In complex applications, this reduces the risk of cascading changes and simplifies the integration of new features or technologies, promoting flexibility and scalability. By decoupling the system's architecture, developers can replace or upgrade parts of the system with minimal impact, facilitating maintenance and promoting a robust, adaptable code base.

Selecting an appropriate architectural pattern requires understanding a project's specific context and constraints, such as the type of project, technologies to be used, and infrastructure constraints like servers and databases . Usability, content navigation, budget, development time, scalability, and the potential for adding new functionalities are other critical factors . This choice is crucial for project success because a well-suited architecture ensures scalability, maintainability, and ease of testability . A fitting architecture pattern separates interests, enabling components to function independently, which enhances modularity and reusability . Furthermore, this decision can dictate the efficiency of development processes and the maintainability of the end product—ensuring stability and adaptability with future changes . Hence, taking these factors into account prevents future refactoring or redesign efforts and optimizes resource use from the onset.

'Clean Architecture' offers the benefit of framework independence by ensuring that business logic is isolated from framework-specific dependencies, allowing the core functions to operate independently of the user interface, data sources, or external libraries . This independence means that frameworks and libraries can be replaced or updated without necessitating changes in the core logic, thus reducing the risk of breaking the application when modifications are needed . This architecture promotes long-term software maintenance by making systems easier to update, test, and extend. The loosely coupled structure also aids in sustaining a stable architecture as frameworks evolve, allowing developers to focus on improving and scaling the core application logic without the overhead of continuous adjustments for compatibility.

User interface independence in 'Clean Architecture' means that the UI is the outermost layer and does not affect the business logic or the application core . This allows the UI to be modified or upgraded without impacting the business logic. Consequently, the application can adapt to new design trends, user requirements, or platform UI guidelines with minimal developmental effort . By decoupling the UI from the core logic, 'Clean Architecture' supports long-term adaptability, enabling the app to evolve over time in response to technological changes or shifting market demands. This stable core also permits longevity, as the crucial parts of the application remain consistent and reliable while the UI changes. This separation encourages innovation on the UI front without jeopardizing application functionality.

MVC, VIPER, and MVVM are architecture patterns used in iOS application development, each offering distinct advantages. MVC (Model-View-Controller) is a simple pattern that separates an application into three interconnected components, which makes it easier for beginners to learn . It allows clear division of responsibilities, facilitating maintenance and scalability. VIPER (View-Interactor-Presenter-Entity-Router) adds complexity but offers clear separation of concerns, improving testability and scalability by strictly organizing components into responsibilities . MVVM (Model-View-ViewModel) enhances testability and code reuse by binding the UI to model data through the ViewModel . Each architecture pattern improves development through varying degrees of modularity, testability, and separation of concerns, thereby allowing developers to choose based on their specific project requirements and complexity.

In the MVP (Model-View-Presenter) architecture pattern, the View component is responsible for displaying data and forwarding user interactions to the Presenter . It does not contain any business logic or data retrieval functionality, thereby focusing solely on UI presentation. The Presenter acts as a middleman between the View and the Model, handling all presentation logic. It processes user inputs received from the View, interacts with the Model to fetch or process data, and updates the View accordingly . This collaboration achieves separation of concerns by decoupling the UI and presentation logic from business logic. The View remains strictly for presentation, while the Presenter manages the application logic, facilitating easier maintenance, testing, and the possibility for reuse of the Presenter logic with different Views.

Implementing the VIPER (View-Interactor-Presenter-Entity-Router) architecture pattern can introduce challenges such as increased complexity due to the separation into many components, each with specific responsibilities . Developers may struggle with managing and coordinating the numerous modules, especially in smaller projects where the overhead might outweigh the benefits . To mitigate these challenges, developers can ensure thorough documentation and establish a clear understanding of the responsibilities of each component, encouraging team discussions to gain consensus on architecture usage . Start with small modules to familiarize with the pattern's complexities before applying it to larger projects, and utilize tooling and frameworks that facilitate VIPER implementation to streamline development processes. By applying VIPER in suitable contexts with clarity on its intricacies, developers can leverage its benefits of modularity and testability in large or complex applications.

The SOLID principles emphasize single responsibility, open-closed, Liskov substitution, interface segregation, and dependency inversion. These principles impact code quality and maintainability by encouraging developers to write code that is modular and easy to extend or refactor without significant changes. A single responsibility ensures that classes have one reason to change, improving focus and modularity . The open-closed principle allows extensions without altering existing code, fostering flexibility . Liskov substitution principle ensures subclasses can replace a class without affecting the program, supporting interchangeable parts . Interface segregation requires specific interfaces for each client, minimizing unused methods . Dependency inversion promotes reliance on abstractions rather than concrete implementations, reducing coupling . Together, these principles lead to a code structure that is easy to understand, test, and adapt over time.

You might also like