Kgothatso Ngako f301a924fd fix: build a release apk, by dropping the app's copies of what the library already ships
`:composeApp:assembleRelease` dies in `mergeDexRelease`:

    Type com.machankura.compose.ui.composable.widgets.nfc.ComposableSingletons$HceMonitorKt
    is defined multiple times:
      composeApp/build/intermediates/project_dex_archive/release/dexBuilderRelease/out/...
      lightning-kmp-app/library/build/.transforms/.../bundleLibRuntimeToDirAndroidMain_dex/...

Both paths are ours. composeApp compiles a class, lightning-kmp-app's `:library`
compiles the same fully-qualified name, and D8 will not merge the two into one
apk. Debug never objected, because it packages the per-project dex archives as
they stand and only the release merge walks the whole set looking for
collisions -- so this has been true for a while and surfaced the first time
anyone asked for a release. There were two such copies, and fixing the first
only uncovered the second.

**The NFC widgets were renamed by directory, not by package.** 9abdf42
("Refactor torch to mantra", 2026-07-15) moved three files under
`composeApp/src/androidMain/kotlin/press/mantra/compose/ui/composable/widgets/nfc/`
and left their `package com.machankura.compose.ui.composable.widgets.nfc` line
alone. The library holds the same three at
`fr/acinq/phoenix/compose/ui/widgets/nfc/`, also declaring `com.machankura...`.
So three directories say three different things and the package -- the only one
of them D8 reads -- says one. `NfcState.kt` is byte-identical across the two;
`HceMonitor.kt` and `NfcReaderMonitor.kt` differ in a single import line,
`press.mantra` against `fr.acinq.phoenix` for `ModalBottomSheet`.

Deleted the app's three rather than renaming their package to `press.mantra`,
which was the other way out and is the worse one. `NfcStateRepository` is an
`object`. The library's own `HceService` and `NfcReaderCallback` import it under
the `com.machankura` name, and `MainActivity:50-52` reaches for it fully
qualified. Renaming the app's copy would have compiled, dexed and run -- with
two singletons: one that `MainActivity` sets back to `Inactive` on a new intent,
and a different one the HCE service is collecting. Nothing would warn, because
by then the names genuinely differ; the tag emulation would just not stop.
Deleting leaves one object, and `MainActivity`'s existing fully-qualified
references land on it untouched. The two composables went along with it as dead
weight -- nothing outside their own files names `HceMonitor` or
`NfcReaderMonitor` anywhere in composeApp.

**The sqldelight databases were a second copy of the same kind.** With the NFC
clash gone, `mergeDexRelease` came back with
`fr.acinq.phoenix.db.sqldelight.AppDatabase$Companion`, out of the same pair of
directories. composeApp's build.gradle.kts declared `ChannelsDatabase`,
`PaymentsDatabase` and `AppDatabase` at packageName
`fr.acinq.phoenix.db.sqldelight` from `.sq` sources under
`src/commonMain/sqldelight`; the library's build.gradle.kts declares the same
three names, the same package and the same layout, over a tree `diff -rq` reports
as identical file for file. Both plugins ran, and both generated the same
classes.

Removed the plugin alias and the whole `sqldelight { }` block from composeApp
and deleted its 32 `.sq`/`.sqm` files, rather than moving the app's generated
package to `press.mantra`. Nothing under `press.mantra` imports
`fr.acinq.phoenix.db` -- grep finds no file -- and nothing there touches
`app.cash.sqldelight` either, against 18 files in the library that do. The app
was generating a database layer it has never opened. Renaming would have kept
generating it, and kept a second identical schema in the tree for someone to
edit and then wonder why nothing moved. A comment sits where the plugin alias
was, since the absence is the load-bearing part and an alias is a one-line thing
to add back by reflex.

**The sqldelight driver dependencies stay.** `-android-driver`,
`-sqlite-driver`, `-native-driver`, `-runtime` and `-coroutines-extensions` are
still declared in composeApp. They are runtime drivers rather than code
generation, and are very likely redundant -- the library declares the same five
as `implementation`, which still carries them onto the runtime classpath. But
they are not what D8 objected to, and a missing driver fails when the app opens
a database on one platform, not when it builds, so retiring them wants more
evidence than a green build and is its own change.

**What this does not fix.** Anything else copied into both trees fails exactly
this way, and only on release. A scan of the two source sets for colliding
fully-qualified top-level types is clean now -- the three NFC files were the
only ones -- but that scan reads `.kt`, and the sqldelight collision had no
`.kt` to find. Generated code is where the next one hides: Room and
compose-resources generate into composeApp, and the library generates
compose-resources too, which is why settings.gradle.kts keeps the submodule's
project named `:library` and renames only the coordinate.

**Verified.** `:composeApp:assembleRelease` produces the unsigned 36MB apk;
`:composeApp:assembleDebug` builds. 906 tests pass, 576 jvm over 69 classes and
330 android over 41 classes, unchanged from before the change since it adds
none. The apk was not installed, so that NFC still works on a device is read
from the code -- one `NfcStateRepository`, the one both sides were already
naming -- rather than watched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 22:13:32 +02:00
2026-07-05 23:21:02 +02:00
2026-07-28 09:10:20 +02:00
2026-03-24 04:47:19 +02:00
2026-03-23 01:41:39 +02:00
2026-03-23 01:41:39 +02:00
2026-03-23 01:41:39 +02:00

This is a Kotlin Multiplatform project targeting Android, iOS, Desktop (JVM).

  • /composeApp is for code that will be shared across your Compose Multiplatform applications. It contains several subfolders:

    • commonMain is for code thats common for all targets.
    • Other folders are for Kotlin code that will be compiled for only the platform indicated in the folder name. For example, if you want to use Apples CoreCrypto for the iOS part of your Kotlin app, the iosMain folder would be the right place for such calls. Similarly, if you want to edit the Desktop (JVM) specific part, the jvmMain folder is the appropriate location.
  • /iosApp contains iOS applications. Even if youre sharing your UI with Compose Multiplatform, you need this entry point for your iOS app. This is also where you should add SwiftUI code for your project.

Build and Run Android Application

To build and run the development version of the Android app, use the run configuration from the run widget in your IDEs toolbar or build it directly from the terminal:

  • on macOS/Linux
    ./gradlew :composeApp:assembleDebug
    
  • on Windows
    .\gradlew.bat :composeApp:assembleDebug
    

Build and Run Desktop (JVM) Application

To build and run the development version of the desktop app, use the run configuration from the run widget in your IDEs toolbar or run it directly from the terminal:

  • on macOS/Linux
    ./gradlew :composeApp:run
    
  • on Windows
    .\gradlew.bat :composeApp:run
    

Build and Run iOS Application

To build and run the development version of the iOS app, use the run configuration from the run widget in your IDEs toolbar or open the /iosApp directory in Xcode and run it from there.


Learn more about Kotlin Multiplatform

Description
Mantra as a kotlin multiplatform project
https://mantra.press
Readme 8.2 MiB
Languages
Kotlin 100%