Skip to content

outline: deep

Compose Multiplatform

Getting Started with Kotlin Multiplatform: Building Your First Cross-Platform Application

This is the second article in the Kotlin Multiplatform development blog series. I plan to update one article weekly, alternating between basic and advanced content.

Introduction

This article is aimed at developers interested in Kotlin Multiplatform (KMP), especially those looking to explore how to develop cross-platform applications using a single codebase. Whether you're a mobile developer, frontend engineer, or full-stack developer, if you're interested in improving development efficiency and cross-platform development, getting to know and experiencing KMP is worthwhile.

In this beginner-friendly tutorial, we'll cover the following topics in detail:

  1. Environment Setup
  2. Project Initialization
  3. Gradle Commands
  4. Launching Desktop Application
  5. Configuring and Launching Android Application
  6. Configuring and Launching iOS Application
  7. Launching Web Application
  8. Setting up and Launching Server Application

Environment Setup

Before starting our KMP journey, we need to set up the development environment.

First, we need to install Android Studio / Xcode / CocoaPods. With these basic environments, we can experience development for all KMP platforms.

For Windows and Linux users, due to system limitations, you can skip the Xcode / CocoaPods installation, which means you'll only be able to experience desktop, Android, and web platform development.

  1. Android Studio: This is our main Integrated Development Environment (IDE). Android Studio not only supports Android development but also perfectly integrates Kotlin and KMP support.

  2. Xcode (Mac users only): Used for iOS application development and testing. Even if you mainly use Android Studio, Xcode is essential for iOS development.

  3. CocoaPods (Mac users only): A commonly used dependency management tool in iOS development.

    • Installation command: brew install cocoapods

Note: Windows and Linux users can skip Xcode and CocoaPods installation. While this means you won't be able to develop iOS applications temporarily, you can still fully experience KMP's powerful features on desktop, Android, and web platforms.

For Mac users, you can also install the kdoctor tool to check your environment:

shell
brew install kdoctor
kdoctor
Environment diagnose (to see all details, use -v option):
[✓] Operation System
[✓] Java
[!] Android Studio
  ! Android Studio (AI-242.23339.11.2421.12483815)
    Bundled Java: openjdk 21.0.3 2024-04-16
    Kotlin Plugin: 242.23339.11.2421.12483815-AS
    Kotlin Multiplatform Mobile Plugin: not installed
    Install Kotlin Multiplatform Mobile plugin - https://plugins.jetbrains.com/plugin/14936-kotlin-multiplatform-mobile
  ! Android Studio (AI-242.21829.142.2421.12366423)
    Bundled Java: openjdk 21.0.3 2024-04-16
    Kotlin Plugin: 242.21829.142.2421.12366423-AS
    Kotlin Multiplatform Mobile Plugin: not installed
    Install Kotlin Multiplatform Mobile plugin - https://plugins.jetbrains.com/plugin/14936-kotlin-multiplatform-mobile
[✓] Xcode
[✓] CocoaPods

Conclusion:
  ✓ Your operation system is ready for Kotlin Multiplatform Mobile Development!

kdoctor will list your environment status in detail, including operating system, Java version, Android Studio configuration, Xcode, and CocoaPods. If there are any issues, it will provide clear suggestions for fixes.

Initializing Project with JetBrains KMP Scaffold

JetBrains provides a very convenient online tool - Kotlin Multiplatform Wizard, which helps us quickly generate a KMP project template with all necessary configurations.

Let's go through it step by step:

  1. Open Kotlin Multiplatform Wizard

  2. On the wizard interface, you'll see multiple options allowing you to choose the platforms to support. To fully demonstrate KMP's powerful features, we'll select all available platforms here. You can choose based on your project requirements. KMP wizard

  3. After configuration, click the download button. This will generate a zip file containing all necessary files and configurations.

  4. After downloading, extract this zip file to your working directory.

  5. Open this project directory with Android Studio.

  6. When you first open the project, Gradle will start syncing and downloading necessary dependencies. This process may take some time, depending on your internet speed.

  7. If you encounter network issues causing sync failure, consider adding domestic Maven mirrors or configuring Gradle proxy. KMP sync

KMP sync

Now, let's look at the project structure, especially the composeApp module, which is the core of our multi-platform application:

shell
composeApp
├── build.gradle.kts
└── src
    ├── androidMain
    ├── commonMain
    ├── desktopMain
    ├── iosMain
    └── wasmJsMain

You'll notice that under the src directory, there are multiple subdirectories, each corresponding to a specific platform:

  • commonMain: Contains code shared across all platforms. This is the essence of KMP, where you can write code once and run it on all platforms.
  • androidMain: Android-specific code.
  • desktopMain: Desktop application (Windows, macOS, Linux) specific code.
  • iosMain: iOS-specific code.
  • wasmJsMain: Web application-specific code.

Besides the composeApp module, you'll also see a shared module with a similar structure to composeApp. The shared module is typically used to store code that can be shared between multiple modules, and composeApp depends on shared. This structure allows you to further modularize your application, improving code maintainability and reusability.

In the composeApp's build.gradle.kts file, you'll see dependency declarations like this:

kotlin
commonMain.dependencies {
    implementation(projects.shared)
}

This line indicates that the composeApp module depends on the shared module. This setup allows you to define shared data models, business logic, or utility classes in the shared module, then use them in composeApp.

Understanding this project structure is crucial for fully utilizing KMP's advantages. It allows you to maximize code reuse while still providing customized implementations for each platform.

Project Gradle Commands

Gradle is the build tool for KMP projects, providing many useful commands to help us build, test, run, and publish multi-platform applications. First, let's look at the build.gradle.kts file in the project root directory:

DSL
plugins {
    // this is necessary to avoid the plugins to be loaded multiple times
    // in each subproject's classloader
    alias(libs.plugins.androidApplication) apply false
    alias(libs.plugins.androidLibrary) apply false
    alias(libs.plugins.jetbrainsCompose) apply false
    alias(libs.plugins.compose.compiler) apply false
    alias(libs.plugins.kotlinJvm) apply false
    alias(libs.plugins.kotlinMultiplatform) apply false
}

These plugins provide necessary functionality support for our KMP project, such as Android application development, Jetpack Compose UI framework, Kotlin multiplatform support, and more.

We can view all commands provided by these plugins through gradle tasks

Output as follows:

shell
Android tasks
-------------
androidDependencies - Displays the Android dependencies of the project.
signingReport - Displays the signing info for the base and test modules
sourceSets - Prints out all the source sets defined in this project.

Application tasks
-----------------
run - Runs this project as a JVM application
runShadow - Runs this project as a JVM application using the shadow jar
startShadowScripts - Creates OS specific scripts to run the project as a JVM application using the shadow jar

These tasks cover various aspects from building to testing, to running and deployment. When we need certain functionalities, we can find them here as needed.

Although we can run these Gradle tasks from the command line, I personally recommend using the IDE to execute these tasks. Using IDE has the following advantages:

  1. Better project isolation: IDE can maintain independent configurations for each project, avoiding conflicts that might arise from global settings.
  2. More intuitive interface: IDE provides a graphical interface, making task execution more intuitive and convenient.
  3. Better debugging support: When encountering problems, IDE provides more detailed logs and debugging tools.
gradle-run

Launching Desktop Application

Let's launch our first platform application - the desktop application. This will demonstrate how KMP seamlessly converts our code into an application that can run on Windows, macOS, and Linux.

You might try to launch the application directly by running the main function through IDE, but this won't execute successfully. This is because KMP applications need a lot of preprocessing work before running, and the IDE itself doesn't have these features preset.

main-run

The correct approach is to use the gradle command mentioned above. We can easily find the relevant command run - Runs this project as a JVM application

shell
gradle run

After executing this command, Gradle will compile our code, handle all necessary dependencies, then launch the application. After a few seconds, you should see a window pop up displaying our application interface:

desktop-ui

Congratulations! You've just successfully run your first KMP desktop application!

While running from command line is convenient, wouldn't it be better if we could click the run button directly in the IDE? Let's save the run configuration we just executed for future use.

save-run.png

With this configuration, you can now run and debug your KMP desktop application as easily as running regular Kotlin / Java applications.

Configuring and Launching Android Application

Next, let's turn our attention to the mobile platform, starting with Android. KMP allows us to reuse most of our code while providing Android-specific implementations.

Before running the Android application, we need to ensure we have the correct Android SDK and tools installed.

  1. In Android Studio, go to Settings/Preferences -> Languages & Frameworks -> Android SDK
  2. Here, you can see installed SDK versions and tools. Make sure you have at least one recent Android SDK version installed.
  3. If needed, install additional tools in the SDK Tools tab, such as Android Emulator and SDK Platform-Tools.

android-sdk.png

After KMP project initialization, Android Studio will automatically create Android run configuration for the composeApp module. You can find it in the run configuration dropdown menu at the top of the IDE:

android-run

You can launch the Android application using either a physical device or an emulator.

  1. If choosing an emulator, you can create one in Device Manager device-manager
  2. If using a physical device, you need to enable Developer options and USB debugging on your Android device.
    • Go to Settings -> About phone, tap "Build number" 7 times to enable Developer options.
    • Return to the main Settings page, you should see "Developer options"
    • Enable "USB debugging" in Developer options

For more details, you can check the official documentation

Running effect android-ui

Configuring and Launching iOS Application

Note: iOS development requires a Mac computer. If you're using Windows or Linux, you can skip this section.

Now let's turn to Apple's ecosystem and see how to run our KMP application on iOS devices.

  1. Open the iosApp launch configuration ios-run

  2. In the Execution target dropdown menu, you can choose the iOS device or simulator to run on. If you have connected a physical iOS device, it will also appear in this list.

  3. To launch the application on a physical device, you also need to enable Developer mode. Taking iPhone as an example, go to Settings -> Privacy & Security -> Developer Mode to find the corresponding switch (the settings path might vary slightly with different system versions).

Configuring Developer Account

When running an iOS application on a physical device for the first time, you might encounter the following error:

log
error: Signing for "iosApp" requires a development team. Select a development team in the Signing & Capabilities editor. (in target 'iosApp' from project 'iosApp')
warning: Run script build phase 'Compile Kotlin Framework' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'iosApp' from project 'iosApp')
** BUILD FAILED **

This is because iOS applications need a developer signature. Let's configure it:

  1. Open Xcode
  2. Select Open Existing Project, then navigate to your KMP project directory
  3. Find and open the iosApp.xcodeproj file in the iosApp folder
  4. In Xcode, select iosApp in the project navigator on the left
  5. In the main editing area, select the Signing & Capabilities tab
  6. In the Team dropdown menu, select your developer account. If you don't have an Apple developer account yet, you need to register one at developer.apple.com first

xcode.png

After setting up the team ID, we can restart the application. ios-ui

You have now successfully deployed the same KMP application to Desktop / Android / iOS platforms.

Launching Web Application

KMP's charm isn't limited to mobile and desktop platforms; it can also help us build web applications. The advantage of web applications is that they can reach users at a lower cost, allowing users to experience initial functionality and attracting them to download the client.

In this section, we'll see how to deploy our application as a web application.

The process of running the web application is similar to the desktop application, using a Gradle command:

shell
gradle wasmJsBrowserRun

This command does several things:

  1. Compiles your Kotlin code to WebAssembly (Wasm)
  2. Sets up a local development server
  3. Opens your application in the default browser

The first time you execute the command, you might see it stuck at: > Task :kotlinNpmInstall

This task might take some time as it's downloading necessary dependencies. Be patient, and after completion, you should see your application in the browser:

wasmJs-ui

Advantages of Web Applications

Deploying your KMP application as a web application has several notable advantages:

  1. Wide accessibility: Users can access your application without installing anything.
  2. Instant updates: You can update the application anytime, and users always have access to the latest version.
  3. Cross-platform compatibility: Web applications can run on any device with a modern browser.

Through KMP, you can maintain consistent core business logic while providing customized UI and functionality for the web platform. This significantly improves development efficiency while ensuring consistency across platforms.

Running Server Application

Finally, let's look at how to run the server application in a KMP project. Server-side Kotlin applications typically use the Ktor framework, a powerful and flexible Kotlin web framework.

In KMP projects, the server module is usually a standard Kotlin JVM application. Its main advantage lies in being able to share code with client applications, especially data models and business logic.

Opening the server module's build.gradle.kts file, you might see dependencies like this:

kotlin
dependencies {
    implementation(projects.shared)
    implementation(libs.ktor.server.core)
    implementation(libs.ktor.server.netty)
    // ... other dependencies
}

Note the implementation(projects.shared) line - it indicates that the server module depends on the shared module, which is how we achieve code sharing.

Including a server module in a KMP project has several key advantages:

  1. Code sharing: Server and client can share data models, validation logic, etc.
  2. Type safety: Using the same language and models allows catching many potential errors at compile time.
  3. Development efficiency: Developers don't need to define things repeatedly, avoiding inconsistency issues and facilitating full-stack development.

Conclusion

In this tutorial, we've gone through the process from environment setup to creating and running a KMP project on multiple platforms. Through practice, we've seen that KMP not only significantly improves development efficiency but more importantly makes cross-platform development elegant and natural.

If you want to see KMP's application in a real project, you can refer to my open-source project CrossPaste. This is a unified desktop clipboard tool developed based on Compose Multiplatform, supporting Windows, macOS, and Linux platforms. Not only can it serve as a standalone clipboard management tool, but more importantly, it supports cross-device copy and paste, making copying and pasting between any devices as natural and smooth as operating on the same device.