Skip to main content

Kotlin Multiplatform: Building Apps for iOS and Android

· 13 min read
Engineering Team

Use Kotlin Multiplatform to share domain and data logic while choosing shared or native UI based on product and team constraints. This revised guide emphasizes supported APIs, production tradeoffs, and an upgrade-friendly path without tying the advice to a calendar year.

kotlin multiplatform mobile building apps ios android

Modern implementation baseline

Use Kotlin Multiplatform to share domain and data logic while choosing shared or native UI based on product and team constraints.

Use modern Kotlin language features, structured concurrency, lifecycle-aware state, and explicit boundaries between UI, domain, and data layers. For Android UI, prefer Compose unless an existing View-based surface has a clear reason to remain.

  • Start with a small shared module and explicit interfaces.
  • Keep platform capabilities behind expect/actual or injected adapters.
  • Design Swift-facing APIs deliberately and test both targets.

Production checklist

  • Pin compatible Kotlin, Gradle, and Android plugin versions.
  • Test cancellation, process recreation, offline behavior, and error recovery.
  • Keep platform-specific code behind small interfaces.

Authoritative references

Introduction

What is Kotlin Multiplatform (KMP)?

Kotlin Multiplatform (KMP) is a framework that enables developers to write shared code in Kotlin and use it across multiple platforms, including iOS and Android. With KMP, developers can save time and effort by reusing code and business logic between different mobile platforms, reducing the need for separate codebases. KMP provides a seamless integration between Kotlin and native platform-specific code, allowing developers to leverage the full power of each platform while maintaining code consistency.

Advantages of using KMP

There are several advantages to using Kotlin Multiplatform for building mobile apps:

  1. Code Reusability: KMP allows developers to write shared code in Kotlin that can be used across multiple platforms, eliminating the need for separate codebases for iOS and Android. This significantly reduces development time and effort.

  2. Native Performance: KMP provides a seamless integration between Kotlin and native platform-specific code, allowing developers to leverage the performance and capabilities of each platform. This ensures that the apps developed with KMP have native-like performance and user experience.

  3. Easy Maintenance: With KMP, developers can maintain a single codebase for both iOS and Android, reducing the complexity and effort required for maintenance. Changes and updates can be implemented once and applied to both platforms simultaneously.

  4. Community Support: Kotlin Multiplatform is backed by JetBrains, the creators of Kotlin, and has a vibrant and growing community of developers. This ensures that developers have access to resources, libraries, and support when building apps with KMP.

Comparison with other cross-platform frameworks

Kotlin Multiplatform offers several advantages over other cross-platform frameworks like React Native, Flutter, and .NET MAUI:

  • React Native: While React Native also allows code sharing between iOS and Android, it uses JavaScript, which can lead to performance and integration issues. KMP, on the other hand, uses Kotlin, a statically-typed language that seamlessly integrates with native platform-specific code.

  • Flutter: Flutter uses its own UI framework, which requires learning a new set of widgets and UI concepts. KMP, on the other hand, allows developers to build the UI using the native platform-specific UI frameworks, such as SwiftUI for iOS and Jetpack Compose for Android.

  • .NET MAUI: .NET MAUI fits teams invested in C# and the Microsoft ecosystem, while Kotlin Multiplatform fits teams that want Kotlin-based shared logic and direct access to native platform APIs.

Setting Up KMP

Installing Kotlin and KMP plugin

Before getting started with KMP, you will need to install Kotlin and the KMP plugin in your development environment. Follow the steps below to set up your environment:

  1. Install Kotlin: Kotlin can be installed using the Kotlin command-line compiler (kotlinc) or by installing the Kotlin plugin for your IDE. Visit the official Kotlin website for detailed installation instructions.

  2. Install KMP Plugin: Install the KMP plugin for your IDE. The plugin provides support for creating and managing KMP projects. Consult the documentation of your IDE for instructions on installing plugins.

Creating a new KMP project

Once you have installed Kotlin and the KMP plugin, you can create a new KMP project. Follow the steps below to create a new KMP project:

  1. Open your IDE and select the option to create a new project.

  2. Choose the Kotlin Multiplatform template and provide a name for your project.

  3. Configure the project settings, such as the project location and platform-specific configurations.

  4. Click "Finish" to create the project.

Configuring shared and platform-specific code

After creating the project, you will have a shared module that contains the shared code and platform-specific modules for iOS and Android. The shared module is where you will write the shared business logic, while the platform-specific modules are where you will write code specific to each platform.

To configure the shared and platform-specific code, follow these steps:

  1. Open the project structure or settings in your IDE.

  2. Navigate to the module settings and select the shared module.

  3. Add the desired platform-specific modules as dependencies to the shared module.

  4. Configure the build settings for each module to include the shared code.

Developing for iOS

Creating iOS-specific UI

To create iOS-specific UI in your KMP project, you can use SwiftUI, Apple's declarative UI framework. SwiftUI allows you to define the UI using a concise and expressive syntax. Here's an example of creating a simple SwiftUI view in your KMP project:

import SwiftUI

class ContentView: View {
var body: some View {
Text("Hello, iOS!")
.font(.largeTitle)
.foregroundColor(.blue)
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

In this example, we define a ContentView class that conforms to the View protocol. The body property defines the UI of the view, which consists of a Text element displaying the text "Hello, iOS!". We also set the font size to .largeTitle and the text color to .blue.

The ContentView_Previews struct provides a preview of the view, allowing you to see how it will look in different environments.

Accessing iOS frameworks and APIs

KMP provides seamless integration with iOS frameworks and APIs, allowing you to access platform-specific functionality in your shared code. To access iOS frameworks and APIs, you can use the expect and actual declarations.

Here's an example of accessing the UIKit framework in your shared code:

expect class UIApplication {
fun openURL(url: NSURL)
}

actual class UIApplication {
actual fun openURL(url: NSURL) {
// Call the iOS-specific implementation
UIApplication.shared.openURL(url)
}
}

In this example, we declare an UIApplication class with an openURL function using the expect keyword. This declares the function signature in the shared code.

In the actual implementation, we provide the platform-specific implementation for iOS using the actual keyword. In this case, we call the openURL function of the UIApplication singleton.

Testing and debugging on iOS devices

To test and debug your KMP app on iOS devices, you can use the Xcode IDE. Follow these steps to test and debug your app on an iOS device:

  1. Connect your iOS device to your computer using a USB cable.

  2. Open the Xcode project for the iOS module of your KMP project.

  3. Select your iOS device as the target device in Xcode.

  4. Build and run the app on your iOS device.

You can use the Xcode debugger to set breakpoints, inspect variables, and step through your code for debugging purposes.

Developing for Android

Creating Android-specific UI

To create Android-specific UI in your KMP project, you can use Jetpack Compose, Google's modern UI toolkit for building native Android apps. Jetpack Compose allows you to define the UI using a declarative syntax. Here's an example of creating a simple Jetpack Compose UI in your KMP project:

import androidx.compose.foundation.Text
import androidx.compose.foundation.layout.Column
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.setContent
import androidx.compose.ui.tooling.preview.Preview

@Composable
fun ContentView() {
MaterialTheme {
Column {
Text(text = "Hello, Android!", style = MaterialTheme.typography.h1)
}
}
}

@Preview
@Composable
fun ContentViewPreview() {
ContentView()
}

In this example, we define a ContentView function using the @Composable annotation. The function defines the UI of the view using Jetpack Compose components. In this case, we have a Text component displaying the text "Hello, Android!" with a heading style.

The ContentViewPreview function provides a preview of the view using the @Preview annotation.

Accessing Android frameworks and APIs

KMP allows you to access Android frameworks and APIs in your shared code using the expect and actual declarations. Here's an example of accessing the SharedPreferences API in your shared code:

expect class SharedPreferences {
fun getString(key: String, defaultValue: String): String?
fun putString(key: String, value: String)
}

actual class SharedPreferences {
actual fun getString(key: String, defaultValue: String): String? {
// Call the Android-specific implementation
return PreferenceManager.getDefaultSharedPreferences(context).getString(key, defaultValue)
}

actual fun putString(key: String, value: String) {
// Call the Android-specific implementation
PreferenceManager.getDefaultSharedPreferences(context).edit().putString(key, value).apply()
}
}

In this example, we declare a SharedPreferences class with getString and putString functions using the expect keyword. This declares the function signatures in the shared code.

In the actual implementation, we provide the platform-specific implementation for Android using the actual keyword. In this case, we call the corresponding SharedPreferences methods using the PreferenceManager class.

Testing and debugging on Android devices

To test and debug your KMP app on Android devices, you can use Android Studio. Follow these steps to test and debug your app on an Android device:

  1. Connect your Android device to your computer using a USB cable.

  2. Open the Android module of your KMP project in Android Studio.

  3. Select your Android device as the target device in the device selector.

  4. Build and run the app on your Android device.

You can use the Android Studio debugger to set breakpoints, inspect variables, and step through your code for debugging purposes.

Sharing Code

One of the key benefits of Kotlin Multiplatform is the ability to share code between different platforms. In this section, we will explore how to write shared business logic, share data models and networking code, and handle platform-specific dependencies.

Writing shared business logic

To write shared business logic in your KMP project, you can create shared Kotlin classes and functions that can be used by both iOS and Android. Here's an example of a shared Calculator class that performs basic arithmetic operations:

class Calculator {
fun add(a: Int, b: Int): Int {
return a + b
}

fun subtract(a: Int, b: Int): Int {
return a - b
}
}

In this example, we define a Calculator class with add and subtract functions that perform addition and subtraction operations. This class can be used by both iOS and Android code.

Sharing data models and networking code

KMP allows you to share data models and networking code between iOS and Android. Here's an example of a shared User data class and a shared networking function that fetches user data:

data class User(val id: String, val name: String, val email: String)

expect suspend fun fetchUser(id: String): User

actual suspend fun fetchUser(id: String): User {
// Perform the networking request and parse the response
val response = httpClient.get("https://api.example.com/users/$id")
return response.toUser()
}

In this example, we declare a User data class using the data class keyword. This class represents a user object with id, name, and email properties.

We also declare a fetchUser function with the expect keyword. This function fetches user data from a remote API and returns a User object.

In the actual implementation, we provide the platform-specific implementation for iOS and Android. In this case, we use the httpClient library to make the network request and parse the response.

Handling platform-specific dependencies

KMP allows you to handle platform-specific dependencies by using the expect and actual declarations. Here's an example of handling a platform-specific database dependency:

expect class Database {
fun saveData(data: String)
}

actual class Database {
actual fun saveData(data: String) {
// Call the platform-specific database implementation
iOSDatabase.saveData(data)
}
}

In this example, we declare a Database class with a saveData function using the expect keyword. This declares the function signature in the shared code.

In the actual implementation, we provide the platform-specific implementation for iOS and Android. In this case, we call the saveData function of the iOSDatabase singleton.

Building and Deploying

Once you have developed your KMP app for iOS and Android, you can build and deploy it to the respective app stores. This section will cover the process of building the iOS and Android apps and publishing them to the App Store and Google Play.

Building the iOS and Android apps

To build the iOS app, follow these steps:

  1. Open the Xcode project for the iOS module of your KMP project.

  2. Select the target device and build configuration.

  3. Click the "Build" button to build the app.

To build the Android app, follow these steps:

  1. Open the Android module of your KMP project in Android Studio.

  2. Select the target device and build configuration.

  3. Click the "Build" button to build the app.

Publishing to the App Store and Google Play

To publish your KMP app to the App Store and Google Play, follow the respective guidelines and processes provided by Apple and Google. You will need to create developer accounts, prepare the app for release, and submit it for review.

Ensure that you follow the platform-specific guidelines and requirements for app submission, including providing appropriate app descriptions, icons, screenshots, and compliance with store policies.

Continuous integration and delivery

To automate the build and deployment process, you can set up continuous integration and delivery (CI/CD) pipelines using tools like Jenkins, Travis CI, or GitHub Actions. These tools allow you to automatically build, test, and deploy your KMP app whenever changes are pushed to the repository.

By setting up CI/CD pipelines, you can ensure that your app is continuously integrated, tested, and deployed, reducing the manual effort required for building and deploying the app.

Conclusion

In this tutorial, we explored Kotlin Multiplatform (KMP) and its capabilities for building mobile apps for iOS and Android. We covered the advantages of using KMP, the setup process, developing for iOS and Android, sharing code, and building and deploying the apps.

With KMP, developers can leverage the power of Kotlin and native platform-specific code to build high-performance and maintainable mobile apps. The ability to share code and business logic between iOS and Android significantly reduces development time and effort, while the seamless integration with platform-specific frameworks and APIs ensures a native-like user experience.

Kotlin Multiplatform is a powerful tool for software developers looking to streamline the mobile app development process and maximize code reuse. By adopting KMP, developers can build cross-platform apps that are efficient, performant, and maintainable.