-
Notifications
You must be signed in to change notification settings - Fork 2
remote config README
Server-driven UI system for Kotlin Multiplatform — powered by Supabase.
Standalone module — no dependency on other kmp-toolkit modules. Per-project Supabase model: bring your own
supabaseUrl+anonKey; noproductTypefilter required.
Remotely push UI overlays to your users without an app update. Control dialogs, banners, full-screen overlays, and bottom sheets from a Supabase dashboard — with frequency capping, scheduling, platform targeting, and action routing.
┌─ DIALOG ──────────────┐ ┌─ FULLSCREEN ──────────┐
│ │ │ [X] │
│ 🚀 Update Ready! │ │ 🎉 Big News! │
│ New version 3.0 │ │ We've completely │
│ [Update Now] [Later] │ │ [Get Started] │
└───────────────────────┘ └───────────────────────┘
┌─ BANNER ──────────────┐ ┌─ BOTTOM_SHEET ────────┐
│ 🔔 New update! [→] │ │ 📣 Announcement │
└───────────────────────┘ │ [Action] [Dismiss] │
└───────────────────────┘
| Type | Use Case |
|---|---|
dialog |
Announcements, confirmations, promotional offers |
fullscreen |
Onboarding, major feature reveals |
banner |
Inline alerts, non-blocking nudges |
bottom_sheet |
Soft prompts, feature discovery |
-
Frequency control:
max_impressions+cooldown_hoursper device -
Scheduling:
start_at+end_atfor time-boxed campaigns -
Platform targeting:
platform = "android" | "ios" | "all" -
Version targeting:
min_app_version+max_app_version -
Action routing: extensible
ActionTypevalue class (built-in URL/DEEPLINK/STORE/DISMISS/PREMIUM, plus consumer-defined types) -
Dynamic UI:
content_jsonfor server-rendered composables — 11 node types (Column, Row, Box, Card, Text, Button, Image, Spacer, Divider, Badge, Icon). See SETUP.md#content_json - RLS-secured: device impressions tracked server-side
artifact: io.github.mobilebytelabs:kmptoolkit-remote-config:4.0.0 # next release — see CHANGELOG
package: com.mobilebytelabs.remoteconfig
supabase_table: product_remote_config # per-project, NO product_type column
install: Module.remoteConfig { … } # Koin DSL extension
ui_composable: RemoteConfigHost(onAction)
depends_on: — # standalone, no kmp-toolkit cross-module dependencies// 1. Install inside any existing Koin module
import com.mobilebytelabs.remoteconfig.remoteConfig
val networkModule = module {
remoteConfig {
supabaseUrl = "https://YOUR_PROJECT.supabase.co"
supabaseKey = "YOUR_ANON_KEY"
// Optional — app-specific action handlers (custom action_type strings):
action(ActionType.PREMIUM) { _, _ -> navigator.navigateTo("paywall") }
action("open_downloads") { v, _ -> navigator.navigateTo("downloads/${v.orEmpty()}") }
}
// … your other bindings …
}
// 2. Add RemoteConfigHost to root composable.
// Pass onAction for explicit per-screen control, or omit to route through
// handlers registered above via the action(...) DSL.
@Composable
fun App() {
Box(modifier = Modifier.fillMaxSize()) {
MainNavHost()
RemoteConfigHost(
onAction = { actionType, actionValue ->
when (actionType) {
ActionType.URL -> openUrl(actionValue)
ActionType.DEEPLINK -> navController.navigate(actionValue ?: return@RemoteConfigHost)
ActionType.STORE -> openStore()
ActionType.PREMIUM -> navController.navigate("paywall")
else -> {}
}
},
)
}
}ActionType is an open value-class — extend with your own typed constants:
object RemoteActions {
val OPEN_DOWNLOADS = ActionType("open_downloads")
val CLEAR_CACHE = ActionType("clear_cache")
val OPEN_PAYWALL = ActionType("open_paywall")
}
remoteConfig {
supabaseUrl = "…"; supabaseKey = "…"
action(RemoteActions.OPEN_DOWNLOADS) { v, _ -> navigator.navigateTo("downloads/${v.orEmpty()}") }
action(RemoteActions.CLEAR_CACHE) { _, _ -> cacheManager.clearAll() }
}ActionType("open_downloads") == RemoteActions.OPEN_DOWNLOADS — value class equality is
structural, so types round-trip exactly through Supabase JSON.
Tables: product_remote_config + device_impressions
RPCs: get_device_impressions, record_config_impression, dismiss_config
See SETUP.md for full SQL.
| 3.x | 4.0.0 |
|---|---|
ProductTicketsConfig.init(...) prerequisite |
None — cmp-remote-config is standalone |
RemoteConfigConfig.supabaseUrl = ... (mutable singleton) |
DSL: remoteConfig { supabaseUrl = ... }
|
modules(remoteConfigModule) in startKoin { }
|
DSL block inside any existing module — no separate registration |
boardType / productType parameter |
Removed — per-project Supabase model |
ActionType enum + ActionType.from(value)
|
@JvmInline value class ActionType(val value) — construct directly, extend with object MyActions { val FOO = ActionType("foo") }
|
ActionType.NONE / URL / DEEPLINK / STORE / DISMISS |
All preserved + new ActionType.PREMIUM
|
- SETUP.md — Integration steps + full SQL
-
CLAUDE_AI_SETUP.md — AI-assisted setup with
/sync-remote-config - ../REMOTE_CONFIG_SAMPLES.md — Sample config rows
** Partials**
App Intents
Bubble
Clipboard
Cookbook
- Clipboard Copy Text
- Clipboard Read Text
- Consumer Anon Key Setup
- Crashlytics Attribution Per Library
- Ifonline Block
- Index
- Index
- Index
- Index
- Open Url Compose
- Pick And Share Image
- React To Offline
- Register Firebase Hooks
- Share Pdf Android
- Share Text
- Wifi Vs Cellular
Firebase Analytics
In App Update
Intent Launcher
Inter App Comms
Modules
- Cmp App Intents
- Cmp App Intents Compose
- Cmp Bubble
- Cmp Clipboard
- Cmp Deep Link
- Cmp Firebase Analytics
- Cmp In App Update
- Cmp Intent Launcher
- Cmp Intent Launcher Compose
- Cmp Library
- Cmp Network Monitor
- Cmp Network Monitor Compose
- Cmp Observe
- Cmp Observe Koin
- Cmp Open Url
- Cmp Pdf Generator
- Cmp Product Tickets
- Cmp Remote Config
- Cmp Share
- Cmp Share Compose
- Cmp Toast
Network Monitor
Open Url
Pdf Generator
Remote Config
Share
Toast
User Tickets
General