Every developer who has built an app for more than one platform knows the feeling: you finish the iOS version, and then you have to rebuild nearly everything for Android. The logic, the data models, the networking—all duplicated. It's like buying the same book twice, once for your home library and once for your office. You want a shared collection that both locations can access, but each still has its own reading nook. That's exactly how Kotlin Multiplatform (KMP) works: a single shared codebase for business logic, with platform-specific UI and APIs where they matter most. In this guide, we'll show you how to find your first cross-platform app using KMP, drawing on the "shared book collection" analogy from bookhub.top.
Why Cross-Platform Feels Like a Library Without a Catalog
Before we dive into Kotlin Multiplatform, let's acknowledge the problem. Cross-platform development has been around for years, but many solutions come with trade-offs that feel like a library where the books are poorly organized. Web-based approaches like Cordova wrap your app in a browser view, but the user experience often suffers. React Native and Flutter offer better performance, but they still require you to learn a new rendering engine or bridge native modules. The result? Teams end up maintaining two almost-identical codebases for iOS and Android, or they lock themselves into a single-vendor ecosystem.
The Shared Collection Analogy
Imagine you and a friend each have a personal library. You both want to read the same new releases, but you also have unique tastes. A shared book collection—like a small club library—lets you buy one copy of each book and place it on a central shelf. Both of you can borrow it, annotate it, and return it. That central shelf is your shared business logic. Meanwhile, each of you can still decorate your own reading room—the platform-specific UI—any way you like. Kotlin Multiplatform is exactly that central shelf. It lets you write your data models, network calls, and business rules once in Kotlin, and then use them from both Android (with Kotlin/JVM) and iOS (with Kotlin/Native).
This approach solves a fundamental pain point: duplication. In a typical project, the business logic—validation, state management, API communication—can account for 60–80% of the code. By sharing that part, you cut development time and reduce bugs, because there's only one version of the truth. You also keep the platform-specific parts—UI, animations, hardware access—written in the native language (Swift for iOS, Kotlin for Android), so you get a truly native feel.
But like any library, there are decisions to make. What goes on the shared shelf? What stays in each reading room? And how do you set up the borrowing system? That's what we'll explore next.
How Kotlin Multiplatform Works: The Shared Shelf in Detail
Kotlin Multiplatform is not a single product; it's a set of tools and conventions that allow you to write shared code in Kotlin and compile it to different targets. The core idea is simple: you define a module (often called shared) that contains all the platform-independent logic. Then, for each target platform, you create a separate module that depends on the shared one. The shared module can also declare expect declarations—promises that a function or class will be provided by each platform—and each platform module supplies the corresponding actual implementation.
The Three Layers of a KMP App
Most KMP projects follow a layered architecture. At the bottom is the data layer, which handles network requests, database operations, and caching. This is almost always fully shared. Next is the domain layer, which contains use cases and business logic—also shared. On top is the presentation layer, which is where the UI lives. This layer is typically platform-specific, though you can share some ViewModel logic using libraries like moko-mvvm or the newer Compose Multiplatform.
For example, consider a simple book-tracking app. The shared module might contain:
- BookRepository – fetches book data from an API and caches it locally using SQLDelight.
- BookViewModel – exposes state and actions (search, add to favorites) using Kotlin coroutines and Flow.
- Book – a data class with fields like title, author, and ISBN.
On Android, you might use Jetpack Compose to render the UI, calling the shared ViewModel. On iOS, you would write SwiftUI views that call the same shared logic via a Kotlin/Native framework. The result: the same search algorithm, the same favorite logic, and the same API client—but with a native UI on each platform.
Comparison with Other Cross-Platform Tools
To help you decide if KMP is right for your first project, here's a comparison with two other popular approaches:
| Approach | Shared Code | UI Layer | Performance | Learning Curve |
|---|---|---|---|---|
| Kotlin Multiplatform | Business logic (60–80%) | Native (SwiftUI / Jetpack Compose) | Native | Moderate (need both Kotlin and platform languages) |
| Flutter | Everything (Dart) | Custom rendering engine (Skia) | Good, but not native | Low (single language for everything) |
| React Native | Everything (JavaScript/TypeScript) | Native widgets via bridge | Good, but bridge overhead | Low (if you know React) |
As the table shows, KMP offers the best performance and the most native feel, but it requires you to maintain two UI codebases. That trade-off is acceptable when you want to ship a polished app that looks and feels at home on each platform—like a book that sits well on any shelf.
Setting Up Your First KMP Project: A Step-by-Step Guide
Let's walk through creating a minimal KMP project from scratch. We'll use the official Kotlin Multiplatform Wizard (available at kmp.jetbrains.com) and Android Studio. By the end, you'll have a shared module that can run on both Android and iOS.
Step 1: Generate the Project
Open the Kotlin Multiplatform Wizard in your browser. Select the template "Mobile App" and choose your targets: Android and iOS. Give your project a name, like "BookCollectionApp". Download the generated zip and open it in Android Studio.
Step 2: Understand the Structure
The wizard creates three modules: shared, androidApp, and iosApp. The shared module contains a commonMain source set for shared code, plus androidMain and iosMain for platform-specific implementations. Notice the expect/actual pattern in the generated Greeting class.
Step 3: Add Your First Shared Logic
In shared/src/commonMain/kotlin, create a new file BookSearch.kt. Write a simple function that takes a query string and returns a list of books (hardcoded for now):
package com.example.bookcollectionapp
data class Book(val title: String, val author: String)
class BookSearch {
fun search(query: String): List<Book> {
return listOf(
Book("The Kotlin Journey", "Jane Dev"),
Book("Multiplatform Patterns", "John Coder")
).filter { it.title.contains(query, ignoreCase = true) }
}
}
Step 4: Use the Shared Code on Android
In the androidApp module, open MainActivity.kt and call the shared BookSearch. For example, in a button click handler:
val search = BookSearch()
val results = search.search("Kotlin")
textView.text = "Found ${results.size} books"
Step 5: Use the Shared Code on iOS
In Xcode, open the iosApp project. The shared module is already linked as a framework. In ContentView.swift, import the shared framework and call the same class:
import BookCollectionApp
struct ContentView: View {
var body: some View {
Text("Found \(BookSearch().search(query: "Kotlin").count) books")
}
}
That's it! You've shared your first piece of logic. From here, you can add real networking with Ktor, persistence with SQLDelight, and more complex state management.
Tools, Stack, and Maintenance Realities
Once you have a working KMP project, you'll need to think about the ecosystem. The good news: Kotlin Multiplatform has matured significantly, with strong support from JetBrains and an active community. The not-so-good news: some libraries are still catching up, and you may need to write platform-specific code for certain features.
Essential Libraries for KMP
- Ktor – a multiplatform HTTP client for networking.
- SQLDelight – a multiplatform database library that generates typesafe Kotlin from SQL statements.
- kotlinx.serialization – for JSON parsing and serialization.
- kotlinx.coroutines – for async programming, fully supported on both platforms.
- Compose Multiplatform (beta) – if you want to share UI as well, but note it's still evolving.
Maintenance Considerations
Maintaining a KMP project requires discipline. You must keep the shared module truly platform-independent—avoid using Java APIs that aren't available on iOS, and be careful with date/time handling (use kotlinx-datetime). When a new iOS or Android version drops, you may need to update platform-specific code, but the shared logic remains untouched. Many teams report that the initial setup takes a bit longer, but the long-term savings in bug fixing and feature parity are substantial.
One common pitfall: over-sharing. Not everything belongs in the shared module. UI-related state that differs significantly between platforms (e.g., navigation patterns) is better kept native. A good rule of thumb: if you can write a unit test for it, it belongs in shared. If it involves a screen layout or a gesture, keep it platform-specific.
Growing Your App: Adding Features and Scaling the Shared Collection
Once your first KMP app is running, you'll want to add more features. The beauty of the shared shelf is that you can add new books (features) to the collection and they become available on both platforms instantly. Let's look at how to scale your shared codebase.
Adding a New Feature: The Book Details Screen
Suppose you want to add a detail view for each book. You would add a new use case in the shared module, say GetBookDetailsUseCase, that fetches additional information from an API. Then, on each platform, you create a new screen that calls this use case and displays the data. The logic for fetching, caching, and error handling is shared; only the UI is new. This pattern keeps your codebase maintainable as it grows.
Handling Platform-Specific Behaviors
Not everything can be shared. For example, handling push notifications requires different APIs on iOS (APNs) and Android (FCM). In KMP, you would define an expect function registerForPushNotifications() and provide actual implementations in each platform module. The rest of your app—like storing the device token or handling the notification payload—can be shared.
Testing Your Shared Code
One major advantage of KMP is that you can write unit tests for your shared module using the same JVM test framework (like JUnit). These tests run on your development machine without needing a device or emulator. For integration tests that involve platform APIs, you can use expect/actual to mock them. Many teams report that having a single test suite for business logic significantly improves code quality.
Common Pitfalls and How to Avoid Them
No technology is without its rough edges. Here are the most common mistakes teams make when starting with KMP, and how to steer clear.
Pitfall 1: Trying to Share Too Much
We mentioned this earlier, but it's worth repeating. Some developers try to put UI logic in the shared module using abstractions like MVVM with platform-specific bindings. While this is possible, it often leads to complicated code that is hard to debug. Keep the shared module focused on pure business logic and data handling. Your future self will thank you.
Pitfall 2: Ignoring iOS Build Configuration
KMP on iOS requires a bit of Xcode configuration. The shared module is compiled into a framework, and you need to ensure the framework is linked correctly. Use the Kotlin/Native compiler plugin in Gradle to automate this. Many teams use a Gradle task that builds the framework and copies it into the Xcode project. Without this, you'll face cryptic linker errors.
Pitfall 3: Not Using expect/actual Properly
The expect/actual mechanism is powerful but can be misused. Avoid making every function expect; instead, design your shared code to be as platform-independent as possible. Use expect/actual only for truly platform-specific APIs, like file I/O or date formatting. Overusing it defeats the purpose of sharing.
Pitfall 4: Neglecting Dependency Management
KMP libraries are still evolving. Some popular Android libraries (like Retrofit) don't have multiplatform versions. Always check the library's documentation for KMP support before committing. Use the kotlinx family of libraries when possible, as they are designed for multiplatform from the ground up.
Frequently Asked Questions About KMP for Beginners
We've gathered the most common questions from developers who are just starting with Kotlin Multiplatform. Here are the answers.
Do I need to know both Kotlin and Swift?
Yes, to some extent. You'll write the shared logic in Kotlin, but for the iOS UI, you'll need Swift (or Objective-C). If you're coming from an Android background, you can focus on the shared module and learn Swift basics for the UI layer. Many teams pair an Android developer with an iOS developer, each contributing to their platform's UI.
Can I use Compose Multiplatform for the UI?
Yes, Compose Multiplatform is now in beta and allows you to share UI code as well. However, it's not yet as mature as native UI frameworks. For a first project, we recommend starting with native UIs to avoid debugging both shared logic and shared UI issues at the same time.
How does KMP compare to Flutter for performance?
KMP produces native code, so it has no additional rendering overhead. Flutter uses its own rendering engine, which is fast but can feel slightly different from native components. For most apps, the performance difference is negligible, but for graphics-heavy apps, KMP may have an edge.
Is KMP production-ready?
Yes, many companies (including Netflix, McDonald's, and Cash App) use KMP in production. However, the ecosystem is still maturing, so you may encounter gaps in library support. Plan for some custom platform code.
Next Steps: Building Your First Shared Collection
By now, you should have a clear picture of how Kotlin Multiplatform works like a shared book collection. You understand the core concepts, the setup process, and the common pitfalls. The next step is to start small. Pick a simple app idea—a book tracker, a note-taking app, or a weather app—and implement it with KMP. Focus on sharing the data layer first, then add business logic. Once you see how easy it is to add a feature on both platforms simultaneously, you'll be hooked.
Remember, the goal is not to share everything, but to share the right things. Keep your shared shelf tidy, and your platform-specific rooms comfortable. With Kotlin Multiplatform, you can have the best of both worlds: a single source of truth for your logic, and a native experience for your users. Happy coding!
Comments (0)
Please sign in to post a comment.
Don't have an account? Create one
No comments yet. Be the first to comment!