Simplifying Application Development with Kotlin Multiplatform Mobile

By Róbert Nagy
    What do you get with a Packt Subscription?

  • Instant access to this title and 7,500+ eBooks & Videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Chapter 1: The Battle Between Native, Cross-Platform, and Multiplatform

About this book

Sharing code between platforms can help developers gain a competitive edge, and Kotlin Multiplatform Mobile (KMM) offers a sensible way to do it. KMM helps mobile teams share code between Android and iOS in a flexible way, leaving room for native development.

The book begins by helping you to gain a clear understanding of the Kotlin Multiplatform approach, how it works, and how it is different from cross-platform technologies, such as React Native and Flutter, and code sharing options, such as C++. You'll then see how your team can use this software development kit (SDK) to build native applications more effectively by learning timeless concepts and working through practical examples. As you advance, you'll get to grips with the core concepts, understand why UI sharing fails, and get hands-on with developing a small KMM application. Finally, you'll discover expert tips and best practices, along with production- and adoption-related questions, that will help you take the next step in your project and career.

By the end of this Kotlin book, you'll have gained a solid understanding of the capabilities of KMM and be able to share code between Android and iOS flexibly.

Publication date:
March 2022
Publisher
Packt
Pages
184
ISBN
9781801812580

 

Chapter 1: The Battle Between Native, Cross-Platform, and Multiplatform

The proliferation of smartphones has led to the development of a large number of applications, making app development an important field. Because the same service in the form of an application needs to be developed on multiple platforms, various technologies, in addition to native solutions, have started to arise – first, cross-platform and now multiplatform. These technologies have been developed mainly to cut costs and make the application development process more efficient.

We'll start by learning about the issues with native development, why cross-platform can solve some of these issues with compromises, and how multiplatform can be a better solution. Also, if you're at the start of your career, we'll dive into why it may be better to focus on a multiplatform technology stack, rather than a cross-platform technology stack. I realize that this chapter is quite theoretical, but I encourage you to bear with me – it should help you build up that dopamine release, which is going to be paramount in the following chapters to maximize your learning.

In this chapter, we're going to cover the following topics:

  • Understanding the compounding costs of native development
  • Exploring the pitfalls of cross-platform solutions
  • Adopting a multiplatform approach
 

Understanding the compounding costs of native development

The manufacturer of every platform or operating system provides a software development kit (SDK), which contains everything necessary for someone to develop applications on that specific platform or OS. Here, we are referring to a native development process, where someone uses that SDK to develop applications for that single platform.

Cross-platform frameworks have a separate SDK, which is usually a layer on top of a native SDK.

Cross-platform solutions are becoming more and more popular; for example, as of May 2021, out of ~5 million apps on the Google Play Store, more than 200,000 are Flutter-based apps, which is not bad for fairly new technology (4-6% of all the apps published in Google Play Store).

For a more detailed look at some of Google Play's statistics, visit https://www.appventurez.com/blog/google-play-store-statistics.

If you are interested in learning a bit more about Flutter-based applications, check out https://www.youtube.com/watch?v=a553D0s7HeE&t=1779s.

To understand why there is an increasing demand for cross-platform solutions, we need to understand what issues people face with native development.

One of the reasons for the increase in demand for cross-platform solutions is developer convenience. Becoming an expert nowadays, and especially staying one, in any programming language or framework is not an easy job. While transferring concepts and general knowledge can be achieved in varying degrees, depending on the similarity between two platforms, becoming an expert in a new language still requires learning. Therefore, those people who'd like to become an all-around frontend developer with considerable expertise in Android, the web, and iOS have to learn not only three different frameworks but their primary languages as well: Swift/Obj-C, Kotlin/Java, and JavaScript. This is the case unless there is a shortcut providing passage between these worlds, which is generally covered by cross-platform technologies.

This explains the openness developers have toward using cross-platform, but it's only one part of the equation – the supply – and we still need demand for it.

There is another major reason for developers steering away from native solutions: cost.

The cost of native app development

So, why doesn't everyone want to go with a native development process? This choice is somewhat similar to buying tailored garments versus ready-made garments from clothing stores: it's cheaper.

Before we understand the costs associated with Native development, let's introduce the concept of nativeness first. Nativeness is a measure of the degree to which the quality of a product conforms with the peculiarities of a platform. For example, imagine the differences between a native English speaker and a non-native one (potentially the author of this book, who has an imperfect Hungarian accent). The differences can range from subtle to more obvious, based on the complexity of the words and the non-native speaker's skills.

Now, why would anyone give up nativeness? It's mainly because people can achieve lower costs (or at least they think they can).

To get a better picture of the cost variance between native, cross-platform, and multiplatform, we're going to examine the relationship between a feature's complexity and the development cost that's needed to bring it to life in a simplistic manner. Features can consist of multiple sub-features. So, for example, a delivery app can be thought of as an app with one delivery feature, where the complexity of this feature is the sum of all of its sub-features.

In the case of native development, since there is little to no cost reduction, the cost of development is determined as follows:

Cost of development (n) = n * FC

Here, n is the number of platforms and FC is the feature complexity, which, as we mentioned earlier, is the sum of all the sub-features that comprise a feature.

Important Note

This and the following calculations are approximations and only describe the reality simplistically. Nevertheless, they should provide you with a better picture to understand the cost differences between frameworks.

This is what costs would look like if you were developing a product on two (blue line) and three (red line) platforms, respectively, where there is no cost reduction by sharing code:

Figure 1.1 – Cost of native development as a function of feature complexity

Unfortunately, there is a little beast known as synchronization between platforms that we didn't take into account, which can significantly increase development costs, bit by bit; it's hard to plan for it, so it can be an unknown variable in calculations.

Synchronization

What is the specialty of frontend and mobile products in general? They are mostly similar, though they do have some differences. Thus, the goal of developers is to achieve consistency between platforms while paying attention to nativeness. This is a lot harder than it sounds. Why? There are a couple of reasons for this:

  • People think differently.
  • Platforms are different (an option that's easy to implement on iOS may not even be available to Android).
  • Creating software requirements documentation that covers everything is impossible.
  • Communication is costly, but no communication is costlier.

Because people think differently and are biased, communication is not easy, and platforms can drive developers toward different solutions, platform-native apps will likely have differences. As feature complexity increases, implementations will likely begin to differ more and more, causing greater and greater differences between platforms. Due to this, the costs of synchronization will compound. At some point, the development team will have to account for the differences between the implementations on the platforms as well.

Taking the synchronization costs into account, we could update our cost of development calculation as follows:

Cost of development (n) = n * FC + Sync Costs ^ FC

Here, n is the number of platforms and FC is the feature complexity.

Synchronization costs typically depend on your team's processes and its ability to communicate. The following chart provides an example of how synchronization in the native world could increase your costs significantly as feature complexity grows:

Figure 1.2 – The cost of native development with and without synchronization costs as a function of feature complexity

Here, we can see why the outlook of costs scares clients and directs them toward cross-platform solutions. But do cross-platform technologies save costs? Yes, though not in all cases and they may lure you into traps.

 

Exploring the pitfalls of cross-platform solutions

The main objective of cross-platform technologies is to allow you to write code that can be used across platforms (Android, iOS, and the web). Due to this, you don't have to write separate code for the same feature multiple times, depending on the platform; the cross-platform framework will provide the tools for you to interpret this code and translate it into platform-specific versions. The power of the cross-platform framework depends on those tools that interpret and translate the cross-platform code into platform-specific code.

Let's learn what the assumed cross-platform development costs are and what you should know about cross-platform in general to avoid some common pitfalls.

Assumed cross-platform development costs

People often estimate cross-platform product costs by cutting the costs that are needed for native apps in half (or even into three, if there is a possibility of deploying the cross-platform app on the web too).

Under this assumption, our formula becomes as follows:

Cost of development (n) = FC

Here, n is the number of platforms and FC is the feature complexity.

Let's compare this to the costs of native development:

Figure 1.3 – The cost of native development versus the cost of cross-platform development as a function of feature complexity

Looking at the preceding diagram and keeping the aforementioned assumption in mind, no wonder there is an increasing demand for cross-platform solutions.

Though this assumption may hold for greenfield projects, this probably won't be the case for real-world projects. To understand this, let's go over some of the currently available cross-platform technologies and how they work. We will review two of the most popular cross-platform frameworks: React Native and Flutter.

React Native

React Native is an open source framework for developing mobile applications. It is based on the React library and converts React components and JavaScript code into native Android and iOS components. For example, a Text component in React Native will be converted into a UITextView component on iOS and a TextView component on Android. This sounds like a good approach and it is a plausible one, especially for developers coming from the Web/React world. But how does this conversion work and what are the tradeoffs and risks of React Native development?

React Native creates a thread where it runs the respective JavaScript code, which communicates with the native code by running on the traditional main thread, through a bridge that asynchronously sends serializable data:

Figure 1.4 – The architecture of React Native

Going back to our example, when a UIView or TextView is clicked in the native component, the appropriate data is then sent through the bridge to the JavaScript code, and then back again. Now, if you're thinking about the performance costs of this bridge mechanism, then you're in the right place. Let's look at the drawbacks of React Native:

  • Performance: It's not native, especially for resource-intensive features.
  • New features support: Because you're relying on React Native to provide support for new things, you can expect a bit of a delay.

There are also some application development specifics, such as permissions, notifications, in-app purchases, and media where you'd like more control over the native platform's API. In those cases, React Native lets you create native modules in regular native code, though it's not the primary purpose of the framework. If you arrive at a point where you need a native module, which is likely unless you have a really simple app, you will face the following issues:

  • As a Developer: If you planned to reuse your JavaScript and/or React knowledge to create mobile applications, you will have to acquire native mobile development skills anyway.
  • As a Client: Every roadblock that pushes you toward implementing a native module means higher costs than writing the same feature with native solutions, simply because there is a need for native expertise. Plus, it has to be integrated with React Native as well.

We'll update our charts and calculations in a moment, but first, let's check out Flutter.

Flutter

Flutter is an open source UI software development kit that's developed by Google and used for developing cross-platform applications. It has three layers from an architectural perspective – the framework, the engine, and the platform – and relies on Dart's language specifics, such as ahead-of-time compilation.

As a developer, you interact with the framework and you write the app and the widgets (UI components in Flutter) in a declarative way using Dart, which the engine then renders to a canvas called Skia Canvas. This canvas is then sent to the native platforms: Android, iOS, or the web. The native platform will show the canvas and send the occurring events back:

Figure 1.5 – The architecture of Flutter

Flutter's architecture may be similar to React Native, but there is a big difference in terms of performance. One key component of how Flutter achieves better performance than React Native is by going one level lower on the native side, meaning that it doesn't use the traditional SDKs that are used by native developers. Instead, it uses SDKs that need more developer expertise and can offer higher performance. Flutter uses Android's Native Development Kit (NDK) and iOS's Low-Level Virtual Machine (LLVM) to compile the C/C++ code coming from the engine.

While Flutter has pretty good performance compared to native and is far better than React Native when it comes to compiling the Dart code into a lower level native code (a key performance component), it also has a drawback: the cost of writing native code with Flutter is higher than using React Native to do the same.

At the time of writing, if you don't have support for a certain piece of functionality in the Flutter framework itself, you can write regular Java/Kotlin and Obj-C/Swift code, but you'll have to communicate with the Dart code through a channel, sending data through Map in Dart, HashMap in Java/Kotlin, and Dictionary in Swift. If we compare this to the regular Java <-> Kotlin or Obj-C <-> Swift interoperability, this can be perceived more as a workaround than a scalable solution.

Important Note

Both the Flutter and the React Native descriptions only serve as high-level overviews to help you understand how cross-platform solutions are designed and what to expect when you're working with them. To get a clearer picture, please read the official documentation.

To conclude our cross-platform overview, let's summarize the patterns that we observed in the aforementioned frameworks and see how we can update the general assumption of cross-platform solutions when it comes to estimating the costs of development.

The main ideology of cross-platform technologies is that you write the same code for Android and iOS (and the web); the framework provides the tooling to interpret this code and translate it into the platform-specific version.

While they do provide solutions for writing native code where needed, they are suboptimal and the goal of any cross-platform project is to avoid situations where interoperability with native code is needed.

This way, you rely heavily on the framework to make good decisions on your behalf when you're translating the cross-platform code into the platform-specific version. In short, all of these frameworks have, or will have, a tough time keeping up-to-date with both Android and iOS, two platforms that don't have an incentive to stay in sync with each other.

So, unless you plan on accepting big compromises, your cost of maintaining an acceptable level of nativeness will be relatively high with any cross-platform solution.

Actual cross-platform technology costs

Going back to our initial cross-platform costs assumption, we can update our formula with a new variable:

Cost of development (n) = FC * (1 + Cost of going Native)

Here, n is the number of platforms and FC is the feature complexity.

The Cost of going Native can depend on a variety of things:

  • How much interoperability the cross-platform technology provides with native. We've seen that this isn't optimal with neither of the aforementioned technologies.
  • The knowledge gap between the cross-platform and native languages. You'll likely observe that expertise hardly translates from cross-platform to native.
  • The more you need to dive into native implementations, the more your costs will compound because synchronization costs will kick in for the native code as well.

For visualization purposes, a more likely scenario of the costs associated with cross-platform development could look like this:

Figure 1.6 – Cost of cross-platform development with potential roadblocks as a function of feature complexity

Again, the number of roadblocks you'll hit heavily depends on how much you're willing to compromise from nativeness and how much you're relying on platform-specific APIs.

To conclude, if I were to write a project for myself, I'd consider Flutter. If it is a simple project where I don't have to cover any platform-dependent stuff (permissions, notifications, in-app purchases), just basic CRUD operations with a backend, a local database, and some nice UI stuff, then I'd probably go with Flutter. Otherwise, I'd use a native solution. Knowing how platform-specific things such as permission handling change on Android, I wouldn't dare trust a third-party framework to keep up-to-date.

That being said, cross-platform will probably still attract many start-ups in the future, due to the nature of start-ups accepting higher compromises to survive and achieve their short-term financial goals or to be product-market fit, which requires moving fast. However, there is another option: the multiplatform approach. This is cost-friendly both long and short term, and it is a sane approach from all perspectives.

 

Adopting a multiplatform approach

We have finally arrived at one of my favorite topics. In this section, we'll explore how multiplatform works, why it's different from cross-platform technologies, and its cost implications.

The multiplatform approach

As we mentioned previously, cross-platform technologies generally try to take on the "burden" of dealing with platform-specifics; thus, their main goal is to help facilitate application development without having to deal with platform-specific decisions. This has two implications – interoperability with the native platform is not the primary scope of these technologies and (partly because of this) the framework needs to do most of the heavy lifting when it comes to making platform-specific decisions.

To overcome these issues, another approach is needed. Kotlin Multiplatform (KMP), a multiplatform solution, introduces a paradigm shift. It recognizes both the need for flexible platform-specific decision making and keeping up to date with different platforms, where these two things go hand-in-hand.

Its aim is not to provide a wrapper layer over the native platforms, but to be a handy tool in the native development palette, which can help with sharing non-platform-specific code such as the business logic.

You may be wondering why understanding the ideology of a framework would be important for you. There are a couple of reasons, as follows:

  • You become more aligned with a framework, and you'll know when something goes against the framework's design.
  • You'll be able to manage your expectations regarding the framework's future direction better.

The main objectives of KMP are as follows:

  • Keeping the native part of development as close to the regular native development process as possible.
  • Ensuring that native developers do not find it difficult when they're writing the shared code.
  • Facilitating interoperability between native and shared code; interacting with shared code should be as close to native-like as possible.

Now, let's take a deeper look into how KMP can empower you to write platform-agnostic code and share that between different platforms.

How KMP works

KMP allows you to write code in Kotlin in a platform-agnostic way and share that code between different platforms, all while leveraging the native programming benefits.

The Kotlin ecosystem contains three main compilers – Kotlin/JVM, Kotlin/JS, and Kotlin/Native (we will cover them in more depth in Chapter 2, Exploring the Three Compilers of Kotlin Multiplatform):

Figure 1.7 – The architecture of KMP

Note

Most developers know Kotlin through the lens of Kotlin/JVM. This is because of Kotlin's reliable interoperability with Java. It has a wide and quickly growing adoption rate in the Android community, but server-side development with Kotlin has also been becoming more and more popular in recent years.

In essence, Kotlin's interoperability power depends on how well these three compilers work with the respective platforms. For Android, we can consider that the interoperability cost with KMP is zero since Kotlin/JVM is part of the Android developer ecosystem. As for iOS (and potentially the web), the costs depend on how well the Kotlin/Native (and Kotlin/JS) compiler works. We will look at this in more detail in the next chapter.

Shared code must be platform-agnostic, which means the code shouldn't contain any JVM, JavaScript, iOS, or any other platform-specific references. For example, working with Date and Time is platform-specific and has different dependencies on iOS than on JVM (or Android). Don't worry – a lot of these use cases are already covered in libraries that have been developed by either the Kotlin community or the JetBrains team.

Now, let's learn how to leverage KMP's capabilities to write platform-agnostic code that will use the proper platform-specific dependencies on the different target platforms, in case you bump into any uncovered use case from the community.

Platform abstractions (expect-actual)

This mechanism is one of the cores of the whole KMP technology. In a lot of cases, when you're writing shared code, you need a way to define how certain functionality should be implemented on the specific native platforms.

Note

Going forward, we will use the terms shared code and common code interchangeably, both of which refer to code written in a platform-agnostic way and that could be seamlessly compiled with one of the Kotlin compilers to the chosen targets.

As you'll see, this could mean being platform-agnostic across Kotlin/Native and Kotlin/JVM only, depending on what platforms you target.

With KMP, you can write expected declarations using the expect keyword in your shared code, which will have an actual implementation for every platform that you specify. Let's look at an example of how to share code between Android and iOS with this mechanism.

Let's say you have an application where users can upload certain files to the cloud and you'd like to share this part of your networking layer. Since file handling is something platform-specific, you'll need to create some abstractions for this (or potentially check if it's already covered in a library).

First, you would declare the expected functionality in your shared code; in our case, we'll need any file to be converted into a byte representation of the file so that we can send it to the backend:

expect class File {
    fun toByteArray(): ByteArray
}

Don't worry much about the syntax; the important part is the expect/actual mechanism.

To make KMP able to substitute the expected implementations with the actual implementations on the different platforms, we need to provide those as well:

// JVM
actual class File(private val file: java.io.File) {
    actual fun toByteArray() = file.readBytes()
}

As you can see, for JVM/Android, we are just wrapping the java.io.File platform-specific implementation. There is a better way to do this using type aliases, which we'll cover in Chapter 5, Writing Shared Code.

For iOS/React Native, the implementation could look like this:

// iOS/Native
actual class File(private val fileHandle:
 platform.Foundation.NSFileHandle) {
actual fun toByteArray() = 
  with(fileHandle.readDataToEndOfFile()) {
      memScoped {
          ByteArray(length.toInt()).apply {
              usePinned {
                  memcpy(it.addressOf(0), bytes, length)
              }
          }
      }
  }
}

As you can see, in the native implementation, you can use the Foundation Kit; there is also a way to include CocoaPods dependencies, which we will also cover in Chapter 5, Writing Shared Code.

At this point, KMP will compile the shared code for two different targets (Kotlin/JVM and Kotlin/Native) with the two different compilers and replace all the expected declarations with their actual implementations on the specific platform.

I can't emphasize enough how important this mechanism is for multiplatform; this is what enables the bridge between different platforms and provides the scalability for the whole platform so that outside contributors can easily build upon the current solutions.

Next, I'm going to touch on a little tool that we're going to cover in more depth in Chapter 2,Exploring the Three Compilers of Kotlin Multiplatform, which helps out tremendously with actual implementations – the commonizer.

This tool automates the process of the expect/actual declaration and generates the expect/actual declarations for us. However, this tool was designed specifically for cases where targets (such as macOS and different iOS architectures) have very similar dependencies (such as the POSIX library on OS X and Linux).

Now that we have a bit of an understanding of the KMP framework and how it enables developers to share code, let's see what it can be used for and how it could help out in a regular development process.

The different use cases for KMP

The KMP framework is unopinionated about what you use it for. Its main goal is to help you share code between multiple target platforms with as good interoperability as possible.

This means that the possible combination of potential use cases is close to infinite. You can play around with the amount of code you plan on sharing and the targets you'd like to share the code between. You can also scale it later on in the process because you can add other target platforms and migrate more and more code to your common part as your project develops.

You can go from having 1% shared code to sharing your UI layer – the only blocking thing will be your sense of what needs to stay platform-specific.

With this in mind, let's check out some of the most common use cases.

Kotlin Multiplatform Mobile (KMM)

You may have heard about Kotlin Multiplatform Mobile (KMM) and perhaps you've been wondering what the difference is between KMP and KMM; allow me to shed a bit of light on this topic.

Technology-wise, KMM is a specific use case, whereas KMP is used for sharing code between mobile targets – Android and iOS.

KMM was introduced when JetBrains realized that this concept is, at the time of writing, one of the main use cases for developers choosing KMP to share code. Hence, a dedicated KMM team was formed and special tooling was introduced to help support this cause:

Figure 1.8 – Kotlin Multiplatform Mobile in the Kotlin Multiplatform technology

In KMM, your code-sharing capabilities will largely depend on two of the Kotlin compilers: Kotlin/JVM and Kotlin/Native. To grasp the limits of what's capable when working with these compilers, we dedicate Chapter 2, Exploring the Three Compilers of Kotlin Multiplatform to this so that you can know what to expect and how to get the most out of both the Kotlin/JVM and Kotlin/Native compilers.

As we've already mentioned, you can start with any level of code sharing, but here are some examples:

  • A Small Part of the Code Base: Kevin Galligan would say to choose one of the parts that's not so fun to work on, such as analytics.
  • Networking Layer or Persistence Layer: This is still a relatively small amount of the code base and it can reduce some of the synchronization costs.
  • The Entire Data Layer: Managing offline support and syncing logic consistently on two different platforms can be a burden, so it can be worth doing this for certain apps.
  • View/Presentation Layer: This can be done, but things get a bit more platform-specific here. This is also where the line between cross-platform and multiplatform starts to get a bit blurry.

You can start going from only a small part of the code base and then bring more and more layers and/or features as you gain more confidence working with KMP.

Another nice benefit of KMM is that it doesn't change the native development cycle radically. Instead, it builds upon it, with KMP being more of an additional tool in the existing palette.

Going forward, this use case is going to be the main focus of this book, but we will briefly explore other potential use cases so that you can get a better picture of what code-sharing possibilities you have with KMP.

Code sharing between frontend applications

You can do this gradually as well, going from a KMM app to sharing logic between all the different frontend platforms you plan on supporting.

Since your current shared code is already based on working with the Kotlin/JVM and Kotlin/Native compilers, adding support for all the different desktop targets such as macOS, Windows, and Linux is relatively easy and largely depends on how well you manage the non-shared part of your code.

A slightly bigger step is to bring the Kotlin/JS compiler into play and share code with your web app through a JS target.

The complexity of this depends on the interoperability power of Kotlin/JS and how well you can work with it.

Code sharing between backend and frontend applications

Another interesting use case of KMP is sharing code between your backend and frontend applications.

In most real-world projects, there is a limited amount of implementation overlap between backend and frontend apps, so this is why it doesn't get much focus from cross-platform solutions.

Nevertheless, there is always a piece of the backend that would be awesome to share. I've had the chance to experience minor modifications that broke the frontend apps, and also remember doing Git history research to understand why there are differences in the way frontend platforms use the backend APIs.

Yes, you can minimize these human errors with carefully designed processes, but enforcing the process itself can be another challenge.

I think that sharing DTOs, API keys, and other useful information, such as base URLs, can speed up development, especially in the long term. Just think about a continuous integration (CI) pipeline, where if a backend modification breaks the builds on the apps, it's immediately visible to the backend team.

I think that the combination of use cases is huge, and as a developer, I would start getting more and more into this world that KMP offers. The whole approach offers a new perspective on how we think about developing apps and introduces a new potential team composition:

  • Platform Experts: Developers with native Android, iOS, web, or other platform expertise
  • Shared Code Experts: The ones who maintain the shared logic and know the ins and outs of KMP

JetBrains had already started experimenting with this setup while developing their Space product and as KMP expertise spreads, I suspect we will see even more people follow.

Now, let's close this chapter by talking about the cost implications of a multiplatform approach.

KMM cost implications

At this point, you hopefully understand the differences between cross-platform, native, and multiplatform. The latter is in-between a native and cross-platform solution, where you remain with your native platform development cycle but enhance it with code sharing capabilities where it makes sense to.

So, how would you calculate the costs for a multiplatform project? It should have native costs for your non-shared code and cross-platform costs for shared code, except that you don't face roadblocks with KMP, with interoperability being much better than it is with cross-platform solutions.

In the case of a real roadblock, you can just decide on not sharing that part of the code so that your interop costs will be diminishing relative to those of cross-platform solutions.

Based on this reasoning, a possible calculation of KMP costs could look like this:

Cost of development (n) = FC * [n * (1 - α) + α]

Here, n is the number of platforms, FC is the feature complexity, and α ∈ [0,1] represents the amount of shared code (1: all the code is shared, 0: no code is shared).

Note that in this case, we don't include any synchronization costs. This is because KMP, when done right, should eliminate the situations where synchronization costs could occur; thus, the non-shared amount of code should be a representation of the platform-specific code that's not worth sharing.

Of course, since KMP is a relatively newborn platform, the aforementioned ideal scenario probably won't manifest for every use case, though it is approachable. To grasp what this cost calculation means, check out the following chart:

Figure 1.9 – Cost of KMP development versus other options as a function of feature complexity

As you can see, as the costs of development increase, cross-platform solutions can be a good choice for short-term, quick projects. But in the long term, KMP is going to be the winner.

Important Note

I'm going to emphasize again that this is a simplistic estimation of costs and that the preceding chart is a representation of a fabricated (though possible) scenario of project development.

Because estimating real-world projects with generic calculation logic and from the perspective of the different technologies is a hugely complex task, this should be enough reasoning as to why this simplistic approach was taken.

Nevertheless, I'm confident that this simplistic approach can provide a good overview of the costs of the different technologies.

Please note that an important aspect is missing from this chart – having an even better view that shows another dimension of the quality would be required to have complete reasoning on the technologies.

We won't dive deeper into this topic, but I'd reason about my technology choices in the following manner:

  • Are quality and nativeness paramount for my project? If the answer is yes, go as native as possible.
  • If both quality and costs are important and you're looking for the highest quality/cost ratio, then go with multiplatform. Note that KMP is applicable for the first scenario as well since it offers gradual code sharing; hence, if you only find out that sharing something affects your quality during the process, you can revert and go fully native for that feature. The upside is that you'll cut a lot of the costs.
  • Cross-platform is the most cost-efficient option, but it is likely to require compromises.
 

Summary

At this point, you should have a better understanding of the different technologies for mobile development and their cost-effectiveness. I also hope that you've become eager to learn more about Kotlin and KMP.

The purpose of this chapter was to provide a good overview of why KMP is different from other cross-platform technologies, why it can be a good career choice for developers, and why it would make sense to choose it for a project.

Now that you've had that dopamine pump, let's dive into the more technical aspects and check out how the Kotlin compilers work and how you can leverage their power.

About the Author

  • Róbert Nagy

    Róbert Nagy is a Senior Android Developer at Octopus Energy. He is an Android and Kotlin developer with a Bachelor of Science in Computer Science. He has designed, developed, and maintained multiple sophisticated Android apps ranging from 100K+ downloads to 10M+ in the financial, IoT, health, social, and energy industries. Some projects that he has been a part of include a social platform for kids, a lightning system controller, and Bloom and Wild.

    Browse publications by this author
Simplifying Application Development with Kotlin Multiplatform Mobile
Unlock this book and the full library FREE for 7 days
Start now