Our iOS tech stack for mobile app development
The tech stack provides the product team with the tools they need to build and maintain the product and ensure it meets customer needs. A tech stack usually consists of programming languages, frameworks, analysis tools, architectural patterns, and many others. In this article, I'll introduce you to our iOS team's solution stack.
Swift is a programming language from Apple, intended for the development of applications for the macOS, watchOS and iOS platforms. The motivation behind the creation of Swift was to create a usable alternative to the Objective-C language. In simple terms, Swift could be described as a modern, fast and powerful programming language designed with an emphasis on security.
There are 2 frameworks available to developers for styling the UI of native iOS apps: SwiftUI or UIKit. We currently use both, but UIKit prevails, mainly because not everything from UIKit can currently be implemented with SwiftUI, which means that for more complex applications, you need to combine UIKit and SwiftUI or use only UIKit alone. For a more detailed look at the issues associated with SwiftUI, you can find this article in the section "SwiftUI or UIKit?"
Most of the iOS projects in GoodRequest are created using the UIKit framework.
The Framework includes components designed for creating user interfaces for iOS, iPasOS, WatchOS or tvOS applications.
It provides an architecture for creating UI, infrastructure for processing user inputs and interaction between the user, the system and our application.
SwiftUI is a modern framework designed for creating a user interface for any Apple platform. It was created as an alternative to UIKit with a declarative approach to programming. We started using the framework only 3 years after its introduction, and it still has limits that developers encounter when developing applications.
The advantage is a declarative approach to programming that simplifies the process of creating a user interface, the ability to cooperate with UIKit. Previews are a big help, with the help of which the developer can almost immediately see all the changes made without the need to launch the application
As part of the team, we also work on various internal frameworks that help us streamline selected activities and at the same time ensure high quality projects.
An internal library used to facilitate working with TableView and CollectionView in our applications. GRProvider "takes responsibility" and the developer does not have to implement all required methods of individual native components himself.
"By developers for developers", the Good IOS Extensions library is a collection of modules and enhancements from server communication to text formatting that make the work of our developers easier.
We still use both SPM and CocoaPods in our iOS projects, choosing one based on whether its advantages outweigh the disadvantages when used in a specific situation, or the availability of support for the required libraries.
Swift Package Manager (SPM) is a tool for managing the distribution of code written in the Swift language. The advantage is that we can integrate dependencies into projects directly through Xcode. Unlike CocoaPods, the files are only stored on local devices and do not "inflate" the project size. However, developers are not happy that with every build of the application, all dependencies are also built, which increases the length of the build. Another unpleasant fact is that SPM has not yet matched CocoaPods in the number of libraries provided.
CocoaPods is another manager used to manage dependencies at the application level. It contains a large number of usable libraries and allows for graceful scaling of any project. Here, in contrast to SPM, developers will be pleased that the libraries are built longer only once, and then the build time is significantly more pleasant. A disadvantage may be the fact that CocoaPods need to be integrated externally into projects.
This tool helps developers maintain good code readability and ensures adherence to conventions and style when writing code in Swift. SwiftLint notifies the developer of their errors directly through the Xcode UI.
SwiftFormat is a tool for automatic code formatting. SwiftFormat can automatically adjust whitespace, add or remove implicit self, remove unnecessary parentheses, and remove other deviations from the standardized Swift idioms.
Logen is an internal tool written in Python that helps developers generate localized strings for Swift projects directly from a Google Docs spreadsheet.
SwiftGen is a tool for automatically generating Swift code, specifically for resources such as colors, images or localized strings, so that they are type-safe.
Firebase is a group of several services for applications of any type.
We use notifications via Firebase Cloud messaging. The main advantage is that our back-end implements only one service and it further communicates with iOS and Android applications automatically.
Firebase Crashlytics is a service that reports crashes to production applications. It allows us to monitor them, react quickly to them and thus increase the quality of our applications
Continuous Integration and Delivery (CI/CD) allows new code to be integrated more often without reducing the quality of the application itself. CI/CD is designed to automate repetitive tasks such as build, test, and deploy, and to flag potential problems. We primarily use three tools for these purposes.
Fastlane is an OpenSource platform designed to simplify the process of deploying iOS applications. Fastlane allows us to automate almost every aspect of our work flow in the software development or release phase.
It allows developers to automate basic workflows, allowing us to combine common tasks such as deploying to a tester, informing the team on Slack about a new version, or increasing a version number.
Danger is a tool deployed as part of the CI process. It gives us the opportunity to automate the work associated with code review and commenting on our PRs based on predefined rules. This allows the development team to focus on more complex issues in the code review process.
It makes it easy for developers to separate UI development from logic development. It then looks like this in projects: View and ViewModel are defined separately, View has its own ViewModel assigned. View takes care of all user interface components and displaying data from the associated ViewModel. The responsibility of the ViewModel is to ensure the necessary handling of the data displayed by the View.
View is used to display data. Reactor is a layer independent of the user interface, it manages the state of the View.
In practice, the flow looks like this: View sends Reactor an action, for example, based on some user input. The Reactor "reacts" to the given action, performs all the logic defined as a reaction to the given action, and as a response the View returns the state, which causes changes to the View.
Coordinator pattern is another structural design pattern that we use. It serves to organize the flow logic between individual screens. In the application, we define all the steps of all the screens. Each step represents moving to a new screen or closing it.