Many Revolut users manage their financial life in our iOS app. Let’s take a look behind the scenes of the iOS development at Revolut ― how we deliver outstanding financial services and smooth user experience on any iOS device.
We have three apps for iOS: Revolut, Revolut Junior and Revolut Business, updated in the App Store on a weekly basis. We work on the principles of continuous integration/continuous delivery to produce new functionality for users fast — as soon as a feature or part of it is ready.
We’ve built our own in-house Framework for feature-based release. According to the new process, a Product Owner specifies a feature to be published to the store, and then a feature flag is enabled and the phased roll-out starts. Feature flagging allows us to deliver value to users with the minimum amount of risk. If anything goes wrong, you just turn off the flag to roll back the problematic change. Moreover, feature flags allow the team members to easily switch between the feature versions and compare them or turn on a new feature and test it.
All our products are implemented with Clean Architecture and MVVM. We use the Flow Coordinator to present the flow of the screens, and life cycle behaviors that help us implement reusable View Controller behaviors.
Our apps are not monolithic. Currently we have around 60 modules. They include shared modules such as the core module, UI components modules, the chat module, and feature modules like trading, payments, credit, cards. All our apps and shared modules are stored in a single repository, Monorepo.
Modular architecture is described in detail here, so let’s just briefly highlight its benefits:
- Faster build times. Every module has an example/demo project to develop it in isolation fastly without compiling the whole app.
- Faster development resulting from faster compiling and easy access to screens in the example project.
- Isolated environment that allows us to easily try new things.
- Tests running in seconds.
We write in Swift and use iOS SDKs. We prefer not to use RxSwift maintained by the community, as we can’t afford to rely on someone else to fix any issues while we wait. We implement reactive programming methods inside our core libraries instead.
Other tools and technologies that we use include
- Xcode and a Git client of choice (e.g. Sourcetree)
- CoreData for data storage. Data from CoreData cache database can be shown on the screen when the app is offline.
- Danger that checks a PR before we run auto-tests, and SwiftLint that checks code style
- fastlane and TeamCity, to support CI
Any build from a release, dev, or feature branch can be published to TestFlight and AppCenter. Each month, our iOS team builds the app 15,000 times, so we decided to experiment with Bazel by Google in order to increase build speed. This helped us cut build time almost in half, from 4.2 minutes to 2.64, saving ~400 working hours a month. This has resulted in our decision to migrate from Xcode build to Bazel.
We have 70 high-skilled iOS developers spread among different teams. There is established code ownership where each team is responsible for maintaining one or more modules.
Each team includes developers for iOS, Android, Web, and Backend, a Product Owner, a Designer, a Data Analyst. So each team is like a small start-up that can come up with ideas and turn them into products or features. Team members have a full control over the functionality they develop. For an engineer, being part of a cross-functional team means having the opportunity to influence the product, participate in decision making, engage and learn technologies outside your expertise.
In a cross-platform team it’s simpler to ensure that our foundational principles are maintained across all our products and platforms ― CI/CD, automated testing, UI consistency, and more.
Security of customer data. All our teams understand that Revolut’s success depends largely on how seriously we deal with security risks. We prefer in-house solutions for user data tracking and analysis so personal data is never exposed to third parties. We use data to improve user experience, like removing unnecessary steps from a flow or finding the best place for a button to go in the app. For iOS in particular, we store user data and credentials in KeyChain. We use Firebase in GDPR regions as an exception to collect data on app crashes.
Automated testing. Developers at Revolut are expected to take responsibility for the quality of their code, and write automated UI tests, unit tests, and screenshot / snapshot tests. Engaging developers in auto-testing doesn’t have to be painful ― mobile teams use the special framework for UI tests that requires just a few lines of code to create a test. We also believe it’s of great importance to test all the main components, so we have a 100% test coverage of our Core modules.
We’ve analysed which UI components are highly reusable in our apps and stored them all in one Design System to facilitate consistency of the app’s visuals. We’ve created this system with the main ideas of atomic design in mind.
The Design System saves resources both in terms of code volume, and time. Developers don’t have to duplicate code or spend time reinventing the wheel. They just start the Example App that shows all existent UI components, pick and reuse the right component for their feature.
Want to build a world-class financial product for iOS? Explore the career opportunities at Revolut, and join the team of the most talented developers.