Do you have headaches everytime you open your 100+ files Xcode project? Are you hopeless, empty of life and you’re close to throwing your Mac out the window?
If that’s so, you really gotta relax! This article will come and rescue you ✊
1. First things first
Working as an iOS developer for a few solid years, I faced a plethora of issues with regard to my Xcode project architecture. The solution I found is called Modlizer, which is a simple yet effective way to structure source code when an app is created from scratch.
Before digging into it, I’d like to point out there are several iOS design patterns out there and I wish to thank Bohdan Orlov for his fantastic contribution.
… but why don’t I put them into service, right?
They didn’t fulfill my needs and I find them inefficient. Especially the classic type of MVC where you put a bulk of code in one file! Although I acknowledge it is the one recommended by Apple, many, including me, don’t use it and prefer to call it Massive View Controller.
VIPER also caught my attention, however I started using the architecture pattern described here instead and it worked like a charm.
2. Concept
Modlizer takes its name from the logic behind the scenes: make the project modular by separating it into as many independent slices as possible.
That’s correct, folks! Slices 🍊 Does the header image make sense now?
The architecture focuses on the view controller and uses broadly the Objective-C categories and the more recent Swift extensions. They are the ones which give the modularity and classify the logic components in a coherent manner.
Long story short, a slice is simply a particular category or extension which acts as a component of the bigger picture.
3. Anatomy
Done with the talking, let’s get our hands dirty!
3.1 Naming
First things first, naming is important in this business. This is how it is constructed:
Slice’s Name = Prefix + View-Controller + Slice-Description
- Prefix = the two most representative letters of your project’s name
- View-Controller = the plain, simple name of your view controller
- Slice-Description = the actual component (further explanation below)
As an example: APLoginButtonController
3.2 Essentials
There are many things which could be called slices inside a view controller, so here is the list of the essential ones:
- ViewController = the actual class, obviously a mandatory file
- Data = data handler, from declaration to interaction and beyond
- Network = the communication with the server (usually, but not exclusively)
Basically, what I observed during my freelancing career is that a concise, smart distinction between these three slices means higher levels of productivity.
3.3 Optionals
Apart from the main ones, there are dozens of other slices, because this really depends on the project and your English preferences. Here is a partial enumeration:
- NavigationBarController = a customizer for the navigation bar; I always keep this file in the top part of the folder, owing to the fact that it’s a cool thought analogy with the graphical representation (the navigation bar is in the 🔝 of the screen)
- ButtonController = IBActions (quite often)
- ScrollViewController = the scroll view’s delegate
- TableViewController = the table view’s data source and delegate (not to be confused with
UITableViewController
) - TextFieldController = the text field’s delegate
The aforementioned are highly common, but they are not compulsory.
It didn’t make sense to include all of my slices I’ve ever coded, as technically you can build one on any component you’d like to. You’re free to create something like an APLoginAudioController
if you implement audio recognition in your project.
3.4 Flow
This image aims to demonstrate the simplicity of the system. The slices have the ability to talk to each other (green nodes), as they are all categories/ extensions of the same view controller (the blue, centered node).
There’s the gray-coloured Model, but this is an exception we’ll discuss later on.
As a side note, I think Modlizer could be easily called the Controller-Data-Network design pattern, even though I find the name horrible and not sexy at all 😭
Observation: it is extremely important to understand the difference between slice and controller. A slice is the actual file, the whole representation of that component, while the controller stands as a standard, personally preferred ending for an optional slice.
4. Source Code
Let’s study Modlizer by looking on some actual code. This article embodies only the Swift version of the design pattern.
All the code was extracted from a project I worked on, called AdPacer, which is an outstanding app if you’re a marketing person. We’ll look only on the login page’s source files.
4.1 Hierarchy
First off, the hierarcy, inside the folder (or group, the way Xcode calls it):
It may not be visible in this minimalist example, but the order is the following:
- NavigationBarController
- ViewController (base class)
- Alphabetically arranged optional slices
- Data
- Network
This is the way I chose to order them, but it isn’t by any means a must. It just seemed more practical to have Data and Network at the bottom, so they are easy to access.
4.2 APLoginViewController
Naturally, this is the place where we subclass UIViewController
and override the default methods.
Notes:
- This is the core view controller of the login page. I won’t go further into explaining IBOutlets and other concepts.
- If you’re wondering where is the NavigationBarController, well, there’s simply no customization (color, text) done to the navigation bar here.
- As you might’ve guessed, I’m all about commenting my code. Placing
@location
comments in the well-knownviewDidLoad
method enhances the cohesion of the project, so that we’re able to clearly identify in which slice the initial setup of the view controller occurs. - Notice the
appendTextField
! It indicates that we execute some initialization code inside theAPLoginTextFieldController
.
Bottom Line: the aim is to use the
APLoginViewController
for subclassing the motherUIViewController
and use it only for that. No other delegates or custom methods here, only the core of the core.
4.3 APLoginButtonController
The key factor here is the extension, because it enables us to access the code (apart from the fileprivate properties or methods) from any other slice. Do you start to see the beauty of Modlizer? 🙌
Notes:
- Observe the
@location
comments, which explictly describe the source of those methods. didTapForgotPasswordButton
is defined here and it’s not an IBOutlet. Later, you’ll see that this it’s assigned as a tap gesture recognizer’s selector, but it belongs in this slice due to the fact that, at the end of the day, when you tap something, that’s a button!
Bottom Line: Anything related to buttons should be kept here. Although there isn’t any snippet of code which initializes stuff, it doesn’t mean one can’t do it. For instance, there could be an
appendButton
method similar toappendTextField
.
4.4 APLoginTextFieldController
Highly similar (in terms of slice structure) with the previous one.
Notes:
- Observe the
appendTextField
called from the class slice. - Look at the
#selector(APLoginViewController.didTapForgotPasswordButton)
, which bridges to theAPLoginButtonController
. didTapBackground
was implemented here because the background is not actually a button and the action is solely connected with the behaviour of the text fields.
Bottom Line: Anything related to text fields should be kept here.
4.5 APLoginData
Being a fundamenal slice, it can have two parts:
- Class
- Extension
Both are optional and it really depends on the project if you implement one or another.
The quick explanation is that a class defined here will act as a model for the data in the view controller (eg: an array of image URLs). The view controller gains access by storing a property of type APLoginData
.
Regarding the extension, it acts just as the previous ones.
Notes:
isDataValid
verifies the requirements of this specific app.- If a model is provided, the extension doesn’t need to have any code at all. And viceversa! It’s up to you.
Bottom Line: Anything related to data should be kept here, even if in certain complex situations you can create data subslices. But this is a story for another time.
4.6 APLoginNetwork
Powers the communication with a server, be that an API, a service such as Firebase etc. There’s no class at all to be defined here.
Notes:
- Both the request and the response are defined in this slice.
- Notice that the
login
method was previously called from theAPLoginButtonController
Bottom Line: Anything related to networking should be kept here.
5. Rest Of The Project
5.1 General-Purpose Data
Paul, what if I have a general data model? Do I have to rewrite the same model in each view controller?
I strongly urge you not to do that 👊
The solution proposed here is straightforward: create a new folder at the root of your Xcode project and include there any data models you use routinely.
For instance, a social network might need to use an User model in most of the view controllers.
DRY is life!
5.2 Support Files
On many occasions, developers need or prefer to subclass classes from the UIKit. Or one might simply want to write some helper entities.
In these cases, Modlizer recommends to create another folder at the root called support or something alike.
In this way, you get a stable structure which do not interferes with the slices.
6. Conclusion
By introducing Modlizer to the iOS community, I don’t wish to say that it’s better than the other design patterns out there. I simply wanted to make it publicly available, because maybe it will help some of you.
It was solely tested in a freelancing, digital nomad environment and in teams of maximum 3 members, so it might not be bulletproof for larger groups of people working together.
But who knows? It worked superbly in our case, because it provided:
- Modularity by using the slice ideology
- Simplicity in each individual file (no more than 300 lines)
- Reusability at a high level
Overall, fast development speed.
Thank you for reading. Here are some cookies for you!
If you enjoyed this article, would you mind to clap it or share it? It’d be greatly appreciated ✌️
Open to any opinions or suggestions!
Check out this awesome GitHub repo for a sample project. Find me on Twitter or Keybase if you want to chat.