Author
Mauricio Cousillas
At Moove It we work hard to create beautiful, scalable and maintainable iOS apps. To achieve this, we have tried different architectures over time, such as the classic Model View Controller (MVC); View, Interactor, Presenter, Entity and Router (VIPER); Model View Presenter (MVP); and Model–View–ViewModel (MVVM). We have also tried several third party-frameworks, which all come with their advantages and disadvantages.
We wanted to define a baseline for any new project, using all our previous experience to streamline and reduce the number of decisions that a developer has to make before beginning to develop a new project. With that in mind, we defined a project template that works as a starting point for iOS applications; providing a base architecture, core frameworks, and helpers to jumpstart development.
In this blog post, we introduce the fundamental concepts and tools included in the project. If you just want to check out the code, you can go directly to our GitHub repo and access the template.
Base tooling
The following list includes all of the basic frameworks that come bundled with the project and their purposes:
R.Swift
is used for xcode asset managementSwiftLint
is used for style checkingRxSwift
is used for data flow managementAlamofire
+Moya/Rx
are used for networkingRealmSwift
+RxRealm
are used for database managementWhisper
is used for in app notification-style messages
Architecture
We are going to use MVVM + routers (R), but with some caveats:
Models
The models won’t store business logic. They will only act as data stores (Realm objects when using a local database, and Structs in other cases).
Views
The responsibility of Views
(or ViewControllers
in this case) is to display the data provided by its ViewModel
and forward all events to their respective ViewModel
.
ViewModel
The ViewModel
is the component in charge of managing the state of each view and any processing necessary for the displayed or submitted data. Moreover, the ViewModel communicates with Controllers
to fetch the data necessary for its view, and uses the Router to forward navigation actions.
Controllers, Services and DataManagers
The Controllers
are in charge of managing the Models
. This means that ideally you should have one Controller
per Model
(unless you need something like a session controller that can manage more than one model such as a user model) and another model for storing session information.
The Controllers
use two types of support classes; Services
and DataManagers
for networking access and database access, respectively. If you don’t have one of them (for example apps that only consume API data and save nothing locally), you can replace your Controller
with a Service
or a DataManager
and use that directly in your ViewModel
.
Router
The router is the component in charge of handling the navigation stack for your entire application. For this, the router keeps track of your rootViewController
and your currentViewController
(the one currently visible).
To keep everything tidy and isolated, the router does not know how to instantiate the screens that it presents. This is defined separately using the Route
protocol. A Route
is a component that encapsulates all of the necessary logic to instantiate a view
, with its corresponding viewModel
and any other parameters forwarded from other routes. Apart from the Route
, you can set a TransitionType
when you navigate to a route. This tells the navigator how the screen should be presented (modally, pushed, resetting the stack, etc.).
So, to call a navigation action, all you need to do is call your Application’s Router
and call .navigate
with the corresponding Route
and TransitionType.
It’s as simple as that!
Usage
Option #1: Copy the core files to your project
If you already have a running project and you want to use this project’s core modules, you can simply copy the source files into your project. Those are available in the /Core
folder. This can come in handy with projects where you want to start by refactoring some parts of the code instead of starting from scratch.
Option #2: Start from scratch
- Clone this repository to your machine.
- Change the git project remote URL to one of your own with
git remote set-url origin https://github.com/USERNAME/REPOSITORY.git
. - Make all of the following scheme, target and ID changes, to update the necessary information about your new app.
- Change all schemes to be named after your new project.
- Change the bundle ID to one of your own.
- Set up certificates (or let xcode do it automatically).
- Update the
TargetType+BaseProject.swift
to be named after your project. - Check the
Constants.swift
file and update thebaseUrl
properties to point to your backend services.
- Go to your
Podfile
and change the target names to the new ones that you have set up. - Run
pod repo update
andpod install
. - Build both targets to confirm that everything is running properly.
Configuring Environments
We’ve all faced the same problem when defining production and development constants, such as the API URL, setting up crash logging only in production, etc. Here at Moove It, we decided to use two separate build targets, Production and Development, to differentiate between these two environments.
Using different build targets allows us to use Build Flags
to see in which environment we are running. If you don’t know how to set up new Build Flags on your project, a detailed step-by-step guide may be found in this article . Additionally, running two separate targets of your app has other advantages, such as the ability to release both separately and having better management of early access for each environment. If you want to dive deeper into the topic, you can read this post which shows how to set up your targets and what can you do with them.
Conclusions
We have defined a starting point for any iOS project that we could imagine. Having a standard for any project is always an advantage, both for developers starting work on a project from scratch, as well as for newcomers who diving into a pre-existing project. It’s always easier to step into a project knowing where everything should be, which frameworks are being used, and other basic development information.
One question that we asked ourselves while building this was: Can we use this for any type of software project. The answer is yes! One of our main goals was to keep this project as small and flexible as possible so that it could be used in as many cases as possible – and we succeeded. We hope it helps you succeed too!