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

components.mddocs/

Component Integration

This document covers how to integrate dependency injection into your classes using Koin's component interfaces and extension functions.

Overview

Koin provides component interfaces that enable seamless dependency injection integration:

  • KoinComponent - Basic dependency injection capabilities
  • KoinScopeComponent - Scope-aware dependency injection with lifecycle management

These interfaces provide extension functions that allow your classes to directly access the dependency injection container without explicitly passing around Koin instances.

KoinComponent Interface

The basic interface for dependency injection integration:

interface KoinComponent {
    fun getKoin(): Koin
}

Extension Functions

// Direct resolution
inline fun <reified T : Any> KoinComponent.get(
    qualifier: Qualifier? = null,
    noinline parameters: ParametersDefinition? = null
): T

// Lazy injection
inline fun <reified T : Any> KoinComponent.inject(
    qualifier: Qualifier? = null,
    mode: LazyThreadSafetyMode = KoinPlatformTools.defaultLazyMode(),
    noinline parameters: ParametersDefinition? = null
): Lazy<T>

Basic Implementation

import org.koin.core.component.KoinComponent
import org.koin.core.component.get
import org.koin.core.component.inject

class UserService : KoinComponent {
    // Direct injection - resolved immediately
    private val repository: UserRepository = get()
    
    // Lazy injection - resolved when first accessed
    private val logger: Logger by inject()
    
    // Qualified injection
    private val cache: Cache = get(named("user"))
    
    fun processUser(userId: String) {
        logger.info("Processing user: $userId")
        val user = repository.findById(userId)
        // ... process user
    }
}

With Parameters

import org.koin.core.parameter.parametersOf

class ConfigurableService : KoinComponent {
    fun createClient(endpoint: String, timeout: Int): ApiClient {
        return get { parametersOf(endpoint, timeout) }
    }
    
    fun getEnvironmentConfig(): Config {
        val env = System.getenv("ENVIRONMENT") ?: "development"
        return get(named(env))
    }
}

Custom Koin Context

class IsolatedService(private val customKoin: Koin) : KoinComponent {
    // Override to use custom Koin instance
    override fun getKoin(): Koin = customKoin
    
    private val service: DataService by inject()
}

KoinScopeComponent Interface

The scope-aware interface that automatically manages scoped dependencies:

interface KoinScopeComponent : KoinComponent {
    val scope: Scope
}

Extension Functions

// Scope identification
fun <T : Any> T.getScopeId(): String
fun <T : Any> T.getScopeName(): TypeQualifier

// Scope creation
fun <T : KoinScopeComponent> T.createScope(
    scopeId: ScopeID = getScopeId(),
    source: Any? = null,
    scopeArchetype: TypeQualifier? = null
): Scope

fun <T : KoinScopeComponent> T.createScope(source: Any? = null): Scope

// Scope access
fun <T : KoinScopeComponent> T.getScopeOrNull(): Scope?

// Lazy scope creation
fun <T : KoinScopeComponent> T.newScope(): Lazy<Scope>
fun <T : KoinScopeComponent> T.getOrCreateScope(): Lazy<Scope>

Basic Scope Component

import org.koin.core.component.KoinScopeComponent
import org.koin.core.component.createScope
import org.koin.core.component.get
import org.koin.core.scope.Scope

class UserSession : KoinScopeComponent {
    // Automatically creates scope for this instance
    override val scope: Scope by lazy { createScope() }
    
    // Scoped dependencies - shared within this session
    private val sessionData: SessionData by inject()
    private val preferences: UserPreferences by inject()
    
    fun login(credentials: Credentials) {
        // Dependencies are resolved from this instance's scope
        sessionData.initialize(credentials.userId)
        preferences.load(credentials.userId)
    }
    
    fun logout() {
        // Close scope when session ends
        scope.close()
    }
}

Manual Scope Management

class CustomScopeComponent : KoinScopeComponent {
    private var _scope: Scope? = null
    
    override val scope: Scope
        get() = _scope ?: throw IllegalStateException("Scope not initialized")
    
    fun initialize() {
        _scope = createScope()
    }
    
    fun cleanup() {
        _scope?.close()
        _scope = null
    }
}

Scope Resolution Behavior

When using KoinScopeComponent, the get() and inject() functions automatically use the component's scope:

class ScopedService : KoinScopeComponent {
    override val scope: Scope by lazy { createScope() }
    
    fun example() {
        // These resolve from the component's scope
        val service1: MyService = get()         // scope.get<MyService>()
        val service2: MyService by inject()     // scope.inject<MyService>()
        
        // Equivalent explicit calls
        val service3: MyService = scope.get()
        val service4: MyService by scope.inject()
    }
}

Practical Implementation Patterns

Service Layer Integration

// Business service with dependency injection
class OrderService : KoinComponent {
    private val orderRepository: OrderRepository by inject()
    private val paymentService: PaymentService by inject()
    private val emailService: EmailService by inject()
    private val logger: Logger by inject()
    
    fun processOrder(order: Order): OrderResult {
        logger.info("Processing order ${order.id}")
        
        return try {
            // Validate order
            val validatedOrder = validateOrder(order)
            
            // Process payment
            val payment = paymentService.processPayment(validatedOrder.total)
            
            // Save order
            val savedOrder = orderRepository.save(validatedOrder.copy(paymentId = payment.id))
            
            // Send confirmation
            emailService.sendOrderConfirmation(savedOrder)
            
            OrderResult.Success(savedOrder)
        } catch (e: Exception) {
            logger.error("Failed to process order ${order.id}", e)
            OrderResult.Failure(e.message)
        }
    }
    
    private fun validateOrder(order: Order): Order {
        // Validation logic
        return order
    }
}

UI Component Integration

// Android ViewModel example
class UserViewModel : KoinComponent {
    private val userService: UserService by inject()
    private val analyticsService: AnalyticsService by inject(named("firebase"))
    
    private val _users = MutableLiveData<List<User>>()
    val users: LiveData<List<User>> = _users
    
    fun loadUsers() {
        viewModelScope.launch {
            try {
                val userList = userService.getAllUsers()
                _users.value = userList
                analyticsService.track("users_loaded", mapOf("count" to userList.size))
            } catch (e: Exception) {
                // Handle error
            }
        }
    }
}

Scoped Component Lifecycle

class RequestHandler : KoinScopeComponent {
    // Each request gets its own scope
    override val scope: Scope by lazy { createScope() }
    
    private val requestData: RequestData by inject()
    private val auditLogger: AuditLogger by inject()
    
    fun handleRequest(request: HttpRequest): HttpResponse {
        try {
            // Process with scoped dependencies
            auditLogger.logRequest(request)
            
            val result = processRequest(request)
            
            auditLogger.logResponse(result)
            return result
            
        } finally {
            // Always cleanup scope
            scope.close()
        }
    }
    
    private fun processRequest(request: HttpRequest): HttpResponse {
        // Business logic using scoped dependencies
        return HttpResponse.ok()
    }
}

Factory with Component Integration

class ServiceFactory : KoinComponent {
    fun createUserService(userId: String): UserService {
        // Inject dependencies for the service
        val repository: UserRepository = get()
        val cache: Cache = get(named("user"))
        
        return UserService(userId, repository, cache)
    }
    
    fun createAdminService(adminLevel: Int): AdminService {
        return get { parametersOf(adminLevel) }
    }
}

Advanced Component Patterns

Component with Multiple Scopes

class MultiScopeComponent : KoinComponent {
    private val globalKoin = getKoin()
    
    // Different scopes for different contexts
    private val sessionScope: Scope by lazy {
        globalKoin.createScope<UserSession>("session-${sessionId()}")
    }
    
    private val requestScope: Scope by lazy {
        globalKoin.createScope<RequestContext>("request-${requestId()}")
    }
    
    fun getSessionService(): SessionService = sessionScope.get()
    fun getRequestService(): RequestService = requestScope.get()
    
    private fun sessionId(): String = "session-123"
    private fun requestId(): String = "request-456"
}

Component with Conditional Injection

class ConditionalComponent : KoinComponent {
    private val featureFlag: FeatureFlag by inject()
    
    // Conditional lazy injection based on feature flags
    private val newFeatureService: NewFeatureService? by lazy {
        if (featureFlag.isEnabled("new_feature")) {
            get<NewFeatureService>()
        } else {
            null
        }
    }
    
    fun performAction() {
        if (newFeatureService != null) {
            newFeatureService.performNewAction()
        } else {
            // Fallback behavior
            performLegacyAction()
        }
    }
    
    private fun performLegacyAction() {
        // Legacy implementation
    }
}

Component Inheritance

abstract class BaseComponent : KoinComponent {
    protected val logger: Logger by inject()
    protected val config: Config by inject()
    
    protected fun logOperation(operation: String) {
        logger.info("$operation in ${this::class.simpleName}")
    }
}

class UserComponent : BaseComponent() {
    private val userService: UserService by inject()
    
    fun processUser(userId: String) {
        logOperation("processUser")  // Uses inherited logger
        userService.process(userId)
    }
}

class OrderComponent : BaseComponent() {
    private val orderService: OrderService by inject()
    
    fun processOrder(orderId: String) {
        logOperation("processOrder")  // Uses inherited logger
        orderService.process(orderId)
    }
}

Testing with Components

Component Testing

class TestableComponent : KoinComponent {
    private val service: MyService by inject()
    
    fun doSomething(): String {
        return service.getData()
    }
}

// Test with mock
@Test
fun testComponent() {
    val testModule = module {
        single<MyService> { mockk<MyService> {
            every { getData() } returns "test data"
        }}
    }
    
    val testApp = koinApplication { modules(testModule) }
    
    val component = TestableComponent()
    // Component uses test Koin context automatically
    assertEquals("test data", component.doSomething())
}

Scoped Component Testing

@Test
fun testScopedComponent() {
    val testModule = module {
        scope<TestScope> {
            scoped<ScopedService> { mockk<ScopedService>() }
        }
    }
    
    val koin = koinApplication { modules(testModule) }.koin
    
    class TestComponent : KoinScopeComponent {
        override val scope = koin.createScope<TestScope>("test")
        
        fun getService(): ScopedService = get()
    }
    
    val component = TestComponent()
    assertNotNull(component.getService())
    
    component.scope.close()
}

Best Practices

1. Use Lazy Injection for Expensive Resources

class OptimizedComponent : KoinComponent {
    // Lazy injection - only created when needed
    private val expensiveService: ExpensiveService by inject()
    
    // Direct injection for lightweight dependencies
    private val config: Config = get()
    
    fun performOperation() {
        // ExpensiveService is created here, not at component creation
        expensiveService.doWork()
    }
}

2. Proper Scope Lifecycle Management

class ProperScopeComponent : KoinScopeComponent {
    override val scope: Scope by lazy { createScope() }
    
    fun initialize() {
        // Setup scope dependencies
    }
    
    fun cleanup() {
        // Always close scope when done
        if (!scope.closed) {
            scope.close()
        }
    }
}

3. Avoid Direct Koin Access

// Good - uses component interface
class GoodComponent : KoinComponent {
    private val service: MyService by inject()
}

// Avoid - direct Koin dependency
class AvoidComponent(private val koin: Koin) {
    private val service: MyService = koin.get()
}

4. Component Interface Segregation

// Specific interfaces for different component types
interface ServiceComponent : KoinComponent
interface RepositoryComponent : KoinComponent  
interface ScopedServiceComponent : KoinScopeComponent

class UserService : ServiceComponent {
    private val repository: UserRepository by inject()
}

class UserSessionManager : ScopedServiceComponent {
    override val scope: Scope by lazy { createScope() }
    private val sessionData: SessionData by inject()
}

Component interfaces provide a clean, testable way to integrate dependency injection into your application architecture while maintaining loose coupling and supporting various lifecycle patterns.

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