Files
sanderling/examples/sample-app/composeApp/build.gradle.kts
T
pj 5b5594ee05 Port sample-app to KMP with Android, iOS, Web, and SQLite (#16)
* chore(sample-app): hoist gradle wrapper to sample-app root

* chore(sample-app): add KMP root gradle config

* chore(sample-app): add composeApp KMP module build config

* chore(sample-app): add Android manifest for composeApp

* feat(sample-app): add shared domain models and auth constants

* feat(sample-app): add shared number and date formatting

* feat(sample-app): add cross-platform storage, clock, and id

* feat(sample-app): add shared repository with file-backed state

* feat(sample-app): add shared in-memory navigator

* feat(sample-app): add Compose theme and design tokens

* feat(sample-app): add shared UI components (icons, screen, widgets)

* feat(sample-app): add Login and Home pages

* feat(sample-app): add AddAccount, Ledger, AddTransaction pages

* feat(sample-app): add App root composable with routing

* feat(sample-app): add Android Application and Activity hosting Compose UI

* chore(sample-app): remove legacy android module (replaced by composeApp)

* chore(sample-app): bump to latest stable Kotlin/AGP/Compose deps

* fix(sample-app): make iOS compile (drop @Volatile, set bundleId)

* feat(sample-app): add xcodegen spec, SwiftUI host, and iOS Info.plist

* chore(sample-app): ignore build artifacts and generated xcodeproj

* chore(sample-app): update justfile for composeApp layout, add ios target

* docs(sample-app): rewrite README for KMP + iOS flow

* refactor(sample-app): drop in-app status bar and formatClock

* feat(sample-app): add SQLDelight schema and per-platform drivers

* refactor(sample-app): back Repository with SQLite, drop file serializer

* feat(sample-app): wire native back on Android and iOS edge swipe

* feat(sample-app): semantic roles, labels, and a11y descriptions

* fix(sample-app): link libsqlite3 for iOS target

SQLDelight's native driver needs libsqlite3.tbd on iOS; without it the
linker fails with undefined _sqlite3_bind_blob and friends.

* refactor(sample-app): abstract storage behind LedgerStore interface

Platform-specific createLedgerStore() returns a SqlLedgerStore backed
by SQLDelight on Android + iOS. Opens the door for a pure in-memory
web implementation that does not require a SQLite driver.

* feat(sample-app): add wasmJs target with in-memory LedgerStore

Wires a Compose Multiplatform browser canvas entry point. The web
implementation of LedgerStore is an in-memory model with localStorage
persistence, so it does not need a SQLite driver. Back navigation maps
the browser back button to the same BackHandler contract Android and
iOS use.

* chore(sample-app): settings + gitignore for wasmJs dev run

Registers the Node.js distributions repository and switches
repositoriesMode to PREFER_PROJECT so the Kotlin wasmJs plugin can
download its toolchain. Adds kotlin-js-store (lockfile) and ignores
runs/, web screenshots, playwright-mcp scratch output.

* fix(sample-app): singularize transaction count on Home

Shows "1 transaction" not "1 transactions" for accounts with a single
transaction; falls back to "$count transactions" otherwise.
2026-04-19 15:56:33 +07:00

131 lines
3.4 KiB
Kotlin

import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
id("com.android.application")
kotlin("multiplatform")
id("org.jetbrains.kotlin.plugin.compose")
id("org.jetbrains.compose")
id("app.cash.sqldelight")
}
val uatuVersion = findProperty("uatu.version") as String? ?: "0.0.0-dev"
val sqldelightVersion = "2.3.2"
kotlin {
@OptIn(ExperimentalKotlinGradlePluginApi::class)
compilerOptions {
freeCompilerArgs.add("-Xexpect-actual-classes")
optIn.add("kotlin.js.ExperimentalWasmJsInterop")
}
androidTarget {
@OptIn(ExperimentalKotlinGradlePluginApi::class)
compilerOptions {
jvmTarget.set(JvmTarget.JVM_17)
}
}
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64(),
).forEach { target ->
target.binaries.framework {
baseName = "ComposeApp"
isStatic = true
binaryOption("bundleId", "dev.uatu.sample")
}
}
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
outputModuleName.set("composeApp")
browser {
commonWebpackConfig {
outputFileName = "composeApp.js"
}
}
binaries.executable()
}
applyHierarchyTemplate {
common {
group("sql") {
withAndroidTarget()
group("apple") {
group("ios") {
withIosX64()
withIosArm64()
withIosSimulatorArm64()
}
}
}
group("wasmJs") {
withWasmJs()
}
}
}
sourceSets {
commonMain.dependencies {
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material3)
implementation(compose.ui)
implementation(compose.components.resources)
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
implementation("app.cash.sqldelight:runtime:$sqldelightVersion")
implementation("app.cash.sqldelight:coroutines-extensions:$sqldelightVersion")
}
androidMain.dependencies {
implementation("androidx.activity:activity-compose:1.13.0")
implementation("app.cash.sqldelight:android-driver:$sqldelightVersion")
implementation("io.github.priyanshujain:sdk-android:$uatuVersion")
}
iosMain.dependencies {
implementation("app.cash.sqldelight:native-driver:$sqldelightVersion")
}
}
}
android {
namespace = "dev.uatu.sample"
compileSdk = 36
defaultConfig {
applicationId = "dev.uatu.sample"
minSdk = 24
targetSdk = 36
versionCode = 1
versionName = uatuVersion
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
buildTypes {
debug {
isDebuggable = true
}
}
sourceSets["main"].apply {
manifest.srcFile("src/androidMain/AndroidManifest.xml")
res.srcDirs("src/androidMain/res")
}
}
sqldelight {
databases {
create("LedgerDatabase") {
packageName.set("dev.uatu.sample.db")
}
}
}