12 KiB
KMP Migration Status
Last updated: 2026-03-21
Single source of truth for Kotlin Multiplatform migration progress. For the forward-looking roadmap, see roadmap.md. For completed decision records, see decisions/.
Summary
Meshtastic-Android has completed its Android-first structural KMP migration across core logic and feature modules, with full JVM cross-compilation validated in CI. The desktop target has a working Navigation 3 shell, TCP transport with full mesh handshake, and multiple features wired with real screens.
Modules that share JVM-specific code between Android and desktop now standardize on the meshtastic.kmp.jvm.android convention plugin, which creates jvmAndroidMain via Kotlin's hierarchy template API instead of manual dependsOn(...) source-set wiring.
Module Inventory
Core Modules (20 total)
| Module | KMP? | JVM target? | Notes |
|---|---|---|---|
core:proto |
✅ | ✅ | Protobuf definitions |
core:common |
✅ | ✅ | Utilities, jvmAndroidMain source set |
core:model |
✅ | ✅ | Domain models, jvmAndroidMain source set |
core:repository |
✅ | ✅ | Domain interfaces |
core:di |
✅ | ✅ | Dispatchers, qualifiers |
core:navigation |
✅ | ✅ | Shared Navigation 3 routes |
core:resources |
✅ | ✅ | Compose Multiplatform resources |
core:datastore |
✅ | ✅ | Multiplatform DataStore |
core:database |
✅ | ✅ | Room KMP |
core:domain |
✅ | ✅ | UseCases |
core:prefs |
✅ | ✅ | Preferences layer |
core:network |
✅ | ✅ | Ktor, StreamFrameCodec, TcpTransport, SerialTransport |
core:data |
✅ | ✅ | Data orchestration |
core:ble |
✅ | ✅ | Kable multiplatform BLE abstractions in commonMain |
core:nfc |
✅ | ✅ | NFC contract in commonMain; hardware in androidMain |
core:service |
✅ | ✅ | Service layer; Android bindings in androidMain |
core:ui |
✅ | ✅ | Shared Compose UI, pure KMP QR generator, jvmAndroidMain + jvmMain actuals |
core:testing |
✅ | ✅ | Shared test doubles, fakes, and utilities for commonTest |
core:api |
❌ | — | Android-only (AIDL). Intentional. |
core:barcode |
❌ | — | Android-only (CameraX). Flavor split minimised to decoder factory only (ML Kit / ZXing). Shared contract in core:ui. |
18/20 core modules are KMP with JVM targets. The 2 Android-only modules are intentionally platform-specific, with shared contracts already abstracted into core:ui/commonMain.
Feature Modules (8 total — 7 KMP with JVM)
| Module | UI in commonMain? | Desktop wired? |
|---|---|---|
feature:settings |
✅ | ✅ ~35 real screens; fully shared settingsGraph and UI |
feature:node |
✅ | ✅ Adaptive list-detail; fully shared nodesGraph, PositionLogScreen, and NodeContextMenu |
feature:messaging |
✅ | ✅ Adaptive contacts + messages; fully shared contactsGraph, MessageScreen, ContactsScreen, and MessageListPaged |
feature:connections |
✅ | ✅ Shared ConnectionsScreen with dynamic transport detection |
feature:intro |
✅ | — |
feature:map |
✅ | Placeholder; shared NodeMapViewModel |
feature:firmware |
— | Placeholder; DFU is Android-only |
feature:widget |
❌ | — |
Desktop Module
Working Compose Desktop application with:
- Navigation 3 shell (
NavigationRail+NavDisplay) using shared routes - Full Koin DI graph (stubs + real implementations)
- TCP, Serial/USB, and BLE transports with auto-reconnect and full
want_confighandshake - Adaptive list-detail screens for nodes and contacts
- Dynamic Connections screen with automatic discovery of platform-supported transports (TCP, Serial/USB, BLE)
- Desktop language picker backed by
UiPreferencesDataSource.locale, with immediate Compose Multiplatform resource updates - Navigation-preserving locale switching via
Main.ktstaticCompositionLocalOfrecomposition instead of recreating the Nav3 backstack - Node detail metrics screens (Device, Environment, Signal, Power, Pax) wired with shared KMP + Vico charts
- Feature-driven Architecture: Desktop navigation completely relies on feature modules via
commonMainexported graphs (settingsGraph,nodesGraph,contactsGraph, etc.), reducing the desktop module to a simple host shell. - Native notifications and system tray icon wired via
DesktopNotificationManager - Native release pipeline generating
.dmg(macOS),.msi(Windows), and.deb(Linux) installers in CI
Scorecard
| Area | Score | Notes |
|---|---|---|
| Shared business/data logic | 9/10 | All core layers shared; RadioTransport interface unified |
| Shared feature/UI logic | 9/10 | All 7 KMP; feature:connections unified; cross-platform deduplication complete |
| Android decoupling | 9/10 | No known java.* calls in commonMain; app module extraction in progress (navigation, connections, background services, and widgets extracted) |
| Multi-target readiness | 9/10 | Full JVM; release-ready desktop; iOS simulator builds compiling successfully |
| CI confidence | 9/10 | 25 modules validated (including feature:connections); native release installers automated |
| DI portability | 8/10 | Koin annotations in commonMain; supportedDeviceTypes injected per platform |
| Test maturity | 9/10 | Mokkery, Turbine, and Kotest integrated; property-based testing established; broad coverage across all 8 features |
See
decisions/architecture-review-2026-03.mdfor the full gap analysis.
Completion Estimates
| Lens | % |
|---|---|
| Android-first structural KMP | ~100% |
| Shared business logic | ~98% |
| Shared feature/UI | ~97% |
| True multi-target readiness | ~85% |
| "Add iOS without surprises" | ~100% |
Proposed Next Steps for KMP Migration
Based on the latest codebase investigation, the following steps are proposed to complete the multi-target and iOS-readiness migrations:
- Wire Desktop Features: Complete desktop UI wiring for
feature:introand implement a shared fallback forfeature:map(which is currently a placeholder on desktop). - Decouple Firmware DFU:
feature:firmwarerelies on Android-only DFU libraries. Evaluate wrapping this in a shared KMP interface or extracting it into a separate plugin to allow the corefeature:firmwaremodule to be fully utilized on desktop/iOS. - Flesh out iOS Actuals: Complete the actual implementations for iOS UI stubs (e.g.,
AboutLibrariesLoader,rememberOpenMap,SettingsMainScreen) that were recently added to unblock iOS compilation. - Boot iOS Target: Set up an initial skeleton Xcode project to start running the now-compiling
iosSimulatorArm64/iosArm64binaries on a real simulator/device.
Key Architecture Decisions
| Decision | Status | Details |
|---|---|---|
Navigation 3 parity model (shared TopLevelDestination + platform adapters) |
✅ Done | Both shells use shared enum + parity tests. See decisions/navigation3-parity-2026-03.md |
| Hilt → Koin | ✅ Done | See decisions/koin-migration.md |
| BLE abstraction (Kable) | ✅ Done | See decisions/ble-strategy.md |
| Material 3 Adaptive (JetBrains) | ✅ Done | Version 1.3.0-alpha06 aligned with CMP 1.11.0-alpha04 |
| JetBrains lifecycle/nav3 alias alignment | ✅ Done | All forked deps use jetbrains-* prefix in version catalog; core:data commonMain uses JetBrains lifecycle runtime |
| Expect/actual consolidation | ✅ Done | 7 pairs eliminated; 15+ genuinely platform-specific retained |
| Transport deduplication | ✅ Done | StreamFrameCodec, TcpTransport, and SerialTransport shared in core:network |
| Transport Lifecycle Unification | ✅ Done | SharedRadioInterfaceService orchestrates auto-reconnect, connection state, and heartbeat uniformly across Android and Desktop. |
| Database Parity | ✅ Done | DatabaseManager is pure KMP, giving iOS and Desktop support for multiple connected nodes with LRU caching. |
| Emoji picker unification | ✅ Done | Single commonMain implementation replacing 3 platform variants |
| Cross-platform deduplication pass | ✅ Done | Extracted shared AlertHost, SharedDialogs, PlaceholderScreen, ThemePickerDialog, formatLogsTo(), handleNodeAction(), findNodeByNameSuffix() to commonMain; eliminated ~200 lines of duplicated code across Android/desktop |
Navigation Parity Note
- Desktop and Android both use the shared
TopLevelDestinationenum fromcore:navigation/commonMain— no separateDesktopDestinationremains. - Both shells iterate
TopLevelDestination.entrieswith shared icon mapping fromcore:ui(TopLevelDestinationExt.icon). - Desktop locale changes now trigger a full subtree recomposition from
Main.ktwithout resetting the shared Navigation 3 backstack, so translated labels update in place. - Firmware remains available as an in-flow route instead of a top-level destination, matching Android information architecture.
- Android navigation graphs are decoupled and extracted into their respective feature modules, aligning with the Desktop architecture.
- Parity tests exist in
core:navigation/commonTest(NavigationParityTest) anddesktop/test(DesktopTopLevelDestinationParityTest). - Remaining parity work is documented in
decisions/navigation3-parity-2026-03.md: serializer registration validation and platform exception tracking.
App Module Thinning Status
All major ViewModels have now been extracted to commonMain and no longer rely on Android-specific subclasses. Platform-specific dependencies (like android.net.Uri or Location permissions) have been successfully isolated behind injected core:repository interfaces (e.g., FileService, LocationService).
The extraction of all feature-specific navigation graphs, background services, and widgets out of :app is complete. The :app module now only serves as the root DI assembler and NavHost container.
Extracted to shared commonMain (no longer app-only):
SettingsViewModel→feature:settings/commonMainRadioConfigViewModel→feature:settings/commonMainDebugViewModel→feature:settings/commonMainMetricsViewModel→feature:node/commonMainUIViewModel→core:ui/commonMainChannelViewModel→feature:settings/commonMainNodeMapViewModel→feature:map/commonMain(Shared logic for node-specific maps)BaseMapViewModel→feature:map/commonMain(Core contract for all maps)
Extracted to core KMP modules:
- Android Services, WorkManager Workers, and BroadcastReceivers →
core:service/androidMain - BLE and USB/Serial radio connections →
core:network/androidMain - TCP radio connections and mDNS/NSD Service Discovery →
core:network/commonMain(with AndroidNsdManagerand DesktopJmDNSimplementations)
Remaining to be extracted from :app or unified in commonMain:
MapViewModel(Unify Google/F-Droid flavors into a singlecommonMainclass consuming aMapConfigProviderinterface)- Top-level UI composition (
ui/Main.kt)
Prerelease Dependencies
| Dependency | Version | Why |
|---|---|---|
| Compose Multiplatform | 1.11.0-alpha04 |
Required for JetBrains Adaptive 1.3.0-alpha06 |
| Koin | 4.2.0-RC2 |
Nav3 + K2 compiler plugin support |
| JetBrains Lifecycle | 2.10.0-beta01 |
Multiplatform ViewModel/lifecycle |
| JetBrains Navigation 3 | 1.1.0-alpha04 |
Multiplatform navigation |
| Kable BLE | 0.42.0 |
Provides fully multiplatform BLE support |
Policy: Stable by default. RC when it unlocks KMP functionality. Alpha only behind hard abstraction seams. Do not downgrade CMP or Koin — they enable critical KMP features.
References
- Roadmap:
docs/roadmap.md - Agent guide:
AGENTS.md - Playbooks:
docs/agent-playbooks/ - Decision records:
docs/decisions/