Quick Example
This page walks you through a minimal Anchor DI setup in four steps. You'll add dependencies, define a few types, initialize the container, and inject them. By the end, you'll have a working example you can extend.
Prerequisites
- A Kotlin Multiplatform project (or a new one created from a template)
- Kotlin 1.9+, KSP 2.3+, Gradle 8+
Step 1: Add Dependencies
Add Anchor DI to your shared module (the one with commonMain). If you use Compose Multiplatform for UI, add anchor-di-compose as well.
// build.gradle.kts (shared module or project)
plugins {
id("com.google.devtools.ksp") version "2.3.5"
}
repositories {
mavenCentral()
}
dependencies {
implementation("io.github.12345debdut:anchor-di-api:x.x.x")
implementation("io.github.12345debdut:anchor-di-core:x.x.x")
implementation("io.github.12345debdut:anchor-di-compose:x.x.x") // For Compose
add("kspCommonMainMetadata", "io.github.12345debdut:anchor-di-ksp:x.x.x")
add("kspAndroid", "io.github.12345debdut:anchor-di-ksp:x.x.x")
add("kspIosArm64", "io.github.12345debdut:anchor-di-ksp:x.x.x")
add("kspIosSimulatorArm64", "io.github.12345debdut:anchor-di-ksp:x.x.x")
}
What each dependency does:
anchor-di-api— Annotations (@Inject,@Module, etc.). Your code uses these.anchor-di-core— Runtime container andAnchorobject. Resolves dependencies at runtime.anchor-di-compose—anchorInject(),viewModelAnchor()for Composables.anchor-di-ksp— KSP processor. Generates the wiring code at compile time. Must be added for each Kotlin target you use.
Step 2: Define Dependencies
Define your types and how they're provided. We'll use:
- An interface
UserApiand its implementation - A
UserRepositorythat depends onUserApi - A module that binds
UserApitoUserApiImpl
// commonMain — UserApi.kt
interface UserApi {
suspend fun getUser(id: String): User
}
// commonMain — User.kt (simple data class)
data class User(val id: String, val name: String)
// commonMain — UserApiImpl.kt (platform-specific impl would use expect/actual)
class UserApiImpl @Inject constructor() : UserApi {
override suspend fun getUser(id: String): User = User(id, "User $id")
}
// commonMain — UserRepository.kt
@Singleton
class UserRepository @Inject constructor(
private val api: UserApi
) {
suspend fun loadUser(id: String): User = api.getUser(id)
}
// commonMain — ApiModule.kt
@Module
@InstallIn(SingletonComponent::class)
interface ApiModule {
@Binds
@Singleton
fun bindUserApi(impl: UserApiImpl): UserApi
}
What's going on:
UserRepositoryhas an@Injectconstructor. Anchor DI will create it and pass inUserApi.UserApiis an interface, so we need a binding.ApiModuleuses@Bindsto mapUserApi→UserApiImpl.@InstallIn(SingletonComponent::class)means this module is part of the app-wide (singleton) scope.@SingletononUserRepositoryandbindUserApimeans one instance for the whole app.
Step 3: Initialize at Startup
You must call Anchor.init(*getAnchorContributors()) once at app startup. The function getAnchorContributors() is generated by KSP — it returns an array of contributors that register all your bindings.
Android (in Application.onCreate() or before the first Composable):
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
Anchor.init(*getAnchorContributors())
}
}
iOS (in your app entry point):
// In your @main or main() — call before any UI
Anchor.init(*getAnchorContributors())
Important for iOS: KSP generates code into target-specific directories. You need an actual for getAnchorContributors() in both iosArm64Main and iosSimulatorArm64Main (not iosMain). See Platform-Specific Setup.
Step 4: Inject
Now you can resolve dependencies. Two common patterns:
Imperative (non-Compose)
val repository = Anchor.inject<UserRepository>()
val user = repository.loadUser("1")
Use this in platform code, use cases, or wherever you have direct access to the call site.
Compose (Composables)
@Composable
fun UserScreen(
repository: UserRepository = anchorInject()
) {
var user by remember { mutableStateOf<User?>(null) }
LaunchedEffect(Unit) {
user = repository.loadUser("1")
}
user?.let { Text(it.name) }
}
anchorInject() is a Composable that calls remember { Anchor.inject<T>() }, so the instance is stable across recomposition.
If You Use ViewModels
For ViewModels with their own dependencies (e.g. a ViewModel-scoped repository), use @AnchorViewModel and viewModelAnchor():
@AnchorViewModel
class UserViewModel @Inject constructor(
private val userRepository: UserRepository
) : ViewModel() {
// ...
}
@Composable
fun UserScreen(
viewModel: UserViewModel = viewModelAnchor()
) {
// ViewModel and UserRepository are created in the correct scope
}
Important: Use viewModelAnchor(), not viewModel { Anchor.inject<ViewModel>() }. Only viewModelAnchor() runs resolution inside the ViewModel scope.
Summary
- Add dependencies — api, core, compose (if needed), ksp for each target.
- Define types — Use
@Injectconstructors and@Modulewith@Provides/@Binds. - Initialize — Call
Anchor.init(*getAnchorContributors())once at startup. - Inject — Use
Anchor.inject<T>(),anchorInject(), orviewModelAnchor().
KSP generates the wiring; you write declarative code. For a full installation guide, including multi-module and platform-specific setup, see Installation.