Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@ public interface TypeConverter<T : Any> {
* converter.fromString("UNKNOWN") // throws IllegalArgumentException
* ```
*
* ## Compatible providers
*
* Pass the returned converter to `registerConverter` on any of these providers before the first
* read or write of an enum flag:
*
* - `DataStoreConfigValueProvider.registerConverter(enumConverter<T>())`
* - `JavaPreferencesConfigValueProvider.registerConverter(enumConverter<T>())`
* - `SharedPreferencesProviderConfig.registerConverter(enumConverter<T>())`
*
* `FirebaseConfigValueProvider` handles enums automatically via reflection — no registration
* is required.
*
* **iOS caveat:** `NSUserDefaultsConfigValueProvider` does not support enums at this time —
* it has no converter API. Use a `String` flag as a workaround on iOS and convert the raw
* value to your enum manually at the call site.
*
* @param T The enum class to convert.
* @return A [TypeConverter] that round-trips [T] by enum constant name.
*/
Expand Down
54 changes: 54 additions & 0 deletions featured-gradle-plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# featured-gradle-plugin

Gradle plugin for the [Featured](../README.md) configuration management library.

Apply it to a module and declare flags in the `featured { }` DSL block; the plugin generates
typed `ConfigParam` objects, `ConfigValues` extension functions, and R8 shrinker rules.

## Enum flags

Declare an enum-typed flag with the `enum(...)` DSL function:

```kotlin
// build.gradle.kts
featured {
localFlags {
enum(
key = "checkout_variant",
typeFqn = "com.example.CheckoutVariant",
default = "LEGACY",
)
}
}
```

### Runtime converter requirement (Android / JVM)

Storage-backed local providers serialize values as strings. Before the first read or write of
an enum flag you must register an `enumConverter` on the provider, otherwise the provider
throws `IllegalArgumentException` synchronously.

Affected providers and the required registration call:

| Provider | Registration |
|---|---|
| `DataStoreConfigValueProvider` | `provider.registerConverter(enumConverter<MyEnum>())` |
| `JavaPreferencesConfigValueProvider` | `provider.registerConverter(enumConverter<MyEnum>())` |
| `SharedPreferencesProviderConfig` | `provider.registerConverter(enumConverter<MyEnum>())` |

`FirebaseConfigValueProvider` handles enums automatically via reflection — no registration
is needed.

```kotlin
// Runtime wiring example (DataStore)
val provider = DataStoreConfigValueProvider(dataStore).apply {
registerConverter(enumConverter<CheckoutVariant>())
}
val configValues = ConfigValues(localProvider = provider)
```

### iOS caveat

`NSUserDefaultsConfigValueProvider` does not support enums at this time — it has no converter
API. Use a `String` flag as a workaround on iOS and convert the raw value to your enum manually
at the call site.
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,49 @@ public class FlagContainer {
/**
* Declares an enum-typed feature flag.
*
* Enum flags are intentionally excluded from R8 `-assumevalues` DCE rules — the value
* cannot be assumed at build time (it is resolved at runtime from providers).
* The plugin generates a typed `ConfigParam<E>` backed by this declaration. Enum flags are
* intentionally excluded from R8 `-assumevalues` DCE rules — the value cannot be assumed at
* build time (it is resolved at runtime from providers).
*
* ## Runtime converter requirement (Android / JVM)
*
* Storage-backed local providers serialize values as strings and require an explicit
* [enumConverter] registration before the first read or write of this flag. Without it the
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot May 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Unresolvable KDoc link: [enumConverter] is defined in the core module which is not a dependency of featured-gradle-plugin. This will produce a broken link or KDoc warning. Use a fully-qualified reference or backtick-quoted text instead.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At featured-gradle-plugin/src/main/kotlin/dev/androidbroadcast/featured/gradle/FlagContainer.kt, line 94:

<comment>Unresolvable KDoc link: `[enumConverter]` is defined in the `core` module which is not a dependency of `featured-gradle-plugin`. This will produce a broken link or KDoc warning. Use a fully-qualified reference or backtick-quoted text instead.</comment>

<file context>
@@ -84,8 +84,49 @@ public class FlagContainer {
+     * ## Runtime converter requirement (Android / JVM)
+     *
+     * Storage-backed local providers serialize values as strings and require an explicit
+     * [enumConverter] registration before the first read or write of this flag. Without it the
+     * provider throws [IllegalArgumentException] synchronously. Affected providers:
+     *
</file context>
Fix with Cubic

* provider throws [IllegalArgumentException] synchronously. Affected providers:
*
* - `DataStoreConfigValueProvider`
* - `JavaPreferencesConfigValueProvider`
* - `SharedPreferencesProviderConfig`
*
* Firebase Remote Config (`FirebaseConfigValueProvider`) handles enums automatically via
* reflection — no `registerConverter` call is needed there.
*
* **iOS caveat:** `NSUserDefaultsConfigValueProvider` does not support enums at this time —
* it has no converter API. Use a `String` flag as a workaround on iOS and convert the raw
* value to your enum manually at the call site.
*
* ## Example
*
* ```kotlin
* // Gradle DSL — declaration
* featured {
* localFlags {
* enum(
* key = "checkout_variant",
* typeFqn = "com.example.CheckoutVariant",
* default = "LEGACY",
* )
* }
* }
* ```
*
* ```kotlin
* // Runtime — required wiring for non-Firebase local providers
* val provider = DataStoreConfigValueProvider(dataStore).apply {
* registerConverter(enumConverter<CheckoutVariant>())
* }
* val configValues = ConfigValues(localProvider = provider)
* ```
*
* @param key The configuration key string (e.g. `"checkout_variant"`).
* @param typeFqn The fully-qualified Kotlin class name of the enum (e.g. `"com.example.CheckoutVariant"`).
Expand Down
Loading