
Collection of reusable UI components, utilities and integrations: shared network client configuration, Supabase and Room helpers, DataStore prefs, QR/pay support, localization sync.
JchuComponents is a collection of Kotlin Multiplatform utilities, Android Jetpack Compose components and native SwiftUI components for building Android and Apple applications with less repeated infrastructure.
JchuComponents 3 is under active development on the
v3branch. Pre-release versions follow3.0.0-alphaNN,3.0.0-betaNNand3.0.0-rcNNbefore the stable3.0.0release.
Apple targets are ARM64 only: iosArm64, iosSimulatorArm64 and
macosArm64. Intel x64 targets are not supported.
| Module | Android | iOS | macOS | Purpose |
|---|---|---|---|---|
jchucomponents-foundation |
✅ | ✅ | ✅ | Portable state, dates, text and utilities |
jchucomponents-network |
✅ | ✅ | ✅ | Ktor client configuration, resources and flow helpers |
jchucomponents-prefs |
✅ | ✅ | ✅ | Multiplatform DataStore preferences |
jchucomponents-pay |
✅ | ✅ | ✅ | Shared payment models and Android RevenueCat integration |
jchucomponents-pay-kmp |
✅ | ✅ | — | Shared RevenueCat purchases for Android and iOS (iOS ARM targets) |
jchucomponents-qr |
✅ | ✅ | ✅ | QR encoding and decoding utilities |
jchucomponents-room |
✅ | ✅ | — | Room 3 runtime configuration and SQL migrations |
jchucomponents-supabase |
✅ | ✅ | ✅ | Supabase Auth, database, realtime, Edge Functions and Koin setup |
jchucomponents-core |
✅ | ✅ | ✅ | Android architecture and lifecycle utilities with portable core APIs |
jchucomponents-ktx |
✅ | ✅ | ✅ | Android APIs and portable Kotlin extension compatibility helpers |
jchucomponents-ui-kmp |
✅ | ✅ | — | Portable Compose components without Android resource dependencies (iOS ARM targets) |
jchucomponents-ui |
✅ | — | — | Android adapter for resource- and platform-dependent components; re-exports jchucomponents-ui-kmp
|
jchucomponents-pdf |
✅ | — | — | Remote PDF reader with generated Navigation 3 routes |
jchucomponents-pdf-kmp |
✅ | ✅ | — | Compose Multiplatform PDF reader with Android PdfRenderer and iOS PDFKit (iOS ARM targets) |
jchucomponents-navigation3 |
✅ | ✅ | — | Compose Multiplatform Navigation 3 annotations, helpers and KSP processor (iOS ARM targets) |
jchucomponents-navigation3-di |
✅ | ✅ | — | Dependency injection helpers for generated Navigation 3 entries on JVM and iOS ARM targets |
jchucomponents-app-updates |
✅ | — | — | Google Play update StateFlow and lifecycle controller; apps own the UI |
jchucomponents-bom |
✅ | — | — | Aligned Android module versions |
The Swift package complements the shared Kotlin code with
JchuComponentsCore, JchuComponentsExtensions, JchuComponentsSwiftUI,
JchuComponentsNavigation and the optional native JchuComponentsPay product.
JchuComponentsSwiftUI also includes JchuPdfReaderView for remote PDFs on iOS.
The examples below use 3.0.0-alpha25. Replace it with the
latest available release.
KMP artifacts are published to Maven Central. Add only the modules needed by your shared source set:
kotlin {
sourceSets {
commonMain.dependencies {
implementation(
"io.github.jeluchu:jchucomponents-foundation:3.0.0-alpha25"
)
implementation(
"io.github.jeluchu:jchucomponents-network:3.0.0-alpha25"
)
implementation(
"io.github.jeluchu:jchucomponents-ui-kmp:3.0.0-alpha25"
)
}
}
}The available Maven Central artifacts are:
io.github.jeluchu:jchucomponents-foundation
io.github.jeluchu:jchucomponents-core
io.github.jeluchu:jchucomponents-ktx
io.github.jeluchu:jchucomponents-network
io.github.jeluchu:jchucomponents-ui-kmp
io.github.jeluchu:jchucomponents-pdf-kmp
io.github.jeluchu:jchucomponents-pay
io.github.jeluchu:jchucomponents-pay-kmp
io.github.jeluchu:jchucomponents-prefs
io.github.jeluchu:jchucomponents-qr
io.github.jeluchu:jchucomponents-room
io.github.jeluchu:jchucomponents-supabase
io.github.jeluchu:jchucomponents-navigation3
io.github.jeluchu:jchucomponents-navigation3-di
io.github.jeluchu:jchucomponents-bom
io.github.jeluchu:jchucomponents-app-updates
jchucomponents-ktx keeps its Android extensions in androidMain; its shared
API provides the existing boolean, list, and serialization package names plus
a portable Job helper, backed by jchucomponents-foundation where an
equivalent already exists. Dispatcher-based coroutine helpers remain in
androidMain.
jchucomponents-pay keeps its existing Android RevenueCat API and shared
payment models. For purchases from common Kotlin on Android and iOS, use
jchucomponents-pay-kmp; it wraps RevenueCat's official Kotlin Multiplatform
SDK and publishes Android, iosArm64, and iosSimulatorArm64 variants. Its
RevenueCat SDK dependency is limited to Android and iOS, so this artifact does
not currently target macOS.
Most listed artifacts contain Android, iOS ARM64 and macOS ARM64 variants.
The exceptions are jchucomponents-room, jchucomponents-navigation3,
jchucomponents-navigation3-di,
jchucomponents-pdf-kmp, jchucomponents-pay-kmp, and
jchucomponents-ui-kmp, which currently contain
Android, iosArm64, and iosSimulatorArm64; and
jchucomponents-app-updates, which is Android-only and intended for apps
distributed through Google Play. Consumers provide their own update interface.
JitPack's Linux builds do not produce the Apple KLIB variants, so KMP
applications targeting iOS or macOS should use Maven Central.
jchucomponents-foundation also provides JchuLocaleProvider for reading the
system language from shared code. getPlatformLanguageCode() maps Spanish in
the United States to es-US and returns the base language code for other
regional locales; getLanguageTag() exposes the full BCP 47 tag where the
platform provides one.
jchucomponents-ui-kmp contains the UI components that compile for Android,
iosArm64, and iosSimulatorArm64. The Android jchucomponents-ui artifact
keeps the existing Android API and adds the platform-bound parts that use R, Context, View,
Bitmap, Android resource APIs or Android Coil requests. KMP applications
should depend on jchucomponents-ui-kmp directly; Android applications can
continue using jchucomponents-ui as before.
Add JitPack to settings.gradle.kts:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven("https://jitpack.io")
}
}Use the BOM to keep Android modules on the same version:
dependencies {
implementation(
platform("io.github.jeluchu:jchucomponents-bom:3.0.0-alpha25")
)
implementation("com.github.jeluchu.jchucomponents:jchucomponents-ui")
implementation("io.github.jeluchu:jchucomponents-ktx")
}Alternatively, specify each JitPack artifact version directly:
dependencies {
implementation(
"com.github.jeluchu.jchucomponents:jchucomponents-ui:3.0.0-alpha25"
)
implementation(
"com.github.jeluchu.jchucomponents:jchucomponents-pdf:3.0.0-alpha25"
)
}The jchucomponents-pdf artifact provides an Android Compose screen for remote
PDF URLs and is available through JitPack starting with 3.0.0-alpha23.
The library generates its typed PdfReaderRoutes.Reader(url) route and
pdfReaderEntries(backStack) provider, so apps register the provider in their
Navigation 3 entry provider and navigate to the generated route. The artifact is
Android-only and is distributed through JitPack.
For Compose Multiplatform apps, use the Maven Central artifact
io.github.jeluchu:jchucomponents-pdf-kmp. It provides the same reader route on
Android, iosArm64, and iosSimulatorArm64, using PdfRenderer/FileProvider on
Android and PDFKit/native sharing on iOS. Add it to commonMain and register
pdfReaderKmpEntries(backStack); then navigate with
PdfReaderKmpRoutes.Reader(url = remotePdfUrl). The Android-only artifact
remains available unchanged.
For a shared RevenueCat integration on Android and iOS, add the KMP artifact
to commonMain:
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.jeluchu:jchucomponents-pay-kmp:3.0.0-alpha25")
}
}
}Initialize it once with the API key for the current store, then load the active offering and make purchases from shared Kotlin:
import com.jeluchu.pay.revenuecat.kmp.Payment
import com.jeluchu.pay.revenuecat.kmp.initPayment
initPayment(
apiKey = revenueCatApiKey,
isDebug = isDebug,
subscriptionName = "premium"
)
val billingInfo = Payment.loadBillingInfo()
val purchaseResult = Payment.purchaseResult(Payment.ProductsType.MONTHLY)The artifact uses RevenueCat's Kotlin Multiplatform SDK 3.6.0 and currently
targets Android, iosArm64, and iosSimulatorArm64. macOS is not included
because the upstream KMP SDK does not currently publish a macOS target. See
the module README for callbacks, restore,
subscription status, and plan switching.
Add the following package URL in Xcode:
https://github.com/Jeluchu/jchucomponents-spm
Select the JchuComponents product and import the modules needed by the
application:
import JchuComponentsCore
import JchuComponentsExtensions
import JchuComponentsSwiftUIFor the native RevenueCat integration, also select and import the optional product:
import JchuComponentsPayThe Swift package currently requires iOS 26.0 or newer. Releases live in the
dedicated jchucomponents-spm repository so consumers do not clone the full
Kotlin and Android monorepo.
Create a platform-specific Ktor engine from shared Kotlin:
val client = createHttpClient(
HttpClientConfiguration(
baseUrl = "https://example.com/api/",
enableLogging = isDebug,
defaultHeaders = mapOf("X-Client" to "my-app"),
)
)createHttpClient uses Ktor's Android engine on Android and Darwin engine on
iOS and macOS. HttpClientConfiguration controls JSON, caching, timeouts,
response validation, logging and sensitive headers.
The v3 network result models and flow helpers belong to
jchucomponents-network:
import com.jeluchu.jchucomponents.network.extensions.flow.flowCollector
import com.jeluchu.jchucomponents.network.extensions.flow.flowResourceCollector
import com.jeluchu.jchucomponents.network.extensions.handleFailure
import com.jeluchu.jchucomponents.network.models.Failure
import com.jeluchu.jchucomponents.network.models.ResourceTheir previous jchucomponents-core packages were removed during the v3 alpha
cycle.
Apply the shared runtime defaults to a Room 3 builder while retaining access to all application-specific configuration:
val database = Room.databaseBuilder<AppDatabase>(context, "app.db")
.configureJchuRoom {
addMigrations(
sqlMigration(
startVersion = 1,
endVersion = 2,
"ALTER TABLE task ADD COLUMN archived INTEGER NOT NULL DEFAULT 0"
)
)
}
.build()The consuming module must apply KSP and add the
androidx.room3:room3-compiler dependency itself. The runtime module does not
enable destructive migrations or choose a database path.
jchucomponents-navigation3 provides Navigation 3 annotations and back-stack
helpers in commonMain. Its KSP processor can generate typed route keys and
entry providers from shared @Screen composables for Android and iOS ARM
targets (iosArm64 and iosSimulatorArm64). Configure KSP with
kspCommonMainMetadata in the module containing those screens. The host still
supplies its Navigation 3 UI dependency and root NavDisplay.
The optional jchucomponents-navigation3-di artifact provides Koin
inject()/injectByParams() helpers on Android, JVM, iosArm64 and
iosSimulatorArm64. It uses Koin's multiplatform Compose ViewModel APIs and
shares the runtime module's target restrictions: Navigation 3 1.2.0 has no
iosX64 or macOS variants.
The runtime module is independent of jchucomponents-ui. The processor groups
screens by graph, uses each composable name as its default route, and wires
typed arguments, back callbacks, cross-graph navigation and app-owned custom
actions. See the module README for KMP
and Android setup, and the Navigation code generation example in the
Android catalog.
The Swift package also provides JchuComponentsNavigation, a SwiftUI @Screen
macro and graph plugin. Annotated screens generate a typed route enum per graph,
including associated arguments and a switch to each destination view. For KMP
ViewModels, @ScreenKoinViewModel can infer the factory from the property type;
for example, UserViewModel uses KoinWrapper.userForScreen():
@Screen(graph: "Wallet")
struct WalletScreen: View {
@ScreenKoinViewModel
@StateViewModel private var viewModel: UserViewModel
var body: some View { WalletContent(viewModel: viewModel) }
}Factories follow <lowerCamel ViewModel name without ViewModel>ForScreen; the
macro forwards @RouteArgument values by name. Use arguments: [] when the
ViewModel needs no route values, or pass factory: explicitly for a custom
factory name. The iOS catalog registers generated routes on its root
NavigationStack.
Register the shared Supabase client from the same Koin graph used by the app repositories:
val supabaseModule = jchuSupabaseModule(
JchuSupabaseConfig(
url = BuildKonfig.SUPABASE_URL,
publishableKey = BuildKonfig.SUPABASE_PUBLISHABLE_KEY,
auth = JchuSupabaseAuthConfig(
flowType = JchuSupabaseAuthFlowType.PKCE,
alwaysAutoRefresh = true,
autoLoadFromStorage = true,
autoSaveToStorage = true,
defaultRedirectUrl = "com.example.app://auth-callback",
scheme = "com.example.app",
host = "auth-callback"
)
)
)Repositories can depend on JchuSupabaseDatabase, JchuSupabaseAuth,
JchuSupabaseFunctions or JchuSupabaseRealtime. Database and Function
operations return Flow<Resource<Failure, T>>; Auth returns a typed
JchuSupabaseAuthFailure so login screens can distinguish invalid credentials,
unconfirmed email, weak password, rate limiting, session expiry and network
failures.
Registration reports whether Supabase created a session or is waiting for the email confirmation link:
auth.signUpWithEmail(
email = email,
password = password,
redirectUrl = "com.example.app://auth-callback",
metadata = buildJsonObject {
put("display_name", displayName)
put("requested_account_type", "shop")
}
).collect { resource ->
if (resource is Resource.Success) {
val result = resource.data ?: return@collect
if (result.requiresEmailConfirmation) {
showCheckYourEmail()
} else {
openAuthenticatedArea()
}
}
}Forward the Android or Apple deep-link URL to
auth.handleAuthCallback(url). Observe auth.sessionState during app startup;
it differentiates initialization, restored/authenticated sessions, sign-out and
refresh failures. Password recovery, confirmation resend, password update,
manual refresh, local session clearing and local/global sign-out are also
available on JchuSupabaseAuth.
The default Supabase session and PKCE caches are used unless custom
SessionManager and CodeVerifierCache implementations are supplied. Inject
platform-secure implementations when the application requires explicit token
encryption or retention policies. Never use user_metadata as an authorization
source: roles, shop ownership and administrator approval must be enforced by
database data, RLS and server-side functions.
PostgREST RPCs are available through database.rpc(...). Edge Functions accept
idempotency and correlation identifiers without allowing callers to override
the Supabase authentication headers:
functions.invoke<AwardPointsRequest, AwardPointsResponse>(
name = "award-points",
body = request,
options = JchuSupabaseFunctionOptions(
idempotencyKey = request.operationId,
correlationId = request.correlationId
)
)Basic table access remains available for ordinary RLS-protected data:
@Serializable
data class Profile(
val id: String,
val name: String
)
object ProfileTable : JchuSupabaseTable<Profile> {
override val name: String = "profiles"
}
class ProfileRepository(
database: JchuSupabaseDatabase
) {
private val profiles = database.table(ProfileTable)
fun profile(id: String): Flow<Resource<Failure, Profile>> =
profiles.findById(id)
fun updateProfile(profile: Profile): Flow<Resource<Failure, Profile>> =
profiles.updateById(
id = profile.id,
value = profile
)
}The first Supabase module intentionally excludes Storage. It focuses on Auth, PostgREST database access, Realtime wiring, Edge Functions, Koin and resource flow helpers for Android, iOS and macOS KMP apps.
Experimental APIs should be explicitly marked with @RequiresOptIn. Once an
API becomes stable, incompatible changes require a deprecation cycle whenever
practical.
The repository includes an Android catalog in app and an iOS catalog in
samples/iosApp.
Run the repository-wide Android and Kotlin checks:
./gradlew ciCheckThis validates binary APIs, tests, debug and release assemblies, and all local Maven publications. See the consumer guide for focused setup and examples for every module.
Inspect Kotlin formatting without modifying source files:
./gradlew ktlintCheckHTML and plain-text reports are generated under each module's
build/reports/ktlint directory. The check is currently advisory and does not
fail CI. Apply safe automatic fixes to one module at a time:
./gradlew :jchucomponents-foundation:ktlintFormatReview the resulting diff before continuing with another module because some rules, such as file naming, require a manual decision.
Build the Android catalog:
./gradlew :app:assembleDebugVerify the artifacts as an external KMP consumer after publishing them locally:
./gradlew \
:jchucomponents-foundation:publishToMavenLocal \
:jchucomponents-network:publishToMavenLocal \
:jchucomponents-pay:publishToMavenLocal \
:jchucomponents-prefs:publishToMavenLocal \
:jchucomponents-qr:publishToMavenLocal \
:jchucomponents-room:publishToMavenLocal \
:jchucomponents-supabase:publishToMavenLocal
./gradlew \
-p smoke-tests/kmp-consumer \
compileAndroidMain \
compileKotlinIosSimulatorArm64 \
compileKotlinMacosArm64The smoke project reserves io.github.jeluchu for Maven Local, ensuring the
check cannot silently consume an already published release from Maven Central.
Build the Swift package after assembling the XCFramework:
./gradlew \
:jchucomponents-foundation:assembleJchuComponentsCoreReleaseXCFramework
xcodebuild \
-scheme JchuComponents-Package \
-destination 'generic/platform=iOS Simulator' \
buildFor contributions:
scripts/sync_strings.py synchronizes Android
strings.xml resources into an Apple Localizable.xcstrings catalog. It adds
new keys and locales, updates translations, converts placeholders such as
%1$s to %1$@ and ignores non-translatable strings.
Register it once as a Gradle task in the Android application module:
tasks.register<Exec>("syncIosStrings") {
group = "localization"
description = "Syncs Android strings into the iOS String Catalog"
commandLine(
"python3",
rootProject.file("scripts/sync_strings.py"),
"--xcstrings",
rootProject.file(
"iosApp/iosApp/resources/strings/Localizable.xcstrings"
),
"--android-res",
project.file("src/main/res"),
"--delete-missing",
)
}From then on, synchronization only requires running the Gradle task:
./gradlew :androidApp:syncIosStringsIt can also be saved as an Android Studio Gradle Run Configuration:
Sync localizables
:androidApp:syncIosStrings
This exposes synchronization as a one-click action from the IDE. Enable Store as project file if the run configuration should be shared with the rest of the team.
Remove --delete-missing when Apple-only keys must be preserved. Add
"--dry-run" to commandLine when the task should only preview changes.
Tagged 3.* versions trigger the release workflow. It validates the Gradle
version, API compatibility, tests, Android publications and the Swift package;
then it publishes the signed Android and KMP artifacts, including Navigation 3,
Play Store updates, and the BOM to Maven Central and prepares the dedicated
SwiftPM repository.
JitPack builds the same Git tag on demand when a consumer requests that version;
it does not need a separate manual upload.
Before tagging, update jchucomponents in gradle/libs.versions.toml and make
sure CI passes on v3:
git tag -a 3.0.0-alpha11 -m "3.0.0-alpha11"
git push origin v3
git push origin 3.0.0-alpha11SwiftPM publication additionally requires the repository variable
SPM_REPOSITORY_ENABLED=true and a SPM_REPOSITORY_TOKEN secret with write
access to Jeluchu/jchucomponents-spm.
JchuComponents is available under the Apache License 2.0.
JchuComponents is a collection of Kotlin Multiplatform utilities, Android Jetpack Compose components and native SwiftUI components for building Android and Apple applications with less repeated infrastructure.
JchuComponents 3 is under active development on the
v3branch. Pre-release versions follow3.0.0-alphaNN,3.0.0-betaNNand3.0.0-rcNNbefore the stable3.0.0release.
Apple targets are ARM64 only: iosArm64, iosSimulatorArm64 and
macosArm64. Intel x64 targets are not supported.
| Module | Android | iOS | macOS | Purpose |
|---|---|---|---|---|
jchucomponents-foundation |
✅ | ✅ | ✅ | Portable state, dates, text and utilities |
jchucomponents-network |
✅ | ✅ | ✅ | Ktor client configuration, resources and flow helpers |
jchucomponents-prefs |
✅ | ✅ | ✅ | Multiplatform DataStore preferences |
jchucomponents-pay |
✅ | ✅ | ✅ | Shared payment models and Android RevenueCat integration |
jchucomponents-pay-kmp |
✅ | ✅ | — | Shared RevenueCat purchases for Android and iOS (iOS ARM targets) |
jchucomponents-qr |
✅ | ✅ | ✅ | QR encoding and decoding utilities |
jchucomponents-room |
✅ | ✅ | — | Room 3 runtime configuration and SQL migrations |
jchucomponents-supabase |
✅ | ✅ | ✅ | Supabase Auth, database, realtime, Edge Functions and Koin setup |
jchucomponents-core |
✅ | ✅ | ✅ | Android architecture and lifecycle utilities with portable core APIs |
jchucomponents-ktx |
✅ | ✅ | ✅ | Android APIs and portable Kotlin extension compatibility helpers |
jchucomponents-ui-kmp |
✅ | ✅ | — | Portable Compose components without Android resource dependencies (iOS ARM targets) |
jchucomponents-ui |
✅ | — | — | Android adapter for resource- and platform-dependent components; re-exports jchucomponents-ui-kmp
|
jchucomponents-pdf |
✅ | — | — | Remote PDF reader with generated Navigation 3 routes |
jchucomponents-pdf-kmp |
✅ | ✅ | — | Compose Multiplatform PDF reader with Android PdfRenderer and iOS PDFKit (iOS ARM targets) |
jchucomponents-navigation3 |
✅ | ✅ | — | Compose Multiplatform Navigation 3 annotations, helpers and KSP processor (iOS ARM targets) |
jchucomponents-navigation3-di |
✅ | ✅ | — | Dependency injection helpers for generated Navigation 3 entries on JVM and iOS ARM targets |
jchucomponents-app-updates |
✅ | — | — | Google Play update StateFlow and lifecycle controller; apps own the UI |
jchucomponents-bom |
✅ | — | — | Aligned Android module versions |
The Swift package complements the shared Kotlin code with
JchuComponentsCore, JchuComponentsExtensions, JchuComponentsSwiftUI,
JchuComponentsNavigation and the optional native JchuComponentsPay product.
JchuComponentsSwiftUI also includes JchuPdfReaderView for remote PDFs on iOS.
The examples below use 3.0.0-alpha25. Replace it with the
latest available release.
KMP artifacts are published to Maven Central. Add only the modules needed by your shared source set:
kotlin {
sourceSets {
commonMain.dependencies {
implementation(
"io.github.jeluchu:jchucomponents-foundation:3.0.0-alpha25"
)
implementation(
"io.github.jeluchu:jchucomponents-network:3.0.0-alpha25"
)
implementation(
"io.github.jeluchu:jchucomponents-ui-kmp:3.0.0-alpha25"
)
}
}
}The available Maven Central artifacts are:
io.github.jeluchu:jchucomponents-foundation
io.github.jeluchu:jchucomponents-core
io.github.jeluchu:jchucomponents-ktx
io.github.jeluchu:jchucomponents-network
io.github.jeluchu:jchucomponents-ui-kmp
io.github.jeluchu:jchucomponents-pdf-kmp
io.github.jeluchu:jchucomponents-pay
io.github.jeluchu:jchucomponents-pay-kmp
io.github.jeluchu:jchucomponents-prefs
io.github.jeluchu:jchucomponents-qr
io.github.jeluchu:jchucomponents-room
io.github.jeluchu:jchucomponents-supabase
io.github.jeluchu:jchucomponents-navigation3
io.github.jeluchu:jchucomponents-navigation3-di
io.github.jeluchu:jchucomponents-bom
io.github.jeluchu:jchucomponents-app-updates
jchucomponents-ktx keeps its Android extensions in androidMain; its shared
API provides the existing boolean, list, and serialization package names plus
a portable Job helper, backed by jchucomponents-foundation where an
equivalent already exists. Dispatcher-based coroutine helpers remain in
androidMain.
jchucomponents-pay keeps its existing Android RevenueCat API and shared
payment models. For purchases from common Kotlin on Android and iOS, use
jchucomponents-pay-kmp; it wraps RevenueCat's official Kotlin Multiplatform
SDK and publishes Android, iosArm64, and iosSimulatorArm64 variants. Its
RevenueCat SDK dependency is limited to Android and iOS, so this artifact does
not currently target macOS.
Most listed artifacts contain Android, iOS ARM64 and macOS ARM64 variants.
The exceptions are jchucomponents-room, jchucomponents-navigation3,
jchucomponents-navigation3-di,
jchucomponents-pdf-kmp, jchucomponents-pay-kmp, and
jchucomponents-ui-kmp, which currently contain
Android, iosArm64, and iosSimulatorArm64; and
jchucomponents-app-updates, which is Android-only and intended for apps
distributed through Google Play. Consumers provide their own update interface.
JitPack's Linux builds do not produce the Apple KLIB variants, so KMP
applications targeting iOS or macOS should use Maven Central.
jchucomponents-foundation also provides JchuLocaleProvider for reading the
system language from shared code. getPlatformLanguageCode() maps Spanish in
the United States to es-US and returns the base language code for other
regional locales; getLanguageTag() exposes the full BCP 47 tag where the
platform provides one.
jchucomponents-ui-kmp contains the UI components that compile for Android,
iosArm64, and iosSimulatorArm64. The Android jchucomponents-ui artifact
keeps the existing Android API and adds the platform-bound parts that use R, Context, View,
Bitmap, Android resource APIs or Android Coil requests. KMP applications
should depend on jchucomponents-ui-kmp directly; Android applications can
continue using jchucomponents-ui as before.
Add JitPack to settings.gradle.kts:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven("https://jitpack.io")
}
}Use the BOM to keep Android modules on the same version:
dependencies {
implementation(
platform("io.github.jeluchu:jchucomponents-bom:3.0.0-alpha25")
)
implementation("com.github.jeluchu.jchucomponents:jchucomponents-ui")
implementation("io.github.jeluchu:jchucomponents-ktx")
}Alternatively, specify each JitPack artifact version directly:
dependencies {
implementation(
"com.github.jeluchu.jchucomponents:jchucomponents-ui:3.0.0-alpha25"
)
implementation(
"com.github.jeluchu.jchucomponents:jchucomponents-pdf:3.0.0-alpha25"
)
}The jchucomponents-pdf artifact provides an Android Compose screen for remote
PDF URLs and is available through JitPack starting with 3.0.0-alpha23.
The library generates its typed PdfReaderRoutes.Reader(url) route and
pdfReaderEntries(backStack) provider, so apps register the provider in their
Navigation 3 entry provider and navigate to the generated route. The artifact is
Android-only and is distributed through JitPack.
For Compose Multiplatform apps, use the Maven Central artifact
io.github.jeluchu:jchucomponents-pdf-kmp. It provides the same reader route on
Android, iosArm64, and iosSimulatorArm64, using PdfRenderer/FileProvider on
Android and PDFKit/native sharing on iOS. Add it to commonMain and register
pdfReaderKmpEntries(backStack); then navigate with
PdfReaderKmpRoutes.Reader(url = remotePdfUrl). The Android-only artifact
remains available unchanged.
For a shared RevenueCat integration on Android and iOS, add the KMP artifact
to commonMain:
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.jeluchu:jchucomponents-pay-kmp:3.0.0-alpha25")
}
}
}Initialize it once with the API key for the current store, then load the active offering and make purchases from shared Kotlin:
import com.jeluchu.pay.revenuecat.kmp.Payment
import com.jeluchu.pay.revenuecat.kmp.initPayment
initPayment(
apiKey = revenueCatApiKey,
isDebug = isDebug,
subscriptionName = "premium"
)
val billingInfo = Payment.loadBillingInfo()
val purchaseResult = Payment.purchaseResult(Payment.ProductsType.MONTHLY)The artifact uses RevenueCat's Kotlin Multiplatform SDK 3.6.0 and currently
targets Android, iosArm64, and iosSimulatorArm64. macOS is not included
because the upstream KMP SDK does not currently publish a macOS target. See
the module README for callbacks, restore,
subscription status, and plan switching.
Add the following package URL in Xcode:
https://github.com/Jeluchu/jchucomponents-spm
Select the JchuComponents product and import the modules needed by the
application:
import JchuComponentsCore
import JchuComponentsExtensions
import JchuComponentsSwiftUIFor the native RevenueCat integration, also select and import the optional product:
import JchuComponentsPayThe Swift package currently requires iOS 26.0 or newer. Releases live in the
dedicated jchucomponents-spm repository so consumers do not clone the full
Kotlin and Android monorepo.
Create a platform-specific Ktor engine from shared Kotlin:
val client = createHttpClient(
HttpClientConfiguration(
baseUrl = "https://example.com/api/",
enableLogging = isDebug,
defaultHeaders = mapOf("X-Client" to "my-app"),
)
)createHttpClient uses Ktor's Android engine on Android and Darwin engine on
iOS and macOS. HttpClientConfiguration controls JSON, caching, timeouts,
response validation, logging and sensitive headers.
The v3 network result models and flow helpers belong to
jchucomponents-network:
import com.jeluchu.jchucomponents.network.extensions.flow.flowCollector
import com.jeluchu.jchucomponents.network.extensions.flow.flowResourceCollector
import com.jeluchu.jchucomponents.network.extensions.handleFailure
import com.jeluchu.jchucomponents.network.models.Failure
import com.jeluchu.jchucomponents.network.models.ResourceTheir previous jchucomponents-core packages were removed during the v3 alpha
cycle.
Apply the shared runtime defaults to a Room 3 builder while retaining access to all application-specific configuration:
val database = Room.databaseBuilder<AppDatabase>(context, "app.db")
.configureJchuRoom {
addMigrations(
sqlMigration(
startVersion = 1,
endVersion = 2,
"ALTER TABLE task ADD COLUMN archived INTEGER NOT NULL DEFAULT 0"
)
)
}
.build()The consuming module must apply KSP and add the
androidx.room3:room3-compiler dependency itself. The runtime module does not
enable destructive migrations or choose a database path.
jchucomponents-navigation3 provides Navigation 3 annotations and back-stack
helpers in commonMain. Its KSP processor can generate typed route keys and
entry providers from shared @Screen composables for Android and iOS ARM
targets (iosArm64 and iosSimulatorArm64). Configure KSP with
kspCommonMainMetadata in the module containing those screens. The host still
supplies its Navigation 3 UI dependency and root NavDisplay.
The optional jchucomponents-navigation3-di artifact provides Koin
inject()/injectByParams() helpers on Android, JVM, iosArm64 and
iosSimulatorArm64. It uses Koin's multiplatform Compose ViewModel APIs and
shares the runtime module's target restrictions: Navigation 3 1.2.0 has no
iosX64 or macOS variants.
The runtime module is independent of jchucomponents-ui. The processor groups
screens by graph, uses each composable name as its default route, and wires
typed arguments, back callbacks, cross-graph navigation and app-owned custom
actions. See the module README for KMP
and Android setup, and the Navigation code generation example in the
Android catalog.
The Swift package also provides JchuComponentsNavigation, a SwiftUI @Screen
macro and graph plugin. Annotated screens generate a typed route enum per graph,
including associated arguments and a switch to each destination view. For KMP
ViewModels, @ScreenKoinViewModel can infer the factory from the property type;
for example, UserViewModel uses KoinWrapper.userForScreen():
@Screen(graph: "Wallet")
struct WalletScreen: View {
@ScreenKoinViewModel
@StateViewModel private var viewModel: UserViewModel
var body: some View { WalletContent(viewModel: viewModel) }
}Factories follow <lowerCamel ViewModel name without ViewModel>ForScreen; the
macro forwards @RouteArgument values by name. Use arguments: [] when the
ViewModel needs no route values, or pass factory: explicitly for a custom
factory name. The iOS catalog registers generated routes on its root
NavigationStack.
Register the shared Supabase client from the same Koin graph used by the app repositories:
val supabaseModule = jchuSupabaseModule(
JchuSupabaseConfig(
url = BuildKonfig.SUPABASE_URL,
publishableKey = BuildKonfig.SUPABASE_PUBLISHABLE_KEY,
auth = JchuSupabaseAuthConfig(
flowType = JchuSupabaseAuthFlowType.PKCE,
alwaysAutoRefresh = true,
autoLoadFromStorage = true,
autoSaveToStorage = true,
defaultRedirectUrl = "com.example.app://auth-callback",
scheme = "com.example.app",
host = "auth-callback"
)
)
)Repositories can depend on JchuSupabaseDatabase, JchuSupabaseAuth,
JchuSupabaseFunctions or JchuSupabaseRealtime. Database and Function
operations return Flow<Resource<Failure, T>>; Auth returns a typed
JchuSupabaseAuthFailure so login screens can distinguish invalid credentials,
unconfirmed email, weak password, rate limiting, session expiry and network
failures.
Registration reports whether Supabase created a session or is waiting for the email confirmation link:
auth.signUpWithEmail(
email = email,
password = password,
redirectUrl = "com.example.app://auth-callback",
metadata = buildJsonObject {
put("display_name", displayName)
put("requested_account_type", "shop")
}
).collect { resource ->
if (resource is Resource.Success) {
val result = resource.data ?: return@collect
if (result.requiresEmailConfirmation) {
showCheckYourEmail()
} else {
openAuthenticatedArea()
}
}
}Forward the Android or Apple deep-link URL to
auth.handleAuthCallback(url). Observe auth.sessionState during app startup;
it differentiates initialization, restored/authenticated sessions, sign-out and
refresh failures. Password recovery, confirmation resend, password update,
manual refresh, local session clearing and local/global sign-out are also
available on JchuSupabaseAuth.
The default Supabase session and PKCE caches are used unless custom
SessionManager and CodeVerifierCache implementations are supplied. Inject
platform-secure implementations when the application requires explicit token
encryption or retention policies. Never use user_metadata as an authorization
source: roles, shop ownership and administrator approval must be enforced by
database data, RLS and server-side functions.
PostgREST RPCs are available through database.rpc(...). Edge Functions accept
idempotency and correlation identifiers without allowing callers to override
the Supabase authentication headers:
functions.invoke<AwardPointsRequest, AwardPointsResponse>(
name = "award-points",
body = request,
options = JchuSupabaseFunctionOptions(
idempotencyKey = request.operationId,
correlationId = request.correlationId
)
)Basic table access remains available for ordinary RLS-protected data:
@Serializable
data class Profile(
val id: String,
val name: String
)
object ProfileTable : JchuSupabaseTable<Profile> {
override val name: String = "profiles"
}
class ProfileRepository(
database: JchuSupabaseDatabase
) {
private val profiles = database.table(ProfileTable)
fun profile(id: String): Flow<Resource<Failure, Profile>> =
profiles.findById(id)
fun updateProfile(profile: Profile): Flow<Resource<Failure, Profile>> =
profiles.updateById(
id = profile.id,
value = profile
)
}The first Supabase module intentionally excludes Storage. It focuses on Auth, PostgREST database access, Realtime wiring, Edge Functions, Koin and resource flow helpers for Android, iOS and macOS KMP apps.
Experimental APIs should be explicitly marked with @RequiresOptIn. Once an
API becomes stable, incompatible changes require a deprecation cycle whenever
practical.
The repository includes an Android catalog in app and an iOS catalog in
samples/iosApp.
Run the repository-wide Android and Kotlin checks:
./gradlew ciCheckThis validates binary APIs, tests, debug and release assemblies, and all local Maven publications. See the consumer guide for focused setup and examples for every module.
Inspect Kotlin formatting without modifying source files:
./gradlew ktlintCheckHTML and plain-text reports are generated under each module's
build/reports/ktlint directory. The check is currently advisory and does not
fail CI. Apply safe automatic fixes to one module at a time:
./gradlew :jchucomponents-foundation:ktlintFormatReview the resulting diff before continuing with another module because some rules, such as file naming, require a manual decision.
Build the Android catalog:
./gradlew :app:assembleDebugVerify the artifacts as an external KMP consumer after publishing them locally:
./gradlew \
:jchucomponents-foundation:publishToMavenLocal \
:jchucomponents-network:publishToMavenLocal \
:jchucomponents-pay:publishToMavenLocal \
:jchucomponents-prefs:publishToMavenLocal \
:jchucomponents-qr:publishToMavenLocal \
:jchucomponents-room:publishToMavenLocal \
:jchucomponents-supabase:publishToMavenLocal
./gradlew \
-p smoke-tests/kmp-consumer \
compileAndroidMain \
compileKotlinIosSimulatorArm64 \
compileKotlinMacosArm64The smoke project reserves io.github.jeluchu for Maven Local, ensuring the
check cannot silently consume an already published release from Maven Central.
Build the Swift package after assembling the XCFramework:
./gradlew \
:jchucomponents-foundation:assembleJchuComponentsCoreReleaseXCFramework
xcodebuild \
-scheme JchuComponents-Package \
-destination 'generic/platform=iOS Simulator' \
buildFor contributions:
scripts/sync_strings.py synchronizes Android
strings.xml resources into an Apple Localizable.xcstrings catalog. It adds
new keys and locales, updates translations, converts placeholders such as
%1$s to %1$@ and ignores non-translatable strings.
Register it once as a Gradle task in the Android application module:
tasks.register<Exec>("syncIosStrings") {
group = "localization"
description = "Syncs Android strings into the iOS String Catalog"
commandLine(
"python3",
rootProject.file("scripts/sync_strings.py"),
"--xcstrings",
rootProject.file(
"iosApp/iosApp/resources/strings/Localizable.xcstrings"
),
"--android-res",
project.file("src/main/res"),
"--delete-missing",
)
}From then on, synchronization only requires running the Gradle task:
./gradlew :androidApp:syncIosStringsIt can also be saved as an Android Studio Gradle Run Configuration:
Sync localizables
:androidApp:syncIosStrings
This exposes synchronization as a one-click action from the IDE. Enable Store as project file if the run configuration should be shared with the rest of the team.
Remove --delete-missing when Apple-only keys must be preserved. Add
"--dry-run" to commandLine when the task should only preview changes.
Tagged 3.* versions trigger the release workflow. It validates the Gradle
version, API compatibility, tests, Android publications and the Swift package;
then it publishes the signed Android and KMP artifacts, including Navigation 3,
Play Store updates, and the BOM to Maven Central and prepares the dedicated
SwiftPM repository.
JitPack builds the same Git tag on demand when a consumer requests that version;
it does not need a separate manual upload.
Before tagging, update jchucomponents in gradle/libs.versions.toml and make
sure CI passes on v3:
git tag -a 3.0.0-alpha11 -m "3.0.0-alpha11"
git push origin v3
git push origin 3.0.0-alpha11SwiftPM publication additionally requires the repository variable
SPM_REPOSITORY_ENABLED=true and a SPM_REPOSITORY_TOKEN secret with write
access to Jeluchu/jchucomponents-spm.
JchuComponents is available under the Apache License 2.0.