Developing a cross-platform building tool like KMM offers powerful solutions, but can also come with its own unique set of potential risks. With this in mind, it's important to weigh the merits and drawbacks before taking on such an endeavor.
KMM, or Kotlin Multiplatform Mobile, is an SDK that allows for sharing of common code between iOS and Android applications, leaving you to write platform-specific code only where it's necessary: UI, navigation, and platform-specific features – among others, AR and Bluetooth.
Although still technically in beta, the KMM community of developers is growing in popularity and the SDK itself is being implemented in more and more commercial projects.
Traditionally, before starting on the development of a mobile project, the team has to make a decision between native app implementation (iOS and Android), and using a cross-platform solution (e.g. Flutter, React Native). Both of these approaches have limitations and might not be optimal for some projects.
KMM provides an interesting alternative, offering the potential for time and cost savings while maintaining 100% of the UI/UX of native mobile apps.
Therefore, it's essential to ask ourselves: “Should we consider KMM for our mobile project or decide as normal between native and cross-platform technologies?” Followed up with: “Are there any red flags, technical limitations, or high-risk factors we need to consider before choosing to implement our project in KMM?”
Below are our findings of thorough investigation into the capabilities and limitations of KMM.
Worst-case-scenario: the most challenging project to rewrite into KMM
We assume the most challenging scenario will arguably be a legacy project that relies heavily on platform-specific features, for example, a video or audio capture. To illustrate, we decided to analyze the BabyGuard iOS application created by Netguru.
BabyGuard is a “digital nanny” application that provides parents with an intelligent way to keep an eye on their children. The app features smart detection of a baby crying, alerting the parents immediately. The app has to be installed on two devices: the parent’s and the child’s. The latter is usually an older device that the parents no longer use.
The app includes additional features such as video and audio streaming and a customizable crying detection threshold. It does not need a backend or streaming services – it is enough that the parent’s and child’s devices are connected to the same local network.
Initially, the BabyGuard application was only available on iOS devices. However, it was soon realized that a significant number of users have an old Android phone or tablet. As a result, the decision was made to also implement the application on Android.
Two potential implementation strategies were evaluated:
- Refactoring the iOS app with subsequent implementation of the native Android app.
- Rewriting the iOS app into KMM, facilitating usage on both platforms.
When implementing a cross-platform solution like KMM, there are several potential risk factors to consider:
- Uncertainty around the correct operation of some app features migrated to the KMM Common module, such as running a Machine Learning model to detect a baby’s crying.
- Difficulties in accessing platform-specific features in shared KMM code, including video and audio streaming, as well as audio capture.
- The need to interact with platform-specific APIs, such as push notifications.
POC – determining the feasibility of a project
Before starting a project, it is recommended to implement a Proof of Concept (POC) for each platform-specific feature:
1. One of the core functionalities of the app is audio stream monitoring.
To avoid code duplication, it’s worth implementing it in the KMM Common module. However, we need to account for the differences in audio capture and encoding, memory management, and access to device resources between the platforms. Potential issues could result in crashes or memory leaks. This is especially important for the iOS app, as the system is more aggressive in terminating apps that exhibit abnormal memory usage.
2. Another core app feature is on-demand video streaming.
It should also be implemented in the KMM Common module to be reused across platforms. The iOS application uses GoogleWebRTC to set up (and connect to) video streaming.
Before committing to the project, it is critical to ensure that a streaming server and a client (implemented in KMM) can seamlessly connect across platforms and perform streaming for a prolonged period of time.
It is also important to test the quality of streamed audio and video for different combinations of devices, for example, a child’s iOS app and a parent’s Android app. As the streaming is an on-demand feature, potential memory leaks should not be severe enough to result in app termination.
3. Both iOS and Android devices support local network discovery (necessary to locate and connect child and parent apps), but they might use different networking protocols to do that.
KMM offers expected/actual functionality to implement platform-specific parts of the feature using native code, exposing the execution results of said native code to the KMM Common module.
This way, only the code responsible for interacting with a platform-specific API would have to be written separately for each platform, while allowing the business logic operating on the resulting data to be implemented once. When the devices exchange IP addresses, communication is carried over to the RTC server (which we described in the previous point).
For each of the above POCs, there is always a “last resort” solution, should the primary choices prove unimplementable. That is, to implement the functionality separately for both platforms. Naturally, this comes with significantly higher costs in terms of both development and time.
The cost of implementing the Baby Guard project in KMM amounted to 69 man-days. Alternatively, refactoring an existing iOS app and implementation of an Android-only app from scratch amounted to 94 man-days. If the decision was made not to refactor the iOS application, the estimations are comparable.
Best-case-scenario project: the least challenging project to implement in KMM
Let’s consider another project: an application visualizing sales reports and individual recommendations for merchants using payment terminals. The business model is to sell the service as a subscription. The application is designed to be a white-label – subject to limited branding by the payment terminal providers.
The sales data is aggregated, anonymized, and processed by the backend, relying on the application to visualize it. In addition, the application is required to conform with the strict security policies of the data providers.
Initially, the project is planned to be released on Android devices: phones, and payment terminals. Following a successful MVP, an iOS version of the app is also planned to be released.
In contrast to the previous example, the project does not seem to rely heavily on platform-specific features. Arguably, the only such feature might be the access to crash reporting, analytics, and push notifications offered by many third party libraries / SDKs.
Those tools have different implementations on iOS and Android. Most of the required features would likely be implementable in the KMM Common module, for example, crash reporting. The rest might be either implemented natively, using the expect-actual KMM feature, or moved to the platform-specific part of the application entirely.
The good examples might be: implementation of the app analytics for the former, and handling push notifications for the latter.
The following app features seem perfect candidates to be implemented in the KMM Common module, allowing the upcoming iOS application to reuse that code, and, subsequently, be released much faster:
- User authentication
- User subscription verification and renewal
- Synchronizing sales report data
- Parsing report data
- Performing device identification
- Handling API rate limitation
- SSL pinning
We also have to point out features that have to be implemented separately in the iOS and Android apps:
- Entire application UI / UX (including the navigation)
- Push notification setup (asking for user permission)
- Receiving push notifications
- Advanced platform-specific security features (e.g., code obfuscation, rooted / jailbroken device detection, etc.)
Based on the following, we attempted to estimate the amount of time required to complete the project. Without going into the technical details, the KMM Android app estimation amounted to roughly 80 man-days. Meanwhile, implementing a traditional, Android-only native application would take roughly 75 man-days.
The difference is accounted for by the additional time required to set up the KMM project and establish communication between the Common module and the platform-specific part of the app.
The true impact of the introduction of KMM into the project would however be noticeable after the start of the iOS app development. Based on the experience that the Android Team had with the initial application, we can assume that the native iOS-only application would be completed faster (already having, e.g., a complete backend API available) – approximately 65 man-days. However, when utilizing the shared codebase with the Android KMM app, we can bring that number down to 40 man-days.
As a result, instead of 140 man-days required to complete two separate native mobile applications, the entire KMM-based project can be completed in less than 120.
Realistically, when accounting for simplified designs and the possibility to use third party charting tools to generate sales report content, the initial estimation can be decreased to less than 100 man-days.
Ultimately, as indicated by Jetbrains' study, the total amount of time saved when using KMM to implement iOS and Android applications amounts to 30-50%.
Additional aspects to consider
Should the client decide not to implement the iOS app after all, there is always the option to revert back to the native Android app. All that needs to be done is to move code from the KMM Common module into the native Android app and remove all unsupported dependencies. This should not take more than 2 man-days.
The evaluation of KMM – when to use, what to look out for
In this article, we have gone through the viability of implementing a mobile project in KMM.
We have discussed arguably the most challenging situation: when we already have an iOS application (whose code we cannot transfer to the KMM Common module in any form) and are considering rewriting it in KMM for both iOS and Android.
In such cases, we need to properly identify critical platform-specific features that have to be implemented in both apps. The more there are, the greater the risk that the KMM project will take longer to develop (and more code would be duplicated across platforms).
The good news, however, is that there is always a way to implement even the most challenging platform-specific features in KMM.
It would just take more time and code. In addition, the structure of code in KMM, especially the multi-modular approach, makes it possible to revert back to native Android-only development.
Having such an option is invaluable should the plans to release an iOS application be abandoned in the middle of the development. The same cannot be said for any cross-platform technologies like Flutter or React Native.
Getting down to the numbers, even in an overwhelmingly unfavorable situation (approximately four platform-specific features), a KMM application still offers approximately 20-30% faster development times when compared to purely native development, assuming the necessary refactoring that would have to be applied to the existing iOS app. Even considering an unlikely scenario in which the iOS application codebase is in perfect condition, the estimation for project development in KMM and native Android SDK are comparable.
Naturally, every project is different and it is highly recommended to implement a POC to determine how to implement a given platform-specific feature, as well as estimate how long it will take to do so. Armed with this data, we can assess how much time we might be able to save thanks to KMM, if any.
We have also examined an ideal candidate – the greenfield project – requiring almost no platform-specific features, developed for Android first, with iOS application development following a successful MVP rollout.
Similar to the case above, the more code we are able to share between the platforms, the faster the development process will be. In the case of this particular project, the rough estimated gain from utilizing KMM, in comparison to natively developing iOS and Android apps, is between 20% and 40%. This is an impressive result given the UI-intensive nature of the project (reports, charts, etc. that need to be implemented natively).
Additionally, it’s worth exploring the maintenance costs of KMM projects. If most of the critical application code can be reused across the platforms, it would require less time and effort to change. The following is also true for the maintenance cost of the Unit Tests suite necessary to ensure that the codebase is not declining.
It’s also worth mentioning a reduced requirement for talent to handle said maintenance. Instead of an iOS and an Android engineer needed to maintain their respective applications, the KMM Common module can be maintained by an Android engineer alone.
In conclusion, KMM should at least be considered as an option to implement native applications when:
- The iOS and Android applications are to be developed at the same time, or the Android app is followed by the iOS one.
- The critical platform-specific features of the app (like the use of Augmented Reality, Bluetooth connectivity, audio and video capture, etc.) can be implemented in the KMM Common module and reused across the platforms.
- The application requires a platform-specific UI/UX.
- There is a focus on low-cost maintenance after the application has been released.
- The project is planned to be maintained for a prolonged period of time.
- There is limited access to cross-platform or native talent on the market.
- Although it is recommended to start the KMM application with two senior developers (each specializing in one of the platforms), the iOS senior developer is not required throughout the process (once critical features have been implemented in the form of a POC).
Krzysztof Kansy, Android Developer at Netguru, is the co-author of the article.