Skip to main content

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 and Anchor object. Resolves dependencies at runtime.
  • anchor-di-composeanchorInject(), 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:

  1. An interface UserApi and its implementation
  2. A UserRepository that depends on UserApi
  3. A module that binds UserApi to UserApiImpl
// 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:

  • UserRepository has an @Inject constructor. Anchor DI will create it and pass in UserApi.
  • UserApi is an interface, so we need a binding. ApiModule uses @Binds to map UserApiUserApiImpl.
  • @InstallIn(SingletonComponent::class) means this module is part of the app-wide (singleton) scope.
  • @Singleton on UserRepository and bindUserApi means 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

  1. Add dependencies — api, core, compose (if needed), ksp for each target.
  2. Define types — Use @Inject constructors and @Module with @Provides/@Binds.
  3. Initialize — Call Anchor.init(*getAnchorContributors()) once at startup.
  4. Inject — Use Anchor.inject<T>(), anchorInject(), or viewModelAnchor().

KSP generates the wiring; you write declarative code. For a full installation guide, including multi-module and platform-specific setup, see Installation.