Kotlin Multiplatform Help

Create your Kotlin Multiplatform app

Here you will learn how to create and run your first Kotlin Multiplatform application using IntelliJ IDEA.

Kotlin Multiplatform technology simplifies the development of cross-platform projects. Kotlin Multiplatform applications can work on a variety of platforms like iOS, Android, macOS, Windows, Linux, web, and others.

One of the major Kotlin Multiplatform use cases is sharing code between mobile platforms. You can share application logic between iOS and Android apps and write platform-specific code only when you need to implement a native UI or work with platform APIs.

Create a project

  1. In the quickstart, complete the instructions to set up your environment for Kotlin Multiplatform development.

  2. In IntelliJ IDEA, select File | New | Project.

  3. In the panel on the left, select Kotlin Multiplatform.

  4. Specify the following fields in the New Project window:

    • Name: GreetingKMP

    • Project ID: com.jetbrains.greetingkmp

  5. Select Android and iOS targets.

  6. For iOS, select the Do not share UI option to keep the UI native.

  7. Once you've specified all the fields and targets, click Create.

Create Kotlin Multiplatform project

Examine the project structure

In IntelliJ IDEA, expand the GreetingKMP folder.

This Kotlin Multiplatform project includes the following modules:

  • androidApp is a Kotlin module that builds into an Android application. It uses Gradle as the build system. The composeApp module depends on and uses the shared module as a regular Android library.

  • iosApp is an Xcode project that builds into an iOS application. It depends on the sharedLogic module, which is exported as an iOS framework. Kotlin Multiplatform projects created using the IDE wizard use the regular framework dependency through direct integration.

  • sharedLogic is the multiplatform module that contains the logic common for both Android and iOS applications.

  • sharedUI is the module with Compose Multiplatform UI code: in this project, it is used only by the Android app, but it is a multiplatform module that can be used by other targets whenever you are ready for that.

Every module except for iosApp uses Gradle as the build system.

Basic Multiplatform project structure

Source set is a Gradle concept for a set of files logically grouped together where each set has its own dependencies. In Kotlin Multiplatform, different source sets in a shared module can target different platforms.

The sharedLogic module contains the androidMain, commonMain, and iosMain source sets. The commonMain source set contains shared Kotlin code, and platform source sets use Kotlin code specific to each target. Kotlin/JVM is used for androidMain and Kotlin/Native for iosMain:

Source sets and modules structure

When the shared module is built into an Android library, common Kotlin code is treated as Kotlin/JVM. When it is built into an iOS framework, common Kotlin is treated as Kotlin/Native:

Common Kotlin, Kotlin/JVM, and Kotlin/Native

Write common declarations

The common source set contains shared code that can be used across multiple target platforms. It's designed to contain code that is platform-independent. If you try to use platform-specific APIs in the common source set, the IDE will show a warning:

  1. Open the sharedLogic/src/commonMain/.../Greeting.kt file where you can find the generated Greeting class with a greet() function.

  2. Let's add a bit of variety to the greeting. Navigate to the definition of the sayHello() function in the GreetingUtil.kt file.

  3. Update the shared code with randomization and the reversed() call from the Kotlin standard library to reverse the received string:

    fun sayHello(to: String): String { val firstWord = if (Random.nextBoolean()) "Hi!" else "Hello!" return "$firstWord Guess what this is! > ${to.reversed()}!" }
  4. Import the kotlin.random.Random class following the IDE's suggestion.

Writing the code only in common Kotlin has obvious limitations because it can't use any platform-specific functionality. Using common interfaces with platform-specific implementations using the expect/actual mechanism solves this.

Check out platform-specific implementations

The common source set can define expected declarations — interfaces, classes, and so on. In each platform source set, in this case androidMain and iosMain, you have to provide actual platform-specific implementations for the expected declarations.

While generating the code for a specific platform, the Kotlin compiler merges expected and actual declarations and generates a single declaration with actual implementations.

  1. When creating a Kotlin Multiplatform project with IntelliJ IDEA, you get a Platform.kt file in the sharedLogic/src/commonMain module:

    interface Platform { val name: String }

    It's a common Platform interface supposed to include information about a platform.

  2. You can find the platform-specific classes that implement the interface in the androidMain and the iosMain source sets:

    // Platform.android.kt in the androidMain source set import android.os.Build class AndroidPlatform : Platform { override val name: String = "Android ${Build.VERSION.SDK_INT}" }
    // Platform.ios.kt in the iosMain source set import platform.UIKit.UIDevice class IOSPlatform: Platform { override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion }
    • The name property of the AndroidPlatform class uses the Android-specific code, namely the android.os.Build dependency. This code is interpreted as Kotlin/JVM. If you try to access a JVM-specific class, such as java.util.Random here, this code will compile.

    • The name property of the IOSPlatform class uses iOS-specific code, namely the platform.UIKit.UIDevice dependency. This code is interpreted as Kotlin/Native, meaning that you can refer to iOS declarations in Kotlin. The code becomes a part of the iOS framework, which is imported in the Swift code of the iosApp module.

  3. Each source set includes a getPlatform() function. Its expected declaration doesn't have a body, and actual implementations are provided in the platform code:

    // Platform.kt in the commonMain source set expect fun getPlatform(): Platform
    // Platform.android.kt in the androidMain source set actual fun getPlatform(): Platform = AndroidPlatform()
    // Platform.ios.kt in the iosMain source set actual fun getPlatform(): Platform = IOSPlatform()

Here, the common source set defines an expected getPlatform() function and has actual implementations, AndroidPlatform() for the Android app and IOSPlatform() for the iOS app, in the platform source sets.

When generating the code for a specific platform, the Kotlin compiler merges expected and actual declarations into a single getPlatform() function with the correct implementation.

That's why expected and actual declarations should be defined in the same package – they are merged into one declaration in the resulting platform code. Any invocation of the expected getPlatform() function in the generated platform code then refers to the correct actual implementation.

Now you can run the apps and see all of this in action.

Create an expect/actual variable (optional)

The template project uses the expect/actual mechanism for functions, but it also works for most Kotlin declarations, such as properties and classes. Let's implement an expected property:

  1. Open Platform.kt in the commonMain module and add the following at the end of the file:

    expect val num: Int

    The Kotlin compiler complains that this property has no corresponding actual declarations in the platform modules.

  2. Try to provide the implementation right away with:

    expect val num: Int = 42

    You'll get an error saying "Expected property cannot have an initializer", because expect declarations must not have a body. The implementations must be provided in actual platform modules.

  3. Remove the initializer.

  4. Add the Android implementation in the androidMain/.../Platform.android.kt as follows:

    actual val num: Int = 1
  5. Now provide an actual implementation for num in the iosMain module. Add the following to the iosMain/.../Platform.ios.kt file:

    actual val num: Int = 2
  6. In the commonMain/.../GreetingUtil.kt file, use the num property in the string formed by the sayHello() function:

    fun sayHello(to: String): String { val firstWord = if (Random.nextBoolean()) "Hi!" else "Hello!" return "$firstWord [$num] Guess what this is! > ${to.reversed()}!" }

Run your application

You can run your multiplatform application for both Android and iOS from IntelliJ IDEA.

If you have created the optional expect/actual property earlier, you should see "[1]" added to the greeting for Android and "[2]" added for iOS.

Run your application on Android

  1. In the list of run configurations, select androidApp.

  2. Choose an Android virtual device next to the list of configurations and click Run.

    If you don't have a device in the list, create a new Android virtual device.

    Run multiplatform app on Android
    First mobile multiplatform app on Android

Run your application on iOS

If you haven't launched Xcode as part of the initial setup, do that before running the iOS app.

In IntelliJ IDEA, select iosApp in the list of run configurations, select a simulated device next to the run configuration, and click Run.

If you don't have an available iOS configuration in the list, start Xcode to populate the available simulators and restart IntelliJ IDEA.

Run multiplatform app on iOS

First mobile multiplatform app on iOS

Run on a real iOS device

You can run your multiplatform application on a real iOS device. Before you start, you'll need to set the Team ID associated with your Apple ID.

Set your Team ID

To set new Team ID for your project for the first time, open the project in Xcode (File | Open Project in Xcode):

  1. In the Project navigator on the left-hand side, select iosApp.

  2. Select iosApp under Targets and switch to the Signing & Capabilities tab.

  3. In the Team list, select your team.

    If you haven't set up your team yet, use the Add an Account option in the Team list and follow Xcode instructions.

  4. Make sure that the Bundle Identifier is unique and a Signing Certificate is successfully assigned.

After you set up a team in Xcode, you can set or change the team in IntelliJ IDEA:

  1. Edit the run configuration for iosApp:

    Edit iOS run configuration
  2. Switch to the Options tab and make the necessary changes in the Development team dropdown, then click OK.

Run the app

Connect your iPhone with a cable. If you already have the device registered in Xcode, IntelliJ IDEA should show it in the list of run configurations. Run the corresponding iosApp configuration.

If you haven't registered your iPhone in Xcode yet, follow Apple recommendations. In short, you should:

  1. Connect your iPhone with a cable.

  2. On your iPhone, enable the developer mode in Settings | Privacy & Security.

  3. In Xcode, go to the top menu and choose Window | Devices and Simulators.

  4. If your iPhone is not shown as connected, click the plus sign at the bottom left and select it.

  5. Follow the on-screen instructions to complete the pairing process.

Once you've registered your iPhone in Xcode, it will become available in the list of available devices in IntelliJ IDEA when you select the iosApp run configuration.

Next step

In the next part of the tutorial, you'll learn how to update the UI elements using platform-specific libraries.

Proceed to the next part

See also

Get help

15 May 2026