CtrlK
BlogDocsLog inGet started
Tessl Logo

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

Koin dependency injection integration with Jetpack Compose for Kotlin Multiplatform development.

Pending
Overview
Eval results
Files

scope-management.mddocs/

Scope Management

Experimental APIs for creating and managing Koin scopes with automatic lifecycle management tied to Compose recomposition, enabling fine-grained dependency scoping within Compose hierarchies.

Capabilities

KoinScope with Definition

Create Koin Scope using a lambda definition and automatically close it when the Composition is forgotten or abandoned.

/**
 * Create Koin Scope and close it when Composition is forgotten/abandoned
 * @param scopeDefinition Lambda to define scope
 * @param content Composable content
 */
@KoinExperimentalAPI
@Composable
fun KoinScope(
    scopeDefinition: Koin.() -> Scope,
    content: @Composable () -> Unit
)

Usage Examples:

@Composable
fun FeatureWithCustomScope() {
    KoinScope(
        scopeDefinition = { 
            createScope("feature-scope", named("feature"))
        }
    ) {
        // Content has access to feature-scoped dependencies
        FeatureContent()
    }
}

@Composable  
fun DynamicScope(userId: String) {
    KoinScope(
        scopeDefinition = {
            createScope("user-$userId", named("user")) {
                // Scope parameters can be provided
                parametersOf(userId)
            }
        }
    ) {
        UserProfile(userId)
    }
}

Typed KoinScope with ScopeID

Create typed Koin Scope with automatic lifecycle management using a scope identifier.

/**
 * Create typed Koin Scope with automatic lifecycle management
 * @param T Scope type
 * @param scopeID Scope identifier
 * @param content Composable content
 */
@KoinExperimentalAPI
@Composable
inline fun <reified T : Any> KoinScope(
    scopeID: ScopeID,
    noinline content: @Composable () -> Unit
)

Usage Examples:

// Define scope type
class UserSessionScope

@Composable
fun UserSession(sessionId: String) {
    KoinScope<UserSessionScope>(scopeID = sessionId) {
        // Content within user session scope
        SessionContent()
    }
}

@Composable
fun TypedFeatureScope() {
    KoinScope<FeatureScope>(scopeID = "main-feature") {
        FeatureComponents()
    }
}

KoinScope with Qualifier

Create Koin Scope with a qualifier and automatic lifecycle management.

/**
 * Create Koin Scope with qualifier and automatic lifecycle management
 * @param scopeID Scope identifier
 * @param scopeQualifier Scope qualifier
 * @param content Composable content
 */
@KoinExperimentalAPI
@Composable
fun KoinScope(
    scopeID: ScopeID,
    scopeQualifier: Qualifier,
    content: @Composable () -> Unit
)

Usage Examples:

@Composable
fun QualifiedScope(featureId: String) {
    KoinScope(
        scopeID = featureId,
        scopeQualifier = named("feature-scope")
    ) {
        // Content with qualified scope access
        QualifiedFeatureContent()
    }
}

@Composable
fun MultiLevelScope() {
    KoinScope(
        scopeID = "level1", 
        scopeQualifier = named("primary")
    ) {
        KoinScope(
            scopeID = "level2",
            scopeQualifier = named("secondary")  
        ) {
            NestedScopeContent()
        }
    }
}

Remember Koin Scope

Remember Koin Scope with automatic closure handling when the composable is disposed.

/**
 * Remember Koin Scope with automatic closure handling
 * @param scope Koin scope to remember
 * @return The same scope with lifecycle management
 */
@KoinExperimentalAPI
@Composable
fun rememberKoinScope(scope: Scope): Scope

Usage Examples:

@Composable
fun RememberedScope() {
    val koin = getKoin()
    val customScope = rememberKoinScope(
        koin.createScope("custom", named("feature"))
    )
    
    // Use the remembered scope
    val scopedService: ScopedService = koinInject(scope = customScope)
    
    // Scope will be automatically closed when component is disposed
}

@Composable
fun ConditionalScope(condition: Boolean) {
    val koin = getKoin()
    
    if (condition) {
        val conditionalScope = rememberKoinScope(
            koin.createScope("conditional", named("temp"))
        )
        
        ConditionalContent(scope = conditionalScope)
    }
}

Lifecycle Management

Automatic Cleanup

All scope management functions automatically handle scope lifecycle:

@Composable
fun LifecycleExample() {
    // Scope created when composable enters composition
    KoinScope(
        scopeDefinition = { createScope("auto-cleanup", named("temp")) }
    ) {
        // Scope available here
        
        LaunchedEffect(Unit) {
            println("Scope created and available")
        }
        
        DisposableEffect(Unit) {
            onDispose {
                // Scope automatically closed here
                println("Scope will be closed")
            }
        }
        
        ScopedContent()
    }
    // Scope closed when composable leaves composition
}

Manual Scope Control

For advanced use cases, combine with manual scope management:

@Composable
fun ManualScopeControl() {
    val koin = getKoin()
    var manualScope by remember { mutableStateOf<Scope?>(null) }
    
    // Create scope manually
    Button(
        onClick = {
            manualScope = koin.createScope("manual", named("controlled"))
        }
    ) {
        Text("Create Scope")
    }
    
    // Use remembered scope when available
    manualScope?.let { scope ->
        val rememberedScope = rememberKoinScope(scope)
        
        ScopedContent(scope = rememberedScope)
        
        Button(
            onClick = {
                scope.close()
                manualScope = null
            }
        ) {
            Text("Close Scope")
        }
    }
}

Scope Hierarchies

Nested Scopes

Create hierarchical scope structures:

@Composable
fun NestedScopes() {
    KoinScope(
        scopeID = "app-feature",
        scopeQualifier = named("feature")
    ) {
        // Parent scope content
        ParentScopeContent()
        
        KoinScope(
            scopeID = "sub-feature", 
            scopeQualifier = named("sub-feature")
        ) {
            // Child scope content with access to parent scope
            ChildScopeContent()
        }
    }
}

Scope Communication

Share data between scopes:

@Composable
fun ScopeCommunication() {
    KoinScope(
        scopeDefinition = { 
            createScope("publisher", named("data-source"))
        }
    ) {
        val publisher: DataPublisher = koinInject()
        
        KoinScope(
            scopeDefinition = {
                createScope("subscriber", named("data-consumer"))
            }
        ) {
            val subscriber: DataSubscriber = koinInject()
            
            LaunchedEffect(publisher, subscriber) {
                publisher.dataFlow.collect { data ->
                    subscriber.process(data)
                }
            }
            
            ConsumerContent()
        }
    }
}

Integration with State

Scope-Based State

Create state that's tied to scope lifecycle:

@Composable
fun ScopeBasedState() {
    KoinScope(
        scopeID = "stateful",
        scopeQualifier = named("state-scope")
    ) {
        val stateManager: StateManager = koinInject()
        var state by remember { mutableStateOf(stateManager.initialState) }
        
        LaunchedEffect(stateManager) {
            stateManager.stateFlow.collect { newState ->
                state = newState
            }
        }
        
        StatefulContent(
            state = state,
            onStateChange = stateManager::updateState
        )
    }
}

Reactive Scope Updates

React to scope changes:

@Composable
fun ReactiveScopeUpdates(scopeConfig: ScopeConfig) {
    KoinScope(
        scopeDefinition = {
            createScope(scopeConfig.id, scopeConfig.qualifier)
        }
    ) {
        val currentScope = currentKoinScope()
        
        LaunchedEffect(currentScope.id) {
            println("Scope changed to: ${currentScope.id}")
        }
        
        ScopeAwareContent(scopeId = currentScope.id)
    }
}

Error Handling

Scope Creation Errors

Handle scope creation and lifecycle errors:

@Composable
fun SafeScopeCreation() {
    var scopeError by remember { mutableStateOf<String?>(null) }
    
    scopeError?.let { error ->
        ErrorDisplay(error) {
            scopeError = null
        }
    }
    
    try {
        KoinScope(
            scopeDefinition = {
                // Scope creation might fail
                createScope("risky-scope", named("risky"))
            }
        ) {
            SafeScopeContent()
        }
    } catch (e: Exception) {
        LaunchedEffect(e) {
            scopeError = "Scope creation failed: ${e.message}"
        }
    }
}

Scope Dependency Errors

Handle missing scoped dependencies:

@Composable
fun SafeScopedInjection() {
    KoinScope(
        scopeID = "feature",
        scopeQualifier = named("feature-scope")
    ) {
        try {
            val service: ScopedService = koinInject()
            ServiceContent(service)
        } catch (e: NoBeanDefFoundException) {
            MissingDependencyFallback()
        }
    }
}

Performance Considerations

Scope Reuse

Reuse scopes when possible to avoid creation overhead:

@Composable
fun EfficientScopeUsage(featureId: String) {
    // Scope recreated only when featureId changes
    KoinScope(
        scopeDefinition = {
            createScope("feature-$featureId", named("feature"))
        }
    ) {
        FeatureContent(featureId)
    }
}

Conditional Scoping

Create scopes only when needed:

@Composable
fun ConditionalScopeCreation(needsScope: Boolean) {
    if (needsScope) {
        KoinScope(
            scopeID = "conditional",
            scopeQualifier = named("when-needed")
        ) {
            ScopedContent()
        }
    } else {
        UnscopedContent()
    }
}

Best Practices

  1. Lifecycle Awareness: Trust automatic cleanup - scopes are closed when composables are disposed
  2. Scope Naming: Use descriptive scope IDs and qualifiers for debugging
  3. Hierarchy Design: Design scope hierarchies to match your component structure
  4. Error Handling: Implement proper error handling for scope creation and dependency resolution
  5. Performance: Reuse scopes when possible and create them conditionally
  6. State Integration: Leverage scope lifecycle for component-specific state management
  7. Experimental API: Remember these are experimental APIs and may change in future versions

Install with Tessl CLI

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

docs

application-setup.md

context-access.md

dependency-injection.md

index.md

module-management.md

scope-management.md

tile.json