跪拜 Guibai
← All articles
Kotlin · KMP · Compose Multiplatform · Cross-Platform Development · Android · iOS · Desktop

Gainful: A Three-Platform Financial Tracker Built with Kotlin Multiplatform and Compose

By Yoke ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Kotlin Multiplatform has matured enough that a solo developer can ship a polished, data-driven app to three platforms without maintaining separate UI codebases. The concrete pain points documented here — datetime handling, iOS framework config, module namespacing — are exactly what teams evaluating KMP need to budget for.

Summary

Gainful is a personal financial tracking app that runs on Android, iOS, and desktop from a single Kotlin codebase. It uses Compose Multiplatform for a declarative UI layer, Room for local storage, Ktor for networking, and Koin for dependency injection, all structured under Clean Architecture with an MVI data flow.

The app covers dashboard metrics, holdings visualization with treemap charts, transaction filtering, and CSV import/export. The developer, Yoke, walks through the real-world friction points: replacing java.time with kotlinx-datetime in common code, configuring iOS static frameworks, and avoiding resource ID collisions across Gradle modules.

Development time was compressed by sharing nearly all code across platforms, with desktop hot reload speeding up UI iteration. The project is available on GitHub as a reference for anyone evaluating KMP for production cross-platform work.

Takeaways
Room in KMP requires BundledSQLiteDriver; the database file gets packaged into the app rather than created on first launch.
java.time is unavailable in commonMain — kotlinx-datetime is the required replacement for cross-platform date handling.
iOS targets need static framework binaries and a configured Team ID in the Gradle build script.
Multi-module Compose projects must use per-module resource namespaces to prevent generated resource ID conflicts.
Desktop hot reload works out of the box and significantly accelerates UI development.
Ktor serves as the networking layer with type-safe API interfaces shared across all three platforms.
Compose Resources handles i18n through standard Android-style strings.xml files, with separate files per language.
Conclusions

The project's real value is as a reference architecture: it validates that Google's Now in Android patterns (Clean Architecture, MVI) port cleanly to KMP without major adaptation.

Room's KMP support is surprisingly seamless — the API surface is nearly identical to Android-native Room, which lowers the migration barrier for existing Android teams.

The three documented pitfalls (datetime, iOS static frameworks, resource IDs) are not KMP bugs but platform impedance mismatches that every cross-platform framework hits in some form.

Desktop as a development target doubles as a fast iteration loop; the hot reload capability makes it the de facto development environment even when the primary target is mobile.

Concepts & terms
Kotlin Multiplatform (KMP)
A Kotlin SDK that allows sharing business logic across Android, iOS, desktop, and web while keeping platform-specific code where necessary. Unlike Flutter or React Native, it does not enforce a single UI framework.
Compose Multiplatform
JetBrains' extension of Jetpack Compose that runs declarative UI code on Android, iOS, desktop, and web from a single Kotlin codebase.
MVI (Model-View-Intent)
A unidirectional data flow architecture where user actions produce Intents, a ViewModel reduces them into immutable State, and the View renders that state. It makes UI state predictable and testable.
Clean Architecture
A layered architecture pattern that separates code into domain, data, and presentation layers with strict dependency rules, making business logic independent of frameworks and UI.
Room
Google's ORM library for SQLite that provides compile-time verification of SQL queries. In KMP, it uses a bundled SQLite driver to work across platforms.
Ktor
JetBrains' asynchronous HTTP client and server framework, designed as the standard networking library for Kotlin Multiplatform projects.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗