Remote Config¶
The Starter Template includes a type-safe Remote Config system built with Clean Architecture.
It allows you to:
- Define strongly typed keys via
RemoteConfigKey - Provide safe defaults
- Deserialize custom JSON objects
- Use values in ViewModels or Compose
- Replace Firebase with your own implementation
Architecture¶
| Piece | Role |
|---|---|
RemoteConfigKey<T> |
Base contract for a single remote config entry (key + default + optional serializer) |
| Your sealed keys | App-owned hierarchy of all config keys (e.g. ConfigKeys) |
GetConfigLogic |
Reads a typed value for a RemoteConfigKey |
rememberRemoteConfig |
Compose helper that observes a key |
Recommended approach: keep one sealed key hierarchy in your app/core module so every screen reads config through the same typed models.
1. Define Keys¶
Extend RemoteConfigKey. Example:
@Serializable
data class PromoConfig(
val isEnabled: Boolean = false,
val discountPercentage: Int = 0,
)
sealed class ConfigKeys<T>(
key: String,
defaultValue: T,
serializer: KSerializer<T>? = null,
) : RemoteConfigKey<T>(
key = key,
defaultValue = defaultValue,
serializer = serializer,
) {
data class ShowAds(
override val defaultValue: Boolean = false,
) : RemoteConfigKey<Boolean>(
key = "show_ads",
defaultValue = defaultValue,
)
data class WelcomeText(
override val defaultValue: String = "Welcome to KMP Starter",
) : RemoteConfigKey<String>(
key = "welcome_text",
defaultValue = defaultValue,
)
data class Promo(
override val defaultValue: PromoConfig = PromoConfig(),
) : RemoteConfigKey<PromoConfig>(
key = "promo_config",
defaultValue = defaultValue,
serializer = PromoConfig.serializer(),
)
data class MinimumVersion(
override val defaultValue: Int = 36,
) : RemoteConfigKey<Int>(
key = "minimum_version",
defaultValue = defaultValue,
)
}
Note
- Override
defaultValueso defaults stay at the call site. - Keys must exactly match Firebase console keys.
- Always provide safe defaults.
- Pass
serializerfor@Serializablecustom objects.
Initialization
Remote Config must be initialized at app startup:
This is already called in the entry point by default.
Warning
Make sure Firebase is integrated correctly because the default implementation uses Firebase Remote Config internally.
2. Use in ViewModel¶
Inject GetConfigLogic and pass a key instance:
Enable/Disable Ads¶
UI can show/hide ads remotely without a new release.
Running a Promotion¶
Firebase JSON for promo_config:
class PromoViewModel(
private val getConfig: GetConfigLogic,
) : ViewModel() {
private val _promo = MutableStateFlow(PromoConfig())
val promo: StateFlow<PromoConfig> = _promo
init {
viewModelScope.launch {
_promo.value = getConfig(ConfigKeys.Promo())
}
}
}
Now you can:
- Enable/disable the campaign remotely
- Change discount percentage
- Avoid app updates for marketing changes
Compose Usage¶
- Starts with default value
- Updates after fetch
- Supports primitives & custom types
Custom Implementation¶
If you don’t want Firebase (or for testing), create your own repository in:
Interface¶
RemoteConfigValue is a typealias for String.
Local Implementation Example¶
Bind it in Koin:
| features/remote_config/data/.../di/Module.kt | |
|---|---|
Useful for:
- Unit testing
- Desktop builds
- CI pipelines
- Offline development
Summary
- Define keys as
RemoteConfigKeysubclasses (e.g. sealedConfigKeys) - Always provide default values
- Use
GetConfigLogicin ViewModels - Use
rememberRemoteConfigin Compose - Replace
RemoteConfigRepositoryif needed