Goal of Software Architecture is Understandability, Replaceability. — Joachim Kurz
If you read blogs and watch lots of video tutorials about architecture patterns, sometimes you can get confused just by the terminology. The namings of elements can deviate from pattern to pattern — however, the main concepts are the same. I will try to add some synonyms so it is easier to understand.
There is no universal solution for every problem. This explains why we have lots of architectural patterns. Every pattern tries to resolve some specific problem. Applying the wrong “solution” for some problem just complicates things.
iOS Project architectures: Introduction
When I became a mobile developer, based on previous software engineer experience I had some expectation that the challenges will be technical, math, and algorithm related. To my amazement, I realized that I was wrong. An exception could be if you go straight to SDK (framework, package) development.
Usually, in mobile app development, things happen faster. Adaptations need to be done much quicker. On the “Business” side, Product Owners and Managers generally have no straight vision of the end goal, just a direction. Sometimes development starts in R&D, PoC format, or A/B test format.
It is hard to make good predictions in advance if the milestones are changing all the time and on the table are not well-defined features.
Joining an ongoing project means that you need to learn the organizational structure of the project before starting to be a productive part of a team. By organization structure I mean, the folder structure and architecture patterns — generally the way of decomposition.
For developers to resolve a problem, they need to separate the key components of the problem, approximate it, and focus on the goal to reach some solution. My goal in this article will be to tell you what I found to be the best way to organize the project and my approach to architecture patterns with examples of the iOS projects. Though, these principles can be applied to other front-end and UI-based projects too.
When we talk about how to organize the project, most people think about architecture patterns. The most basic one is MVC.
I won’t go into details of each MVC-like pattern. If the screen has too much responsibility you can add a child view controller.
If need more separation, MVC with some Service can maybe help. MVP can maybe do the job if the view has complexity. And we can level up this to MVVM, VIPER, CleanSwift, etc.
On a high level, all these patterns try to extract some responsibility from code by adding some layer. Later we’ll get back to this.
When we talk about project decomposition there are many ways how to do it. I will present 4 different ways, but there are many more.
- Technical — this is familiar for most. This is the case when you group around one Architecture pattern. E.g.: In MVC there is a
Viewfolder that contains
RegisterViewand all views. Get the idea?
- Visual — this is what I prefer in most cases. If the number of the screens is significantly bigger than the number of features, then this is the way of decomposition. This allows you to introduce more architecture patterns to the same project with a lower risk of confusion. Also, this is the best pick for the majority of the application on the market.
- Domain — this is more connected to feature in some way. If some screen presents lots of features and it can be split based on that. Example: some graph and some list of articles, this can be managed by different
ViewController(child VC) as are built by different models. This is useful when the app has one complex screen with dozen of features or view fewer SDKs. The idea of this decomposition comes from the parallel programming world.
- Temporal — This represents the sequence of something. Example: if you create some onboarding framework, where onboarding is linear. Or some tutorial pager view. Then this is your choice.
When developers start their careers, they focus too much on this technical dimension, and even worse, they apply it to the whole project. Maybe this is biased by online tutorials? Too much attention is given to decomposing the app technically and not considering the real need of the business.
Focusing to decompose in technical dimension, we are just adding one more layer. Maybe some parts don’t need networking. Maybe some features don’t need a view. So we have lots of small parts, which are not useful and just decrease the readability. With this approach, the code might look cleaner, but that does not mean it is understandable.
Resolve different problems with different decomposition dimensions but locally. The problem is local, the solution doesn’t need to be applied globally on the whole project. For example: if there is a screen that requires some more sophisticated architecture pattern like CleanSwift or VIPER, that doesn’t mean that needs to be applied to the whole project.
This could happen when a team of senior developers starts the project and apply some pattern like VIPER with templates. In mid-term, a junior developer is introduced to the project and has no opportunity to do step by step introduction to technologies. The developer is forced right away to learn VIPER, business logic, team structure, maybe some agile process. This can be overwhelming, intimidating, and under pressure.
iOS Project Architectures Organisation
If you have lots of changes and brainstorming with Product Owner I would separate two concepts around which you should build the app.
Scene (example: Login Screen) and
Feature (e.g.: Geolocation, when a user enters some particular shop in range, get push notification about shop best deals). Also, I like to create graphs of screens and mark which part is using which feature.
The second thing that I have found useful is the Layered Architecture concept. The version with 3 + 1 layer. In case there’s some complex application, layers can be increased. These number of layers are enough to cover most cases. It reduces software complexity and increases the understandability and readability of the code. This Layered Architecture can be also reflected in a folder structure.
- Application Layer
- Presentation Layer (UI Layer)
- Business Logic Layer (Domain Layer)
- Data Layer (Core Layer, Data Access Layer, Persistence layer)
This is the entry-level of the app. This part is the global layer that should contain elements like
SceneDelegate, Configurations, Constants, starting DI components. Let’s say all the things that need to be accessible in the whole application, and need to be live from start.
Most applications have lots of screens. More screens compared to the number of the features. In such cases, this layer is the central figure.
As you can see in the previous picture, it contains a
CommonUI folder. That folder’s purpose is to contain extensions, some universal base classes, UI helpers, universal buttons, etc. It contains
Scene folder which is the central component. If you have a login screen, it will have
LoginScreen folder with
ViewData) classes etc. This depends on the screen architecture pattern too.
Keep in mind that ViewModel in different patterns can have a bit different responsibility and belong to a different layer. This can be confusing. In my example this is for MCV like patterns, where ViewModel serve just the view with simple data like text, number.
Business Logic Layer
This layer is all about client business rules. This is at the heart of the app’s logic and provides a meaning to the codebase.
I would call this as the
Feature part. More screens can share the same
Feature aspects. From a technical point of view this contains the
Provider — any of this some kind of data structure as an output, which serves the Presentation Layer or Core Layer. These components are stateless. For instance: the Presentation Layer, can ask the
UserWorkflow to give you some user by id — which would give you some
- Feature can be built from multiple Services and Providers.
- More Screens can represent the same Feature in different ways, or to represent some aspect of the Feature.
- One Screen can represent more Features.
- The Feature can be represented without a Screen (like push notification)
This is the part where we get data from remote or local data sources. Or after some operation with data, we save it. This could be like CoreData, some Network API, or UserDefaults. It’s also the place where we can find some Models, Entities,
DTO (Data Transfer Object),
ModelController. Someone would put here also the
LocalDataBaseManager as consider them as
ModelControllers, I would say they are Services that convert JSON to Models.
As you can see in the picture above, some parts are between layers. This means that in different patterns, components can belong to different layers. From my perspective, it doesn’t matter. What’s important is you have a separation and a defined data flow.
So let’s go back a couple of steps. When developers generally start building an app, the focus is mostly on the development speed, reuse, consistency, portability, easy maintenance, etc. Then it evolves into compile-time optimisation, easy onboarding, runtime performance, etc.
In any case, these goals are on different ends of the table and the focus can be on 1 or 2 at the same time.
So the quote from start says, that technically the focus should be on understandability, replaceability with which I totally agree.
Every app has a couple of complex screens and 10–15 or more really simple ones. From this point of view, I approve to use different architecture patterns per screen. Usually the central class is the
ViewController, and every screen has at least one. What could help the next developer on this screen is some description about the used pattern or any technical information. I’d leave this information in the header as a comment.
ScreenA(login, simple) — use MVC + some
ScreenB(some complex dashboard with more API calls, optional and different charts, and expected that it will have new features, A/B testing) — use CleanSwift
ScreenC(some semi-complex with couple view parts) — MVC with child VCs
Pros of this approach
- Replaceability: Screens can be easily replaced as all the screen components are in one place
- If a junior developer comes to the project, onboarding is easier. Most parts are easy to understand. Can start to work on easier parts.
- For most of the screens, MVC and some Service are enough.
- No need to follow one complex architecture pattern for simple screens with almost empty classes.
- Understandability: just couple of screens have bigger complexity
- Debugging is easier as understandability is important
- Replaceability: also means that it is easier to test it
Cons of this approach
- Not the best code organization, if there is more feature then screen
- Bad for SDK development
MassiveViewController should not exist. If you see that this is happening, then ask yourself. What does this screen do? For what is this feature used?
If the answer is not a simple sentence, then probably some decomposition is needed, refactor it. There is no universal best pattern. Every pattern is a solution for some problem. Also, ask yourself will this resolve something globally or locally? If we want good understandability then we need to ask lots of questions.
If you got to this point, thanks for reading. If you like the content please ??, share, subscribe, it means to me. If you have some suggestions or questions please feel free to comment.
Full Article: Varga Zolt @ Better Programming
Mobile App Development Best Practices – 02.10
Data.ai has summarized the interim results of the year – and once again we have a record. Annual consumer spending...
How to Use Kotlin’s Timing API
Kotlin’s Timing API is stable as of Kotlin 1.9, and it offers some great ways to both measure and specify...
Candy Crush made $20 billion
King attributes its huge success to several factors, including not only the games themselves, but also the company's commitment to...
FittedSheets – Bottom sheets for iOS.
This project is to enable easily presenting view controllers in a bottom sheet that supports scrollviews and multiple sizes. Contributions...
ComposeFadingEdges – Android Compose UI with fading edges
The ComposeFadingEdges is a powerful Android Compose library that seamlessly incorporates customisable fading edges with horizontal or vertical orientations, static or scrollable...
Mobile Consumer Spend Surpasses $100 Billion in Record Time in 2023
Positive growth in mobile consumer spend continued in Q3 2023 at a solid 3.7% year-over-year. Downloads declined slightly over the...