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.
The following list includes all of the basic frameworks that come bundled with the project and their purposes:
R.Swiftis used for xcode asset management
SwiftLintis used for style checking
RxSwiftis used for data flow management
Moya/Rxare used for networking
RxRealmare used for database management
Whisperis used for in app notification-style messages
We are going to use MVVM + routers (R), but with some caveats:
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).
The responsibility of
ViewControllers in this case) is to display the data provided by its
ViewModel and forward all events to their respective
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
Controllers are in charge of managing the
Models. This means that ideally you should have one
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.
Controllers use two types of support classes;
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
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
TransitionType.It’s as simple as that!
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.swiftto be named after your project.
- Check the
Constants.swiftfile and update the
baseUrlproperties to point to your backend services.
- Go to your
Podfileand change the target names to the new ones that you have set up.
pod repo updateand
- Build both targets to confirm that everything is running properly.
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.
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!