cyberangles guide

Building Your First Kotlin Multiplatform Mobile (KMM) App

In today’s mobile development landscape, building apps for both iOS and Android often means maintaining separate codebases—doubling the effort for development, testing, and maintenance. **Kotlin Multiplatform Mobile (KMM)** aims to solve this by allowing you to share core business logic across platforms while retaining native UIs for iOS and Android. KMM leverages Kotlin’s multiplatform capabilities to write shared code (e.g., data models, APIs, business logic) that compiles to both Android (JVM) and iOS (Native). This reduces duplication, ensures consistency, and accelerates development. In this guide, we’ll walk you through building your first KMM app from scratch, covering setup, project structure, shared logic, and platform integration.

Table of Contents

  1. Prerequisites
  2. Setting Up the Development Environment
  3. Creating Your First KMM Project
  4. Understanding the KMM Project Structure
  5. Writing Shared Business Logic
  6. Integrating Shared Logic with Android
  7. Integrating Shared Logic with iOS
  8. Testing the App
  9. Troubleshooting Common Issues
  10. Advanced KMM Concepts (Brief Overview)
  11. Conclusion
  12. References

Prerequisites

Before diving in, ensure you have the following tools installed:

  • Android Studio (2022.2.1 or later): For Android development and KMM project management.
  • Xcode (14.0 or later): Required for iOS development (only available on macOS).
  • JDK 17: KMM requires Java Development Kit 17.
  • macOS: To build and run iOS apps (KMM iOS development is only supported on macOS).

Setting Up the Development Environment

Step 1: Install Android Studio

Download Android Studio from the official website. During installation, select the “Android SDK,” “Android SDK Platform,” and “Android Virtual Device” components.

Step 2: Install the KMM Plugin

Open Android Studio, go to Preferences > Plugins, search for “Kotlin Multiplatform Mobile,” and install the plugin. Restart Android Studio to activate it.

Step 3: Configure Xcode

If you’re on macOS, install Xcode from the Mac App Store. After installation, open Xcode and accept the license agreement. Then, install the Xcode command-line tools:

xcode-select --install  

Step 4: Verify JDK Installation

Check if JDK 17 is installed:

java -version  

If not, download it from Adoptium and set it as the default JDK in Android Studio (File > Project Structure > SDK Location > JDK Location).

Creating Your First KMM Project

Step 1: Start a New KMM Project

Open Android Studio and click New Project. Select the Kotlin Multiplatform App template, then click Next.

Step 2: Configure Project Details

  • Name: Enter a name (e.g., “KmmFirstApp”).
  • Location: Choose a project directory.
  • Language: Select “Kotlin” (default).
  • Minimum SDK: For Android, choose API 24 (Android 7.0) or higher.
  • iOS Framework Distribution: Select “Regular framework” (default for most cases).

Click Finish to generate the project.

Understanding the KMM Project Structure

A KMM project has three main modules:

1. shared Module

This is the core of your KMM app, containing code shared between Android and iOS. It has three source sets:

  • commonMain: Code shared across all platforms (e.g., data models, business logic).
  • androidMain: Android-specific code (e.g., Android SDK integrations).
  • iosMain: iOS-specific code (e.g., iOS SDK integrations).

2. androidApp Module

Android-specific app code (UI, activities, Jetpack Compose/SwiftUI). It depends on the shared module.

3. iosApp Module

iOS-specific app code (UI, ViewControllers, SwiftUI). It uses the shared module as a framework.

Writing Shared Business Logic

Let’s add a simple shared feature: a Greeting class that returns a personalized message.

Step 1: Add Code to commonMain

In the shared module, navigate to src/commonMain/kotlin/com/example/kmmfirstapp. Create a new Kotlin file Greeting.kt:

package com.example.kmmfirstapp  

class Greeting {  
    private val platform = getPlatform()  

    fun greeting(): String {  
        return "Hello, ${platform.name}!"  
    }  
}  

// Expect declaration: Defines a common interface  
expect class Platform {  
    val name: String  
}  

The expect keyword declares a type/function that must be implemented for each platform (Android and iOS).

Step 2: Implement Platform for Android

In src/androidMain/kotlin/com/example/kmmfirstapp, create Platform.kt:

package com.example.kmmfirstapp  

// Actual implementation for Android  
actual class Platform actual constructor() {  
    actual val name: String = "Android"  
}  

Step 3: Implement Platform for iOS

In src/iosMain/kotlin/com/example/kmmfirstapp, create Platform.kt:

package com.example.kmmfirstapp  

// Actual implementation for iOS  
actual class Platform actual constructor() {  
    actual val name: String = "iOS"  
}  

The actual keyword provides platform-specific implementations for expect declarations.

Integrating Shared Logic with Android

Now, let’s use the Greeting class in the Android app.

Step 1: Update Android UI

The androidApp module uses Jetpack Compose by default. Open androidApp/src/main/java/com/example/kmmfirstapp/MainActivity.kt:

package com.example.kmmfirstapp  

import android.os.Bundle  
import androidx.activity.ComponentActivity  
import androidx.activity.compose.setContent  
import androidx.compose.foundation.layout.Arrangement  
import androidx.compose.foundation.layout.Column  
import androidx.compose.foundation.layout.fillMaxSize  
import androidx.compose.material3.MaterialTheme  
import androidx.compose.material3.Surface  
import androidx.compose.material3.Text  
import androidx.compose.runtime.Composable  
import androidx.compose.ui.Alignment  
import androidx.compose.ui.Modifier  
import androidx.compose.ui.tooling.preview.Preview  

class MainActivity : ComponentActivity() {  
    override fun onCreate(savedInstanceState: Bundle?) {  
        super.onCreate(savedInstanceState)  
        val greeting = Greeting() // Shared class  
        setContent {  
            MyApplicationTheme {  
                Surface(  
                    modifier = Modifier.fillMaxSize(),  
                    color = MaterialTheme.colorScheme.background  
                ) {  
                    GreetingText(greeting.greeting())  
                }  
            }  
        }  
    }  
}  

@Composable  
fun GreetingText(message: String) {  
    Column(  
        modifier = Modifier.fillMaxSize(),  
        verticalArrangement = Arrangement.Center,  
        horizontalAlignment = Alignment.CenterHorizontally  
    ) {  
        Text(text = message)  
    }  
}  

@Preview  
@Composable  
fun DefaultPreview() {  
    MyApplicationTheme {  
        GreetingText("Hello, Android!")  
    }  
}  

Here, we initialize Greeting from the shared module and display its greeting() result in the UI.

Integrating Shared Logic with iOS

Now, let’s use the Greeting class in the iOS app.

Step 1: Open the iOS Project

In Android Studio, right-click the iosApp module and select Open in Xcode. This opens the iOS project in Xcode.

Step 2: Update SwiftUI Code

The default iOS app uses SwiftUI. Open iosApp/iosApp/ContentView.swift:

import SwiftUI  
// Import the shared KMM framework  
import shared  

struct ContentView: View {  
    let greeting = Greeting() // Shared Kotlin class  

    var body: some View {  
        VStack(spacing: 16) {  
            Text(greeting.greeting()) // Call shared function  
                .font(.title)  
        }  
        .padding()  
    }  
}  

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

Kotlin classes/functions in the shared module are exposed to Swift as regular Swift types. Here, Greeting and greeting() are called directly.

Testing the App

Run the Android App

  1. In Android Studio, select the androidApp module from the run configurations dropdown.
  2. Choose an Android emulator or physical device.
  3. Click Run (▶️). The app will launch, displaying “Hello, Android!“.

Run the iOS App

  1. In Xcode, select a simulator (e.g., “iPhone 14”) from the run configurations dropdown.
  2. Click Run (▶️). The app will launch, displaying “Hello, iOS!“.

Troubleshooting Common Issues

1. “Kotlin/Native Compilation Failed”

  • Ensure Xcode is installed and up-to-date.
  • Clean the project: Build > Clean Project (Android Studio) or ./gradlew clean (terminal).

2. “Shared Framework Not Found” in Xcode

  • Rebuild the shared module: In Android Studio, run :shared:build.
  • In Xcode, go to Product > Clean Build Folder.

3. Expect/Actual Mismatch

  • Ensure all expect declarations have corresponding actual implementations in androidMain and iosMain.

Advanced KMM Concepts (Brief Overview)

Once you’re comfortable with the basics, explore these advanced topics:

  • Networking: Use Ktor for cross-platform HTTP requests.
  • Local Storage: Use SQLDelight for shared database logic.
  • State Management: Use Jetpack Compose (Android) and SwiftUI (iOS) with shared state holders (e.g., ViewModel-like classes in commonMain).

Conclusion

You’ve built your first KMM app! You now understand how to share business logic between Android and iOS, integrate shared code into native UIs, and test across platforms. KMM reduces duplication and streamlines development while retaining native performance and UX.

References