Claude
Skills
Sign in
Back

kotlin-multiplatform

Included with Lifetime
$97 forever

Expert guidance for Kotlin Multiplatform (KMP), JetBrains' technology for sharing code between Android, iOS, web, and desktop applications. Helps developers build shared business logic, networking, and data layers in Kotlin while keeping UI native on each platform.

Design

What this skill does


# Kotlin Multiplatform — Shared Business Logic for Mobile


## Overview


Kotlin Multiplatform (KMP), JetBrains' technology for sharing code between Android, iOS, web, and desktop applications. Helps developers build shared business logic, networking, and data layers in Kotlin while keeping UI native on each platform.


## Instructions

### Project Structure

```
my-kmp-app/
├── shared/                              # Shared Kotlin code
│   └── src/
│       ├── commonMain/                  # Platform-independent code
│       │   └── kotlin/com/example/
│       │       ├── data/                # Repositories, models
│       │       ├── domain/              # Use cases, business logic
│       │       └── network/             # API clients
│       ├── androidMain/                 # Android-specific implementations
│       │   └── kotlin/com/example/
│       │       └── platform/
│       └── iosMain/                     # iOS-specific implementations
│           └── kotlin/com/example/
│               └── platform/
├── androidApp/                          # Android app (Jetpack Compose)
├── iosApp/                              # iOS app (SwiftUI)
└── build.gradle.kts
```

### Shared Business Logic

```kotlin
// shared/src/commonMain/kotlin/com/example/domain/TaskRepository.kt
// This code runs on BOTH Android and iOS — write once, test once.

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

class TaskRepository(
    private val api: TaskApi,
    private val db: TaskDatabase,
) {
    /**
     * Get all tasks, fetching from API and caching in local DB.
     * Returns a Flow that emits updates when data changes.
     */
    fun getTasks(): Flow<List<Task>> {
        return db.observeAll().map { entities ->
            entities.map { it.toTask() }
        }
    }

    /**
     * Sync tasks from the remote API to the local database.
     * Called on app launch and pull-to-refresh.
     */
    suspend fun syncTasks() {
        val remoteTasks = api.fetchTasks()
        db.upsertAll(remoteTasks.map { it.toEntity() })
    }

    /**
     * Create a new task — saves locally and syncs to server.
     * If offline, saves locally and syncs when connection returns.
     */
    suspend fun createTask(title: String, priority: Priority): Task {
        val task = Task(
            id = generateUuid(),
            title = title,
            priority = priority,
            status = TaskStatus.TODO,
            createdAt = Clock.System.now(),
        )
        db.insert(task.toEntity())

        try {
            api.createTask(task.toRequest())
        } catch (e: Exception) {
            // Queue for sync when online
            db.markPendingSync(task.id)
        }

        return task
    }

    suspend fun updateStatus(taskId: String, status: TaskStatus) {
        db.updateStatus(taskId, status)
        try {
            api.updateTask(taskId, UpdateTaskRequest(status = status))
        } catch (e: Exception) {
            db.markPendingSync(taskId)
        }
    }
}
```

### Networking with Ktor

```kotlin
// shared/src/commonMain/kotlin/com/example/network/TaskApi.kt
// Ktor is Kotlin's multiplatform HTTP client — same code on Android/iOS.

import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.request.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.json.Json

class TaskApi(private val baseUrl: String, private val authToken: String) {
    private val client = HttpClient {
        install(ContentNegotiation) {
            json(Json {
                ignoreUnknownKeys = true    // Don't crash on extra API fields
                isLenient = true
            })
        }
        defaultRequest {
            header("Authorization", "Bearer $authToken")
        }
    }

    suspend fun fetchTasks(): List<TaskResponse> {
        return client.get("$baseUrl/api/tasks").body()
    }

    suspend fun createTask(request: CreateTaskRequest): TaskResponse {
        return client.post("$baseUrl/api/tasks") {
            setBody(request)
        }.body()
    }

    suspend fun updateTask(id: String, request: UpdateTaskRequest): TaskResponse {
        return client.patch("$baseUrl/api/tasks/$id") {
            setBody(request)
        }.body()
    }
}
```

### Platform-Specific Code with expect/actual

```kotlin
// shared/src/commonMain/kotlin/com/example/platform/Platform.kt
// 'expect' declares what each platform must implement.

expect fun generateUuid(): String
expect fun getPlatformName(): String

// shared/src/androidMain/kotlin/com/example/platform/Platform.kt
actual fun generateUuid(): String = java.util.UUID.randomUUID().toString()
actual fun getPlatformName(): String = "Android ${android.os.Build.VERSION.SDK_INT}"

// shared/src/iosMain/kotlin/com/example/platform/Platform.kt
import platform.Foundation.NSUUID
import platform.UIKit.UIDevice

actual fun generateUuid(): String = NSUUID().UUIDString()
actual fun getPlatformName(): String = UIDevice.currentDevice.systemName() +
    " " + UIDevice.currentDevice.systemVersion
```

### Local Database with SQLDelight

```kotlin
// shared/src/commonMain/sqldelight/com/example/db/Task.sq
// SQLDelight generates type-safe Kotlin from SQL — works on all platforms.

CREATE TABLE TaskEntity (
    id TEXT NOT NULL PRIMARY KEY,
    title TEXT NOT NULL,
    status TEXT NOT NULL DEFAULT 'todo',
    priority TEXT NOT NULL DEFAULT 'medium',
    pending_sync INTEGER NOT NULL DEFAULT 0,
    created_at INTEGER NOT NULL
);

selectAll:
SELECT * FROM TaskEntity ORDER BY created_at DESC;

insert:
INSERT OR REPLACE INTO TaskEntity(id, title, status, priority, created_at)
VALUES (?, ?, ?, ?, ?);

updateStatus:
UPDATE TaskEntity SET status = ? WHERE id = ?;

markPendingSync:
UPDATE TaskEntity SET pending_sync = 1 WHERE id = ?;

getPendingSync:
SELECT * FROM TaskEntity WHERE pending_sync = 1;
```

### ViewModel (Shared UI Logic)

```kotlin
// shared/src/commonMain/kotlin/com/example/viewmodel/TaskListViewModel.kt
// Shared ViewModel — both Android and iOS consume this.

import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch

class TaskListViewModel(private val repository: TaskRepository) {
    private val _uiState = MutableStateFlow(TaskListUiState())
    val uiState: StateFlow<TaskListUiState> = _uiState.asStateFlow()

    init {
        // Observe local database changes
        repository.getTasks()
            .onEach { tasks ->
                _uiState.update { it.copy(tasks = tasks, isLoading = false) }
            }
            .launchIn(viewModelScope)

        // Initial sync from server
        refresh()
    }

    fun refresh() {
        viewModelScope.launch {
            _uiState.update { it.copy(isLoading = true) }
            try {
                repository.syncTasks()
            } catch (e: Exception) {
                _uiState.update { it.copy(error = e.message) }
            }
            _uiState.update { it.copy(isLoading = false) }
        }
    }

    fun createTask(title: String, priority: Priority) {
        viewModelScope.launch {
            repository.createTask(title, priority)
        }
    }
}

data class TaskListUiState(
    val tasks: List<Task> = emptyList(),
    val isLoading: Boolean = true,
    val error: String? = null,
)
```

## Installation

```kotlin
// build.gradle.kts (shared module)
plugins {
    kotlin("multiplatform")
    kotlin("plugin.serialization")
    id("app.cash.sqldelight")
}

kotlin {
    androidTarget()
    iosX64()
    iosArm64()
    iosSimulatorArm64()

    sourceSets {
        commonMain.dependencies {
            implementation("io.ktor:ktor-client-core:2.3.7")
            implementation("io.ktor:ktor-client-content-negotiation:2.3.7")
            implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.7")
            implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
            implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0
Files: 2
Size: 12.4 KB
Complexity: 23/100
Category: Design

Related in Design