CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-insert-koin--koin-core-iossimulatorarm64

Koin Core is a pragmatic lightweight dependency injection framework for Kotlin Multiplatform with iOS Simulator ARM64 target support.

Pending
Overview
Eval results
Files

scoping.mddocs/

Scope Management

This document covers Koin's scoping system, which provides lifecycle management for dependencies by grouping related instances that should be created and destroyed together.

Overview

Scopes in Koin enable you to:

  • Control instance lifecycles - Create and destroy groups of related instances
  • Isolate dependencies - Keep certain instances separate from global singletons
  • Manage resources - Ensure proper cleanup when scope is closed
  • Link scopes - Create hierarchical relationships between scopes

A scope represents a bounded context where instances live and die together, making it perfect for features like user sessions, request handling, or component lifecycles.

Scope Class

The Scope class provides the container for scoped instances and resolution methods:

class Scope(
    val scopeQualifier: Qualifier,
    val id: ScopeID,
    val isRoot: Boolean = false,
    val scopeArchetype: TypeQualifier? = null,
    @PublishedApi
    internal val _koin: Koin
) : Lockable() {
    val closed: Boolean
    
    // Resolution methods
    inline fun <reified T : Any> inject(
        qualifier: Qualifier? = null,
        mode: LazyThreadSafetyMode = LazyThreadSafetyMode.SYNCHRONIZED,
        noinline parameters: ParametersDefinition? = null
    ): Lazy<T>
    
    inline fun <reified T : Any> get(
        qualifier: Qualifier? = null,
        noinline parameters: ParametersDefinition? = null
    ): T
    
    inline fun <reified T : Any> getOrNull(/* ... */): T?
    inline fun <reified T : Any> injectOrNull(/* ... */): Lazy<T?>
    
    fun <T> get(clazz: KClass<*>, qualifier: Qualifier?, parameters: ParametersDefinition?): T
    fun <T> getOrNull(clazz: KClass<*>, qualifier: Qualifier?, parameters: ParametersDefinition?): T?
    
    inline fun <reified T : Any> getAll(): List<T>
    
    // Scope operations
    fun linkTo(vararg scopes: Scope)
    fun unlink(vararg scopes: Scope)
    fun close()
    
    // Instance declaration
    inline fun <reified T> declare(
        instance: T,
        qualifier: Qualifier? = null,
        secondaryTypes: List<KClass<*>> = emptyList(),
        allowOverride: Boolean = true,
        holdInstance: Boolean = false
    )
    
    // Scope access
    fun getKoin(): Koin
    fun getScope(scopeID: ScopeID): Scope
    
    // Callbacks
    fun registerCallback(callback: ScopeCallback)
    
    // Properties (inherited from Koin)
    fun <T : Any> getProperty(key: String, defaultValue: T): T
    fun <T : Any> getPropertyOrNull(key: String): T?
    fun <T : Any> getProperty(key: String): T
}

typealias ScopeID = String

Creating Scopes

Basic Scope Creation

import org.koin.core.qualifier.named

val koin = koinApplication { modules(myModule) }.koin

// Create scope with qualifier and ID
val userScope = koin.createScope("user-123", named("user"))

// Create scope with type-based qualifier
val sessionScope = koin.createScope<UserSession>("session-456")

// Create scope with auto-generated ID
val tempScope = koin.createScope<TempData>()

Scope Creation Methods

// In Koin class
fun createScope(scopeId: ScopeID, qualifier: Qualifier, source: Any? = null, scopeArchetype: TypeQualifier? = null): Scope

inline fun <reified T : Any> createScope(scopeId: ScopeID, source: Any? = null, scopeArchetype: TypeQualifier? = null): Scope

inline fun <reified T : Any> createScope(scopeId: ScopeID = KoinPlatformTools.generateId()): Scope

fun <T : KoinScopeComponent> createScope(t: T): Scope

// Get or create patterns
fun getOrCreateScope(scopeId: ScopeID, qualifier: Qualifier, source: Any? = null): Scope
inline fun <reified T : Any> getOrCreateScope(scopeId: ScopeID): Scope

Practical Scope Creation Examples

class UserSession(val userId: String)
class ShoppingCart(val sessionId: String)

// User session scope
val userSession = UserSession("user-123")
val sessionScope = koin.createScope<UserSession>("session-${userSession.userId}")

// Shopping cart scope  
val cartScope = koin.createScope("cart-abc", named("shopping"))

// Get or create - won't create duplicate if exists
val existingScope = koin.getOrCreateScope<UserSession>("session-user-123")

Defining Scoped Dependencies

Scope DSL

class ScopeDSL(val scopeQualifier: Qualifier, val module: Module) {
    inline fun <reified T> scoped(
        qualifier: Qualifier? = null,
        noinline definition: Definition<T>
    ): KoinDefinition<T>
    
    inline fun <reified T> factory(
        qualifier: Qualifier? = null,
        noinline definition: Definition<T>
    ): KoinDefinition<T>
}

Basic Scoped Definitions

import org.koin.dsl.*
import org.koin.core.qualifier.named

val userModule = module {
    // Define a scope for user sessions
    scope(named("user")) {
        scoped<UserPreferences> { UserPreferences() }
        scoped<UserCache> { UserCache() }
        factory<UserActivity> { UserActivity() }  // New instance each time
    }
}

Type-Based Scoped Definitions

class UserSession
class PaymentFlow

val scopedModule = module {
    // Type-based scope definition
    scope<UserSession> {
        scoped<SessionData> { SessionData() }
        scoped<AuthToken> { AuthToken() }
    }
    
    scope<PaymentFlow> {
        scoped<PaymentData> { PaymentData() }
        scoped<TransactionLog> { TransactionLog() }
    }
}

Scoped Dependencies with Injection

val advancedScopeModule = module {
    // Global singletons
    single<Database> { Database() }
    single<Logger> { Logger() }
    
    scope<UserSession> {
        // Scoped instances can inject global singletons
        scoped<UserRepository> { UserRepository(get()) }  // Injects Database
        
        // Scoped instances can inject other scoped instances
        scoped<UserService> { 
            UserService(get<UserRepository>(), get<Logger>()) 
        }
        
        // Factory within scope
        factory<UserQuery> { params ->
            val queryType: String = params.get()
            UserQuery(queryType, get<UserRepository>())
        }
    }
}

Working with Scopes

Resolving Scoped Dependencies

// Create scope instance
val userScope = koin.createScope<UserSession>("user-123")

// Resolve scoped dependencies
val userService: UserService = userScope.get()
val userRepo: UserRepository = userScope.get()

// Lazy resolution
val userCache: Lazy<UserCache> = userScope.inject()

// With parameters
val userQuery: UserQuery = userScope.get { parametersOf("findByEmail") }

// Nullable resolution
val optionalService: OptionalService? = userScope.getOrNull()

Scope Instance Lifecycle

// Create and use scope
val scope = koin.createScope<UserSession>("user-123")

// All scoped instances are created as needed
val service1 = scope.get<UserService>()  // Creates UserService + dependencies
val service2 = scope.get<UserService>()  // Returns same UserService instance

// Close scope - destroys all scoped instances
scope.close()

// Attempting to use closed scope throws exception
// val service3 = scope.get<UserService>()  // ClosedScopeException!

Scope Linking

Create hierarchical relationships between scopes:

// Parent scope
val applicationScope = koin.createScope<Application>("app")

// Child scope linked to parent
val userScope = koin.createScope<UserSession>("user-123")
userScope.linkTo(applicationScope)

// Child can resolve from parent scope
val appConfig: AppConfig = userScope.get()  // Resolves from applicationScope

// Multiple links
val requestScope = koin.createScope<RequestContext>("request-456")
requestScope.linkTo(userScope, applicationScope)  // Links to both

// Unlinking
requestScope.unlink(applicationScope)

Scope Callbacks

Register callbacks for scope lifecycle events:

import org.koin.core.scope.ScopeCallback

class UserSessionCallback : ScopeCallback {
    override fun onScopeClose(scope: Scope) {
        println("User session ${scope.id} is closing")
        // Cleanup user-specific resources
    }
}

val userScope = koin.createScope<UserSession>("user-123")
userScope.registerCallback(UserSessionCallback())

// When scope closes, callback is invoked
userScope.close()  // Prints: "User session user-123 is closing"

Runtime Scope Declaration

Declare instances directly in scopes at runtime:

Basic Declaration

val userScope = koin.createScope<UserSession>("user-123")

// Declare instance in scope
val sessionData = SessionData("user-123", "premium")
userScope.declare(sessionData)

// Now available for resolution
val data: SessionData = userScope.get()

Declaration with Options

// Declare with qualifier
userScope.declare(
    instance = UserPreferences("dark-mode"),
    qualifier = named("ui")
)

// Declare with multiple type bindings
interface Cache
class UserCache : Cache

userScope.declare(
    instance = UserCache(),
    secondaryTypes = listOf(Cache::class)
)

// Can resolve as either type
val cache: Cache = userScope.get()
val userCache: UserCache = userScope.get()

Hold Instance Control

// Instance held in scope (default)
userScope.declare(
    instance = PersistentData(),
    holdInstance = true  // Available in this and future scope instances
)

// Instance not held in scope
userScope.declare(
    instance = TemporaryData(),
    holdInstance = false  // Only available in current scope instance
)

Advanced Scope Patterns

User Session Management

class UserSessionManager(private val koin: Koin) {
    private val userScopes = mutableMapOf<String, Scope>()
    
    fun startUserSession(userId: String): Scope {
        val scope = koin.createScope<UserSession>("user-$userId")
        
        // Initialize session-specific data
        scope.declare(UserSessionData(userId))
        scope.declare(UserPreferences.load(userId))
        
        userScopes[userId] = scope
        return scope
    }
    
    fun endUserSession(userId: String) {
        userScopes[userId]?.close()
        userScopes.remove(userId)
    }
    
    fun getUserScope(userId: String): Scope? {
        return userScopes[userId]
    }
}

Request-Scoped Processing

class RequestProcessor(private val koin: Koin) {
    fun processRequest(requestId: String): Response {
        val requestScope = koin.createScope<RequestContext>(requestId)
        
        try {
            // Declare request-specific data
            requestScope.declare(RequestData(requestId))
            
            // Process with scoped dependencies
            val processor: RequestHandler = requestScope.get()
            return processor.handle()
            
        } finally {
            // Always cleanup
            requestScope.close()
        }
    }
}

Hierarchical Scope Architecture

class ApplicationArchitecture {
    fun setupScopes(): Triple<Scope, Scope, Scope> {
        val koin = koinApplication { modules(appModules) }.koin
        
        // Application level - global for app lifetime  
        val appScope = koin.createScope<Application>("app")
        
        // Feature level - for specific feature lifetime
        val featureScope = koin.createScope<FeatureContext>("feature-user")
        featureScope.linkTo(appScope)
        
        // Request level - for individual request lifetime
        val requestScope = koin.createScope<RequestContext>("request-123")
        requestScope.linkTo(featureScope, appScope)
        
        return Triple(appScope, featureScope, requestScope)
    }
}

Scope-Based Component Isolation

// Different scope contexts for different features
val eCommerceModule = module {
    scope<ShoppingCart> {
        scoped<CartService> { CartService() }
        scoped<PriceCalculator> { PriceCalculator() }
    }
    
    scope<Checkout> {
        scoped<PaymentProcessor> { PaymentProcessor() }
        scoped<OrderService> { OrderService() }
        factory<Receipt> { Receipt() }
    }
}

// Usage
val cartScope = koin.createScope<ShoppingCart>("cart-user123")
val checkoutScope = koin.createScope<Checkout>("checkout-order456")

// Services are isolated per scope
val cartService1 = cartScope.get<CartService>()        // ShoppingCart scope instance
val paymentProcessor = checkoutScope.get<PaymentProcessor>()  // Checkout scope instance

Error Handling

Scope-related exceptions:

// ClosedScopeException - using closed scope
val scope = koin.createScope<UserSession>("test")
scope.close()
// val service = scope.get<Service>()  // Throws ClosedScopeException

// ScopeNotCreatedException - accessing non-existent scope
// val nonExistent = koin.getScope("does-not-exist")  // Throws ScopeNotCreatedException

// ScopeAlreadyCreatedException - creating duplicate scope
val scope1 = koin.createScope<UserSession>("duplicate")
// val scope2 = koin.createScope<UserSession>("duplicate")  // May throw ScopeAlreadyCreatedException

Best Practices

1. Use Clear Scope Boundaries

// Good - clear lifecycle boundaries
class UserSessionScope {
    companion object {
        fun create(koin: Koin, userId: String): Scope {
            return koin.createScope<UserSession>("user-$userId")
        }
    }
}

2. Always Close Scopes

// Use try-finally or use pattern
fun processUserRequest(userId: String) {
    val userScope = koin.createScope<UserSession>("user-$userId")
    try {
        val service: UserService = userScope.get()
        service.processRequest()
    } finally {
        userScope.close()  // Always cleanup
    }
}

3. Link Scopes Appropriately

// Child scopes should link to parents for dependency resolution
val appScope = koin.createScope<Application>("app")
val userScope = koin.createScope<UserSession>("user-123")
userScope.linkTo(appScope)  // User scope can access app-level dependencies

4. Use Callbacks for Resource Cleanup

class ResourceCleanupCallback : ScopeCallback {
    override fun onScopeClose(scope: Scope) {
        // Cleanup any resources that need explicit cleanup
        scope.getOrNull<FileResource>()?.close()
        scope.getOrNull<NetworkConnection>()?.disconnect()
    }
}

Scopes provide powerful lifecycle management capabilities that enable clean resource management and component isolation in complex applications.

Install with Tessl CLI

npx tessl i tessl/maven-io-insert-koin--koin-core-iossimulatorarm64

docs

application-setup.md

components.md

dependency-injection.md

error-handling.md

index.md

modules-and-definitions.md

qualifiers-parameters.md

scoping.md

tile.json