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
Being an iOS developer for a few solid years now, 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.
… 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.
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.
Done with the talking, let’s get our hands dirty!
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:
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.
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
- 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.
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.
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:
- ViewController (base class)
- Alphabetically arranged optional slices
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.
Naturally, this is the place where we subclass
UIViewController and override the default methods.
- 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
@locationcomments in the well-known
viewDidLoadmethod 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 the
Bottom Line: the aim is to use the
APLoginViewControllerfor subclassing the mother
UIViewControllerand use it only for that. No other delegates or custom methods here, only the core of the core.
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? 🙌
- Observe the
@locationcomments, which explictly describe the source of those methods.
didTapForgotPasswordButtonis 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
appendButtonmethod similar to
Highly similar (in terms of slice structure) with the previous one.
- Observe the
appendTextFieldcalled from the class slice.
- Look at the
#selector(APLoginViewController.didTapForgotPasswordButton), which bridges to the
didTapBackgroundwas 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.
Being a fundamenal slice, it can have two parts:
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
Regarding the extension, it acts just as the previous ones.
isDataValidverifies 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.
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.
- Both the request and the response are defined in this slice.
- Notice that the
loginmethod was previously called from the
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.
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.