iOS Starter Kit
One day you’re scrolling through your mobile device, consuming media through apps, and you think to yourself: how hard would it be to create an app? So, you quickly search “How to create iPhone apps” and find countless tutorials. You hear and read about Swift, Xcode, UIKit, SwiftUI, etc. Your curiosity is piqued. You tell yourself, “Well, I could do that,” and you start creating your first app.
Time goes by, but too much. You finally finish. You created a weather app! You’re amazed and, more importantly, inspired. You made your device display and run something you created. Now, you decide it’s time to make your career in iOS development.
After devoting many hours honing your craft, you’re ready to apply for a job. Suddenly, a flicker of uncertainty catches you mid-application: “What does a day in the life of a professional developer look like? How do professional developers go about developing apps in the real world?”
Your road to becoming a developer probably didn’t occur like the situation above. However, you may have had the same questions at some point in your career: What does it look like to create an app in a professional environment?
Here you can learn about the iOS development processes at FIVE. We’ll do our best to explain what our developers do and how they do it, giving you better insights into the world of professional development while helping you start your career.
So, who are we?
We’re a bunch of developers who set out to create the most enjoyable mobile experiences. We’re also a pretty laid-back group. We’re always looking for ways to improve, have fun while improving, and we’re dedicated to creating a very clean and easily maintainable codebase. We try to make sure every process is logical and contributes to the ease of development. We do our best to stay in the loop with tools and technologies that have helped us create many successful apps used worldwide.
Let’s start! So, you’ve been assigned a project, and now it’s time to start setting things up. Here are some tips we follow while initializing any project that we undertake. Remember, starting a project with a good foundation is better than applying bandages later on.
We strive to have every piece of code reviewed by peers regardless of the developer’s experience — two heads are always better than one! For source code storage, we use Git. To get the most out of Git and make your work more productive and consistent, it’s best to stick to a specific workflow. At FIVE, we prefer Gitflow.
One way to manage an app’s growing complexity is to have a layered architecture where each layer is one logical structure. Traditionally, there are three layers:
The Data Layer is responsible for fetching data and its manipulation. Data can be stored in a local database or fetched from the server. Any synchronization operations between the remote and local data should be done in this layer using Repository and Domain Transfer Object design patterns.
The Business Logic Layer is responsible for implementing business logic to match the application’s needs using data from the data layer. It also transforms the data from the data layer into models that can be used in the presentation layer.
The Presentation Layer handles interactions between the user and the app. Using the business logic layer data, it draws on user interface elements.
These layers are independent of each other, and each has a separate set of model structures that interact with the lower layer. The layers follow the dependency rule: a higher level can access a layer below it, but not vice versa. In an iOS app, the presentation layer is the highest level, and the data layer is the lowest.
To keep business logic and presentation layers organized and concise, we favor a stripped modification of Viper architecture called VIP which defines these units:
- View – displays information retrieved from the presenter and forwards user interactions back to the presenter. These are primarily ViewControllers and other UIKit files.
- Presenter – responsible for retrieving models from the use case and preparing content for the user interface, as well as reacting to users’ input. It has to be independent of the view layer.
- Interactor (Use case) – retrieves data models from the data layer and contains the business logic for a particular use case. They are not tied to a single view and can be consumed by one or many presenters.
If you’re wondering why we didn’t mention the router unit, it’s because we prefer to keep routing separate from the VIP units and apply the Coordinator pattern to handle the routing across the app. The coordinator (router) is responsible for instantiating the screens and their dependencies and presenting them on the navigation stack. The coordinator should not contain any business logic but instead focus only on routing. There will be many routes and, therefore, many dependencies to manage in mid to large applications, which can be considered as overhead. To solve this kind of scenario, you can use a dependency injection framework, like Resolver.
Here’s a typical example of a class diagram from the presentation layer to the data layer:
Notice the dependency rule on the image above.
Our beloved design paradigm is reactive programming. This pattern relies on asynchronous programming logic to handle real-time updates to otherwise static content. In plain words, if there is an asynchronous event, code can instantly react to that change.
We use Combine for achieving this idea where parts of the code react to specific changes. Before reactive programming, we had to manage many different mechanisms of handling data such as network requests, user input, notifications, KVO, and more. Even though Combine is our main reactive framework, it’s not our first. We have a lot of experience in reactive programming using RxSwift too.
There are many ways to implement programmatic UI. We decided that the best practice is to formalize the whole process.
We created a recipe that streamlines the programmatic UI building process that we religiously follow on all of our projects. It consists of three steps:
- creating views – creates and initializes all child views
- styling views – sets style properties for each child view
- defining the layout for views – sets layout constraints for each view
These steps are defined in a ConstructViewsProtocol, and every class responsible for views construction needs to implement it.
We’ve used Cocoapods as our primary dependency manager for a long time. However, Swift Package Manager or SPM has matured over the years. It’s become our weapon of choice for managing both external and internal packages as it perfectly suits our needs.
SPM is a first-party tool created by Apple to manage Swift code distribution. Its seamless integration with Xcode facilitates package management. Updating packages is as easy as clicking a mouse and selecting “Update packages.” It comes with a simple UI for searching for packages and determining and constraining their versions.
Sometimes Apple doesn’t take the most straightforward route for problem management. Nevertheless, third-party frameworks come to the rescue.
Programmatic UI is our preferred way to build a beautiful UI. Anyone who’s tried building natively knows it is cumbersome to type out every constraint. For this task, we use the power of SnapKit, which helps us write readable and straightforward layouts. Another framework worth mentioning is PureLayout, which we also use in many of our projects.
When it comes to dependency injection, our favorite tool is Resolver. It’s an ultralight DI framework that supports the inversion of control design patterns. Resolver allows us to write loosely coupled, straightforward code to use, reuse, mock, and test. It’s noted on the Resolvers GitHub repository that dependency injection boils down to: “Giving an object the things it needs to do its job.”
Additional shoutouts go check out:
Kingfisher: a powerful library for downloading and caching images from the web
CocoaLumberjack: fast and undemanding logging framework
Realm: easy and fast mobile database which is a great alternative to the Core Data
SwiftLint: a powerful linter that we’ll talk about later
You set everything up. Congrats! Now it’s time to put hard work into project development. You should have a solid foundation so let’s start building on it. These few tips should help you create consistent and aesthetically pleasing code that is readable and easy to understand.
Good code style is subjective, but code consistency helps with its readability while avoiding the introduction of errors. Robert C. Martin says we spend 10x more reading code than writing code. With that in mind, follow the Official raywenderlich.com Swift Code Style while writing your code. While we don’t use this style guide for day-to-day operations, we recommend it highly.
FIVE’s code style guide is one we developed on our own. Things were said, friendships were broken, but we agreed on the final set of rules and conventions that now make the FIVE Swift Code Style. Developers are famous for being passionate about their code, and we’re not any different. The FIVE Swift Code Style is our bible. We live by it. But like anything valuable, it’s kept safe in a private repo.
A linter is a static code analysis tool that flags programming errors, bugs, stylistic errors, and suspicious constructs. In other words, a linter is a great tool to enforce a particular code style. We use SwiftLint as our linter of choice.
SwiftLint is painless to implement and has a simple configuration file which makes it effortless to add and modify code style rules. It can even be used as a pre-commit hook, making it impossible to push improperly formatted code.
Here is a SwiftLint Configuration File that you can use in your projects.
While many of our coding processes have evolved due to technologies, experiences, and frameworks, code review is one process that has been deeply ingrained at FIVE from the beginning.
With code reviews, developers learn codebases and new technologies and grow their skillset. Often, teams discover new knowledge that surfaces during code reviews. Additionally, with fresh eyes, newer team members find gnarly, time-plagued areas of the codebase that need a new perspective. Code reviews also help to ensure new insights are tempered with existing knowledge.
So, with Gitflow as our branching model, code changes are committed to the feature branch before being merged into the destination branch. When a feature has been developed, a pull request is opened, and other team members review the new while considering the questions below:
- Are all requirements fully implemented?
- Are there obvious logic errors in the code?
- Does the new code conform to existing style guidelines?
Only when other team members have approved the pull request can changes be merged into the destination branch.
Our cards are on the table for anyone to see. We showed you how we approach new projects. If you decide to use this doc for your next coding project (and you don’t want to scroll up and down continuously looking for the correct info), we made a simple checklist that you can follow.
Your next steps
We hope that you learned something from our iOS starter kit and maybe even sparked your interest in professional iOS development at FIVE.
It would be awesome to create a small project showcasing what you learned from here — but it’s not a requirement if you’re applying for a job. If you want to up your game as an iOS developer, check out open positions at FIVE. If you don’t see anything, send us an open application anyway.
We can’t wait to see what you come up with!