SDK Reference

Kotlin / Android

Kotlin-first SDK with coroutine support, thread-safe evaluation, and seamless Android integration. Works on Kotlin/JVM and Android.

Installation

build.gradle.kts
dependencies {
    implementation("com.toggleai:sdk-kotlin:1.0.0")
}

// For Android projects, also add:
// implementation("com.toggleai:sdk-kotlin-android:1.0.0")

Basic Usage

The Kotlin / Android SDK operates in close harmony with ToggleAI backend services to deliver first-class mobile performance, zero-overhead evaluations, and asynchronous telemetry reporting:

⚑ Local In-Memory Evaluation

When client.init() is called, the SDK makes a single secure HTTP request to GET /sdk/config to retrieve the configuration rules. Subsequent checks via client.getFlag() or client.getFlagValue() execute instantly in-memory (sub-millisecond latency)β€”completely eliminating network delays from critical user interactions or Jetpack Compose rendering paths.

πŸ›°οΈ Edge Remote Evaluation

In security-sensitive contexts demanding immediate validation bypassing local caches, client.evaluateFlagRemote() performs a direct edge network query to POST /sdk/evaluate, ensuring the latest server-side flag determination in real-time.

Application.kt
import com.toggleai.sdk.ToggleAIClient
import com.toggleai.sdk.ToggleAIOptions
import com.toggleai.sdk.EvaluationContext

// Create client
val client = ToggleAIClient(
    ToggleAIOptions(
        clientId = "pk_live_xxx",
        secret = "sk_live_xxx",
        pollingIntervalMs = 30_000,
        defaultContext = EvaluationContext(
            attributes = mapOf("server_region" to "us-east-1")
        ),
    )
)

// Initialize (suspend function β€” call from coroutine)
client.init()

// Evaluate flags
val context = EvaluationContext(
    userId = "user_123",
    attributes = mapOf("plan" to "premium", "country" to "US"),
)

if (client.getFlag("new-checkout", context)) {
    showNewCheckout()
}

// Typed flag value
val color = client.getFlagValue<String>("button-color", context, "#000000")
val maxItems = client.getFlagValue<Int>("max-items", context, 10)

// Remote config
val timeout = client.getConfig<Int>("api_timeout_ms", 5000)

// Cleanup
client.close()

Android Setup

Application Class

ToggleAIApp.kt
class ToggleAIApp : Application() {

    lateinit var toggleai: ToggleAIClient
        private set

    override fun onCreate() {
        super.onCreate()

        toggleai = ToggleAIClient(
            ToggleAIOptions(
                clientId = BuildConfig.TOGGLEAI_CLIENT_ID,
                secret = BuildConfig.TOGGLEAI_SECRET,
                pollingIntervalMs = 60_000, // poll less on mobile
                context = this, // Pass application context to capture device platform details
            )
        )

        // Initialize in a coroutine scope
        ProcessLifecycleOwner.get().lifecycleScope.launch {
            try {
                toggleai.init()
                Log.d("ToggleAI", "SDK initialized")
            } catch (e: Exception) {
                Log.e("ToggleAI", "Init failed", e)
            }
        }
    }
}

Usage in Activities / Fragments

CheckoutActivity.kt
class CheckoutActivity : AppCompatActivity() {

    private val toggleai get() = (application as ToggleAIApp).toggleai

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val userId = FirebaseAuth.getInstance().currentUser?.uid ?: "anonymous"
        val context = EvaluationContext(
            userId = userId,
            attributes = mapOf(
                "platform" to "android",
                "app_version" to BuildConfig.VERSION_NAME,
            ),
        )

        if (toggleai.getFlag("new-checkout-ui", context)) {
            setContentView(R.layout.activity_checkout_new)
        } else {
            setContentView(R.layout.activity_checkout_legacy)
        }

        // Read remote config
        val timeout = toggleai.getConfig<Int>("checkout_timeout_ms", 10_000)
    }
}

Jetpack Compose

CheckoutScreen.kt
@Composable
fun CheckoutScreen(toggleai: ToggleAIClient) {
    val context = remember {
        EvaluationContext(
            userId = currentUserId(),
            attributes = mapOf("platform" to "android"),
        )
    }

    val showNewUI = remember { toggleai.getFlag("new-checkout-ui", context) }
    val buttonColor = remember {
        toggleai.getFlagValue<String>("checkout-btn-color", context, "#2196F3")
    }

    if (showNewUI) {
        NewCheckoutUI(buttonColor = Color(android.graphics.Color.parseColor(buttonColor)))
    } else {
        LegacyCheckoutUI()
    }
}

Gradle Configuration

app/build.gradle
android {
    defaultConfig {
        minSdkVersion 21
        buildConfigField "String", "TOGGLEAI_CLIENT_ID",
            """ + (project.findProperty("TOGGLEAI_CLIENT_ID") ?: "") + """
        buildConfigField "String", "TOGGLEAI_SECRET",
            """ + (project.findProperty("TOGGLEAI_SECRET") ?: "") + """
    }
}

dependencies {
    implementation("com.toggleai:sdk-kotlin:1.0.0")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
}

Coroutine Support

Network operations (init(), evaluateFlagRemote(), flush()) are suspend functions. Local evaluation is synchronous and safe to call from any thread.

kotlin
// Suspend functions β€” call from coroutines
lifecycleScope.launch {
    client.init()
    val result = client.evaluateFlagRemote("dark-mode", context)
    println("Remote result: ${result.reason}")
}

// Synchronous β€” safe from any thread
val enabled = client.getFlag("dark-mode", context)     // instant, from cache
val timeout = client.getConfig<Int>("timeout_ms", 5000) // instant, from cache

Error Handling

kotlin
import com.toggleai.sdk.ToggleAIException

try {
    client.init()
} catch (e: ToggleAIException) {
    when (e.code) {
        "INVALID_KEY"   -> Log.e("ToggleAI", "Bad credentials")
        "RATE_LIMITED"  -> Log.e("ToggleAI", "Rate limited")
        "NETWORK_ERROR" -> Log.e("ToggleAI", "Cannot reach API")
        else            -> Log.e("ToggleAI", "Error: ${e.message}")
    }
}

// Logging errors
val logger = client.getLogger()
try {
    riskyOperation()
} catch (e: Exception) {
    logger.captureError(e, mapOf("screen" to "checkout"))
}

// Flush before app closes
logger.flush()

API Reference

MethodReturnsDescription
init()suspendFetch config, start polling
close()suspendStop polling, flush logs
refresh()suspendManually refresh config
getFlag(key, ctx?, default?)BooleanBoolean flag, local eval
getFlagValue<T>(key, ctx?, default?)T?Typed flag value
evaluateFlag(key, ctx?)FlagEvaluationResultFull evaluation
evaluateFlagRemote(key, ctx?)suspend β†’ ResultServer-side eval
getConfig<T>(key, default?)T?Typed config value
getAllConfigs()Map<String, Any?>All config values
hasConfig(key)BooleanKey existence check
getLogger()ToggleAILoggerGet attached logger
logger.info(msg, ctx?)β€”Log info
logger.captureError(err, ctx?)β€”Capture exception
logger.flush()suspendFlush queued events