Skip to content
/ injekt Public

Next gen dependency injection library for Kotlin [WIP]

Notifications You must be signed in to change notification settings

IVIanuu/injekt

Repository files navigation

Injekt

Next gen dependency injection library for Kotlin.

@Provide fun jsonParser() = JsonParser()

interface Http

@Provide class HttpImpl : Http

@Provide class Api(private val http: Http, private val jsonParser: JsonParser)

@Provide class Repository(private val api: Api)

@Provide data class AppDependencies(val repository: Repository)

fun main() {
  val dependencies = create<AppDependencies>() // translates to AppDependencies(Repository(Api(HttpImpl(), jsonParser()))
  dependencies.repo
}

Setup

plugins {
  id("io.github.ivianuu.injekt") version latest_version
}

dependencies {
  // core runtime
  implementation("io.github.ivianuu.injekt:core:${latest_version}")
  // optional - common utilities
  implementation("io.github.ivianuu.injekt:common:${latest_version}")
}

Providers

You can provide dependencies by annotating them with @Provide:

// classes and objects
@Provide class MyApi(baseUrl: BaseUrl)

// constructors
class MyService @Provide constructor(logger: Logger) {
  @Provide constructor()
}

// functions
@Provide fun okHttp(authenticator: Authenticator): OkHttpClient = ...

// properties and local variables
@Provide val apiKey: ApiKey = ...

// value parameters
fun run(@Provide config: Config) {
}

Scoping

The core of Injekt doesn't know anything about scoping, but there is a api in the common module. You have to annotate your class or the return type of a function or a property with @Scoped tag.

@Provide @Scoped<UiScope> class MyViewModel

Then you have to provide a Scope instance.

// use a object as name for the scope
object UiScope

Then you can inject your class.

@Provide val uiScope = Scope<UiScope>()

fun onCreate() {
  // use ui scoped dependency
  val db = create<MyViewModel>()
}

Later it should be disposed like so.

fun onDestroy() {
  uiScope.dispose()
}

Multi injection

You can inject all dependencies of a given type by injecting a List<T>

@Provide fun singleElement(): String = "a"
@Provide fun multipleElements(): Collection<String> = listOf("b", "c")

fun main() {
  create<List<String>>() == listOf("a", "b", "c") // true
}

Function injection

Sometimes you want to delay the creation, need multiple instances, want to provide additional parameters, or to break circular dependencies. You can do this by injecting a function.

// inject a function to create multiple Tokens
@Provide class HttpClient(tokenFactory: () -> Token) {
  val tokenA = tokenFactory()
  val tokenB = tokenFactory()
}

// inject a function to create a MyViewModel with the additional String parameter
@Provide class MyActivity(viewModelFactory: (String) -> MyViewModel) {
  val viewModel by lazy { viewModelFactory("user_id") }
}

// break cycles
@Provide class Foo(val bar: Bar)
@Provide class Bar(foo: (Bar) -> Foo) {
   val foo = foo(this)
}

Distinguish between types

Sometimes you have multiple dependencies of the same type Injekt will need help to keep them apart here are two strategies:

Value classes:

@JvmInline value class PlaylistId(val value: String)
@JvmInline value class UserId(val value: String)

@Provide class PlaylistTracksPresenter(playlistId: PlaylistId, userId: UserId)

Tags:

@Tag annotation class PlaylistId
@Tag annotation class UserId

@Provide class PlaylistTracksPresenter(playlistId: @PlaylistId String, userId: @UserId String)

Optionally you can add a typealias for your tag to make it easier to use

@Tag annotation class PlaylistIdTag
typealias PlaylistId = @PlaylistIdTag String

@Tag annotation class UserIdTag
typealias UserId = @UserIdTag String

@Provide class PlaylistTracksPresenter(playlistId: PlaylistId, userId: UserId)

Errors / Debugging

Injekt will show an error if there are missing dependencies. Additionally it will dump generated code in a kotlin like syntax in the /build/injekt/dump folder for each file where injections happen

More complex uses can be found in my essentials project(base project for my apps)

https://github.com/IVIanuu/essentials