refactor: Replace Nordic, use Kable backend for Desktop and Android with BLE support (#4818)

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
James Rich 2026-03-16 18:06:43 -05:00 committed by GitHub
parent 0e5f94579f
commit 0b2e89c46f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
79 changed files with 1980 additions and 2965 deletions

View file

@ -1,30 +1,31 @@
# Decision: BLE KMP Strategy
> Date: 2026-03-10 | Status: **Decided — Phase 1 complete**
> Date: 2026-03-16 | Status: **Decided — Fully Migrated to Kable**
## Context
`core:ble` needed to support non-Android targets. Nordic's KMM-BLE-Library is Android/iOS only (no Desktop/Web). KABLE supports all KMP targets but lacks mock modules.
`core:ble` needed to support non-Android targets. Nordic's Kotlin-BLE-Library, while mature on Android and actively tested in the app, was primarily Android/iOS focused and lacked support for Desktop (JVM) targets. Kable natively supports all Kotlin Multiplatform targets (Android, Apple, Desktop/JVM, Web).
Initially, we implemented an **Interface-Driven "Nordic Hybrid" Abstraction** (keeping Nordic on Android behind `commonMain` interfaces) to wait and see if Nordic expanded their KMP support.
However, as Desktop integration advanced, we found the need for a unified BLE transport.
## Decision
**Interface-Driven "Nordic Hybrid" Abstraction:**
**Migrate entirely to Kable:**
- `commonMain`: Pure Kotlin interfaces (`BleConnection`, `BleScanner`, `BleDevice`, `BleConnectionFactory`, etc.) — zero platform imports
- `androidMain`: Nordic KMM-BLE-Library implementations behind those interfaces
- `jvm()` target added — interfaces compile fine; no JVM BLE implementation needed yet
- Future: KABLE or alternative can implement the same interfaces for Desktop/iOS without touching core logic
**BLE library decision: Stay on Nordic, wait.** Our abstraction layer is clean — switching backends later is a bounded, mechanical task (~6 files, ~400 lines). Nordic is actively developing. We don't currently need real BLE on JVM/iOS. If Nordic hasn't shipped KMP by the time we need iOS, revisit KABLE.
- We migrated all BLE transport logic across Android and Desktop to use Kable.
- The `commonMain` interfaces (`BleConnection`, `BleScanner`, `BleDevice`, `BluetoothRepository`, etc.) remain, but their core implementations (`KableBleConnection`, `KableBleScanner`) are now entirely shared in `commonMain`.
- The Android-specific Nordic dependencies (`no.nordicsemi.kotlin.ble:*`) and the Nordic DFU library were completely excised from the project.
- OTA Firmware updates on Android were successfully refactored to use the Kable-based `BleOtaTransport`.
## Consequences
- `core:ble` compiles on JVM and is included in CI smoke compile
- No Nordic types leak into `commonMain`
- Desktop simply doesn't inject BLE bindings
- Migration cost to KABLE is predictable and bounded
- **Maximal Code Deduplication:** The BLE implementation is completely shared across Android and Desktop in `core:ble/commonMain`.
- **Future-Proofing:** Adding an `iosMain` target in the future will be trivial, as it can leverage the same shared Kable abstractions.
- **Lost Nordic Mocks:** Kable lacks the comprehensive mock infrastructure of the Nordic library. Consequently, several complex BLE OTA unit tests had to be deprecated. Re-establishing this test coverage using custom Kable fakes is an ongoing technical debt item.
## Archive
Full analysis: [`archive/ble-kmp-strategy.md`](../archive/ble-kmp-strategy.md)
- Original Hybrid Analysis: [`archive/ble-kmp-strategy.md`](../archive/ble-kmp-strategy.md)
- Original Abstraction Plan: [`archive/ble-kmp-abstraction-plan.md`](../archive/ble-kmp-abstraction-plan.md)

View file

@ -29,7 +29,7 @@ Modules that share JVM-specific code between Android and desktop now standardize
| `core:prefs` | ✅ | ✅ | Preferences layer |
| `core:network` | ✅ | ✅ | Ktor, `StreamFrameCodec`, `TcpTransport` |
| `core:data` | ✅ | ✅ | Data orchestration |
| `core:ble` | ✅ | ✅ | BLE abstractions in commonMain; Nordic in androidMain |
| `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, `jvmAndroidMain` + `jvmMain` actuals |
@ -103,7 +103,7 @@ Based on the latest codebase investigation, the following steps are proposed to
|---|---|---|
| Navigation 3 parity model (shared `TopLevelDestination` + platform adapters) | ✅ Done | Both shells use shared enum + parity tests. See [`decisions/navigation3-parity-2026-03.md`](./decisions/navigation3-parity-2026-03.md) |
| Hilt → Koin | ✅ Done | See [`decisions/koin-migration.md`](./decisions/koin-migration.md) |
| BLE abstraction (Nordic Hybrid) | ✅ Done | See [`decisions/ble-strategy.md`](./decisions/ble-strategy.md) |
| BLE abstraction (Kable) | ✅ Done | See [`decisions/ble-strategy.md`](./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 |
@ -141,7 +141,7 @@ Extracted to shared `commonMain` (no longer app-only):
| 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 |
| Nordic BLE | `2.0.0-alpha16` | Behind abstraction boundary |
| 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.