📖 PrzeglądOverview

Moduł Features umożliwia zarządzanie feature flags z obsługą Firebase Remote Config, percentage rollout i lokalnych overrides do testów.The Features module enables feature flag management with Firebase Remote Config support, percentage rollout, and local overrides for testing.

Szybki przykładQuick Example
// Definiowanie flag
ADict.Features.define("new_ui", default = false, rolloutPercentage = 50)
ADict.Features.define("max_items", default = 10)

// Sprawdzanie
if (ADict.Features.isEnabled("new_ui")) {
    showNewUI()
}

val maxItems = ADict.Features.getInt("max_items")

📝 Definiowanie flag

define(key, default: Boolean, description, rolloutPercentage)

Definiuj flagę boolean z opcjonalnym percentage rollout.Define a boolean flag with optional percentage rollout.

define(key, default: String, description, variants)

Definiuj flagę string z opcjonalnymi wariantami.Define a string flag with optional variants.

define(key, default: Int, description, range)

Definiuj flagę int z opcjonalnym zakresem wartości.Define an int flag with optional value range.

defineLong(key, default: Long, description)

Definiuj flagę long.Define a long flag.

defineDouble(key, default: Double, description)

Definiuj flagę double.Define a double flag.

Definiowanie różnych typów flagDefining Different Flag Types
// Boolean z 50% rollout
ADict.Features.define(
    key = "new_checkout_flow",
    default = false,
    description = "Nowy proces checkout",
    rolloutPercentage = 50  // tylko 50% użytkowników
)

// String z wariantami
ADict.Features.define(
    key = "button_color",
    default = "blue",
    variants = listOf("blue", "green", "red")
)

// Int z zakresem
ADict.Features.define(
    key = "max_retries",
    default = 3,
    range = 1..10
)

// Long
ADict.Features.defineLong(
    key = "cache_duration_ms",
    default = 3600000L
)

// Double
ADict.Features.defineDouble(
    key = "discount_rate",
    default = 0.1
)

📖 Pobieranie wartościGetting Values

isEnabled(key: String): Boolean

Sprawdź czy flaga boolean jest włączona (uwzględnia rollout).Check if boolean flag is enabled (respects rollout).

getString(key: String): String

Pobierz wartość string.Get string value.

getInt(key: String): Int

Pobierz wartość int (z walidacją range).Get int value (with range validation).

getLong(key: String): Long

Pobierz wartość long.Get long value.

getDouble(key: String): Double

Pobierz wartość double.Get double value.

🔧 Overrides (testowanie)

override(key: String, value: Any)

Nadpisz wartość lokalnie (do testów).Override value locally (for testing).

clearOverride(key: String)

Usuń nadpisanie.Remove override.

clearAllOverrides()

Usuń wszystkie nadpisania.Remove all overrides.

TestowanieTesting z overrides
// W debug buildzie - wymuś włączenie feature
if (BuildConfig.DEBUG) {
    ADict.Features.override("new_checkout_flow", true)
}

// Reset po testach
ADict.Features.clearAllOverrides()

📡 Obserwacja zmian

observe<T>(key: String, listener: (T) -> Unit)

Obserwuj zmiany wartości flagi.Observe flag value changes.

changes: StateFlow<Pair<String, Any?>>

Flow zmian (key to value).

Obserwacja zmian
// Callback
ADict.Features.observe<Boolean>("dark_mode") { enabled ->
    applyTheme(if (enabled) Theme.DARK else Theme.LIGHT)
}

// Flow
lifecycleScope.launch {
    ADict.Features.changes.collect { (key, value) ->
        Log.d("Features", "Flag changed: $key = $value")
    }
}

📊 Percentage Rollout

Rollout pozwala włączyć funkcję tylko dla części użytkowników.Rollout allows enabling a feature for only a subset of users. Jest deterministyczny - ten sam użytkownik zawsze dostanie tę samą wartość.It is deterministic - the same user always gets the same value.

setUserId(id: String)

Ustaw ID użytkownika dla spójnego rollout.Set user ID for consistent rollout.

A/B Testing z rollout
// Ustaw user ID (ważne dla spójności!)
ADict.Features.setUserId(currentUser.id)

// Definiuj eksperyment - 20% użytkowników
ADict.Features.define(
    key = "experiment_new_onboarding",
    default = false,
    rolloutPercentage = 20
)

// Użycie
if (ADict.Features.isEnabled("experiment_new_onboarding")) {
    showNewOnboarding()
    ADict.Analytics.log("experiment_variant", "new_onboarding" to "enabled")
} else {
    showOldOnboarding()
    ADict.Analytics.log("experiment_variant", "new_onboarding" to "control")
}

💡 PrzykładyExamples praktyczne

Pełna konfiguracjaFull Configuration
class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()

        ADict.init(this, BuildConfig.DEBUG)
        setupFeatureFlags()
    }

    private fun setupFeatureFlags() {
        // UI Features
        ADict.Features.define("dark_mode_enabled", default = true)
        ADict.Features.define("new_navigation", default = false, rolloutPercentage = 30)

        // Limits
        ADict.Features.define("max_downloads", default = 5, range = 1..20)
        ADict.Features.define("cache_size_mb", default = 100)

        // A/B Tests
        ADict.Features.define("checkout_variant", default = "classic",
            variants = listOf("classic", "one_page", "express"))

        // Kill switches
        ADict.Features.define("enable_chat", default = true)
        ADict.Features.define("enable_push", default = true)
    }
}

// Użycie w kodzie
fun loadData() {
    val maxItems = ADict.Features.getInt("max_downloads")
    // ...
}

fun showCheckout() {
    when (ADict.Features.getString("checkout_variant")) {
        "one_page" -> showOnePageCheckout()
        "express" -> showExpressCheckout()
        else -> showClassicCheckout()
    }
}