--- paths: - "**/*.kt" - "**/*.kts" --- # Kotlin Patterns > This file extends [common/patterns.md](../common/patterns.md) with Kotlin and Android/KMP-specific content. ## Dependency Injection Prefer constructor injection. Use Koin (KMP) or Hilt (Android-only): ```kotlin // Koin — declare modules val dataModule = module { single { ItemRepositoryImpl(get(), get()) } factory { GetItemsUseCase(get()) } viewModelOf(::ItemListViewModel) } // Hilt — annotations @HiltViewModel class ItemListViewModel @Inject constructor( private val getItems: GetItemsUseCase ) : ViewModel() ``` ## ViewModel Pattern Single state object, event sink, one-way data flow: ```kotlin data class ScreenState( val items: List = emptyList(), val isLoading: Boolean = false ) class ScreenViewModel(private val useCase: GetItemsUseCase) : ViewModel() { private val _state = MutableStateFlow(ScreenState()) val state = _state.asStateFlow() fun onEvent(event: ScreenEvent) { when (event) { is ScreenEvent.Load -> load() is ScreenEvent.Delete -> delete(event.id) } } } ``` ## Repository Pattern - `suspend` functions return `Result` or custom error type - `Flow` for reactive streams - Coordinate local + remote data sources ```kotlin interface ItemRepository { suspend fun getById(id: String): Result suspend fun getAll(): Result> fun observeAll(): Flow> } ``` ## UseCase Pattern Single responsibility, `operator fun invoke`: ```kotlin class GetItemUseCase(private val repository: ItemRepository) { suspend operator fun invoke(id: String): Result { return repository.getById(id) } } class GetItemsUseCase(private val repository: ItemRepository) { suspend operator fun invoke(): Result> { return repository.getAll() } } ``` ## expect/actual (KMP) Use for platform-specific implementations: ```kotlin // commonMain expect fun platformName(): String expect class SecureStorage { fun save(key: String, value: String) fun get(key: String): String? } // androidMain actual fun platformName(): String = "Android" actual class SecureStorage { actual fun save(key: String, value: String) { /* EncryptedSharedPreferences */ } actual fun get(key: String): String? = null /* ... */ } // iosMain actual fun platformName(): String = "iOS" actual class SecureStorage { actual fun save(key: String, value: String) { /* Keychain */ } actual fun get(key: String): String? = null /* ... */ } ``` ## Coroutine Patterns - Use `viewModelScope` in ViewModels, `coroutineScope` for structured child work - Use `stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), initialValue)` for StateFlow from cold Flows - Use `supervisorScope` when child failures should be independent ## Builder Pattern with DSL ```kotlin class HttpClientConfig { var baseUrl: String = "" var timeout: Long = 30_000 private val interceptors = mutableListOf() fun interceptor(block: () -> Interceptor) { interceptors.add(block()) } } fun httpClient(block: HttpClientConfig.() -> Unit): HttpClient { val config = HttpClientConfig().apply(block) return HttpClient(config) } // Usage val client = httpClient { baseUrl = "https://api.example.com" timeout = 15_000 interceptor { AuthInterceptor(tokenProvider) } } ``` ## References See skill: `kotlin-coroutines-flows` for detailed coroutine patterns. See skill: `android-clean-architecture` for module and layer patterns.