CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-insert-koin--koin-compose

Jetpack Compose integration for Koin dependency injection framework providing Compose-specific APIs for dependency injection

Pending
Overview
Eval results
Files

context-access.mddocs/

Context Access

Functions for accessing the current Koin application instance and scope within Compose functions, providing fallback mechanisms to ensure reliable access to dependency injection context.

Capabilities

Get Koin Application

Retrieve the current Koin application from the composition with automatic fallback to the default platform context.

/**
 * Retrieve the current Koin application from the composition.
 * Falls back to default context if composition context is not available.
 *
 * @return Current Koin application instance
 * @throws Exception if no Koin context is available
 */
@Composable
fun getKoin(): Koin

Usage Examples:

import org.koin.compose.getKoin

@Composable
fun DiagnosticsScreen() {
    val koin = getKoin()
    
    // Access Koin application properties
    val logger = koin.logger
    val scopeRegistry = koin.scopeRegistry
    
    // Create custom scopes
    val customScope = koin.createScope<MyScope>("custom-scope-id")
    
    // Log current state
    LaunchedEffect(Unit) {
        logger.info("Current Koin instance: $koin")
        logger.info("Active scopes: ${scopeRegistry.size}")
    }
}

@Composable
fun ManualDependencyAccess() {
    val koin = getKoin()
    
    // Manually get dependencies (alternative to koinInject)
    val userService = koin.get<UserService>()
    val apiClient = koin.get<ApiClient>(qualifier = named("public"))
    
    // Access with parameters
    val sessionService = koin.get<SessionService> { 
        parametersOf("user-123") 
    }
}

Get Current Scope

Retrieve the current Koin scope from the composition with automatic fallback to root scope.

/**
 * Retrieve the current Koin scope from the composition
 * Falls back to root scope if composition scope is not available or closed
 *
 * @return Current Koin scope instance
 * @throws Exception if no Koin scope is available
 */
@Composable
fun currentKoinScope(): Scope

Usage Examples:

import org.koin.compose.currentKoinScope

@Composable
fun ScopeAwareComponent() {
    val currentScope = currentKoinScope()
    
    // Check scope properties
    val scopeId = currentScope.id
    val isRootScope = currentScope.isRoot
    val isClosed = currentScope.closed
    
    // Use scope directly for dependency resolution
    val service = currentScope.get<MyService>()
    
    Text("Current scope: $scopeId (root: $isRootScope)")
}

@Composable
fun ScopeInspector() {
    val scope = currentKoinScope()
    
    // Access scope metadata
    LaunchedEffect(scope) {
        scope.logger.debug("Accessing scope: ${scope.id}")
        
        // Check if specific dependency exists in this scope
        val hasUserService = try {
            scope.get<UserService>()
            true
        } catch (e: Exception) {
            false
        }
        
        scope.logger.info("UserService available in scope: $hasUserService")
    }
}

Composition Local Integration

Both functions integrate with Compose's CompositionLocal system:

LocalKoinApplication

/**
 * Current Koin Application context, as default with Default Koin context
 */
val LocalKoinApplication: ProvidableCompositionLocal<Koin>

LocalKoinScope

/**
 * Current Koin Scope, as default with Default Koin context root scope
 */
val LocalKoinScope: ProvidableCompositionLocal<Scope>

Usage with CompositionLocalProvider:

import androidx.compose.runtime.CompositionLocalProvider
import org.koin.compose.LocalKoinApplication
import org.koin.compose.LocalKoinScope

@Composable
fun CustomKoinProvider(
    customKoin: Koin,
    customScope: Scope,
    content: @Composable () -> Unit
) {
    CompositionLocalProvider(
        LocalKoinApplication provides customKoin,
        LocalKoinScope provides customScope
    ) {
        content()
    }
}

Error Handling and Fallbacks

getKoin() Error Handling

The function includes comprehensive error handling with fallback mechanisms:

// Internal implementation with fallback
@Composable
fun getKoin(): Koin = currentComposer.run {
    try {
        consume(LocalKoinApplication)
    } catch (e: Exception) {
        KoinPlatform.getKoinOrNull()?.let {
            it.logger.debug("Error while accessing Koin context. Fallback on default context ...")
            it
        } ?: error("Can't get Koin context due to error: $e")
    }
}

currentKoinScope() Error Handling

The function handles closed scope exceptions gracefully:

// Internal implementation with fallback
@Composable  
fun currentKoinScope(): Scope = currentComposer.run {
    try {
        consume(LocalKoinScope)
    } catch (e: ClosedScopeException) {
        KoinPlatform.getKoinOrNull()?.let {
            it.logger.debug("Error while accessing Koin scope. Fallback on default root scope...")
            it.scopeRegistry.rootScope
        } ?: error("Can't get Koin scope due to error: $e")
    }
}

Advanced Usage Patterns

Context Validation

@Composable
fun ValidatedKoinAccess() {
    val koin = try {
        getKoin()
    } catch (e: Exception) {
        null
    }
    
    if (koin != null) {
        // Koin is available
        val service = koin.get<MyService>()
        ServiceContent(service)
    } else {
        // Koin not available, show error or fallback
        Text("Dependency injection not available")
    }
}

Scope Lifecycle Monitoring

@Composable
fun ScopeLifecycleMonitor() {
    val scope = currentKoinScope()
    
    DisposableEffect(scope) {
        scope.logger.debug("Scope ${scope.id} entered composition")
        
        onDispose {
            scope.logger.debug("Scope ${scope.id} left composition")
        }
    }
}

Manual Dependency Resolution

@Composable
fun ManualResolution() {
    val koin = getKoin()
    val scope = currentKoinScope()
    
    // Different ways to resolve dependencies
    val service1 = koin.get<MyService>() // From Koin instance
    val service2 = scope.get<MyService>() // From specific scope
    val service3 = koinInject<MyService>() // Using koinInject (recommended)
    
    // All three approaches can yield the same or different instances
    // depending on scope configuration
}

Core Types

// Core Koin interfaces
interface Koin {
    val logger: Logger
    val scopeRegistry: ScopeRegistry
    fun <T : Any> get(clazz: KClass<T>, qualifier: Qualifier? = null): T
    fun <T : Any> get(qualifier: Qualifier? = null, parameters: ParametersDefinition): T
    fun createScope(scopeId: String): Scope
    fun createScope<T>(scopeId: String): Scope
}

interface Scope {
    val id: String
    val isRoot: Boolean
    val closed: Boolean
    val logger: Logger
    fun <T : Any> get(clazz: KClass<T>, qualifier: Qualifier? = null): T
    fun <T : Any> getWithParameters(clazz: KClass<T>, qualifier: Qualifier?, parameters: ParametersHolder): T
    fun close()
}

// Composition local types
interface ProvidableCompositionLocal<T> {
    val defaultFactory: () -> T
}

Platform Differences

The fallback mechanisms work differently across platforms:

Android

Falls back to KoinPlatform.getKoin() which looks for Application-level Koin setup

JVM/Desktop

Falls back to global Koin instance managed by KoinPlatform

JavaScript/WebAssembly/Native

Falls back to platform-specific Koin platform implementations

Error Types

// Common exceptions
class ClosedScopeException : Exception()
class UnknownKoinContext : Exception()
class NoBeanDefFoundException : Exception()

Common error scenarios:

  • UnknownKoinContext: No Koin context available in composition or platform
  • ClosedScopeException: Trying to access a scope that has been closed
  • Composition errors: Issues with CompositionLocal access during recomposition

Install with Tessl CLI

npx tessl i tessl/maven-io-insert-koin--koin-compose

docs

application-setup.md

context-access.md

dependency-injection.md

index.md

module-management.md

scope-management.md

tile.json