This document covers the all koin projects.

Koin core: DSL, Container & API

Arnaud Giuliani <arnaud.g@insert-koin.io> - and on twitter: @arnogiu :revnumber: 2.0.0-beta-1

Cover the core project features and core sub projects.

1. Introduction

This is the koin-core project manual. This project brings the core feature of Koin.

1.1. What is Koin?

Koin is pragmatic lightweight dependency injection framework for Kotlin developers. Written in pure Kotlin, using functional resolution as key concept. Koin is a DSL, a lightweight container and a pragmatic API.

1.2. Gradle setup

Add the koin-core dependency to your Gradle project:

// Add Jcenter to your repositories if needed
repositories {
    jcenter()
}
dependencies {
    // Koin for Android
    compile 'org.koin:koin-core:2.0.0-beta-1'
}

2. Koin DSL

Thanks to the power of Kotlin language, Koin provides a DSL to help your describe your app instead of annotate it or generate code for it. With its Kotlin DSL, Koin offers a smart functional API to achieve to prepare your dependency injection.

2.1. Application & Module DSL

Koin offers several keywords to let you describe the elements of a Koin Application:

  • Application DSL, to describe the Koin container configuration

  • Module DSL, to describe the components that have to be injected

2.2. Application DSL

A KoinApplication instance is a Koin container instance configuration. This will let your configure logging, properties loading and modules.

To build a new KoinApplication, use the following functions:

  • koinApplication { } - create a KoinApplication container configuration

  • startKoin { } - create a KoinApplication container configuration and register it in the GlobalContext to allow the use of GlobalContext API

To configure your KoinApplication instance, you can use any of the following functions :

  • logger( ) - describe what level and Logger implementation to use (by default use the EmptyLogger)

  • modules( ) - set a list of Koin modules to load in the container (list or vararg list)

  • properties() - load HashMap properties into Koin container

  • fileProperties( ) - load properties from given file into Koin container

  • environmentProperties( ) - load properties from OS environment into Koin container

2.3. KoinApplication instance: Global vs Local

As you can see above, we can describe a Koin container configuration in 2 ways: koinApplication or startKoin function.

  • koinApplication describe a Koin container instance

  • startKoin describe a Koin container instance and register it in Koin GlobalContext

By registering your container configuration into the GlobalContext, the global API can use it directly. Any KoinComponent refers to a Koin instance. By default we use the one from GlovalContext.

Check chapters about Custom Koin instance for more information.

2.4. Starting Koin

Starting Koin means run a KoinApplication instance into the GlobalContext.

To start Koin container with modules, we can just use the startKoin function like that:

// start a KoinApplication in Global context
startKoin {
    // declare used logger
    logger()
    // declare used modules
    modules(coffeeAppModule)
}

2.5. Module DSL

A Koin module gather definitions that you will inject/combine for your application. To create a new module, just use the foolowing function:

  • module { // module content } - create a Koin Module

To describe your content in a module, you can use the following functions:

  • factory { //definition } - provide a factory bean definition

  • single { //definition } - provide a singleton bean definition (also aliased as bean)

  • get() - resolve a component dependency (also can use name, scope or parameters)

  • bind() - add type to bind for given bean definition

  • binds() - add types array for given bean definition

  • scope { // scope group } - define a logical group for scoped definition

  • scoped { //definition }- provide a bean definition that will exists only in a scope

2.6. Writing a module

A Koin module is the space to declare all your components. Use the module function to declare a Koin module:

val myModule = module {
   // your dependencies here
}

In this module, you can declare components as decribed below.

3. Definitions

By using Koin, you describe definitions in modules. In this section we will see how to declare, organize & link your modules.

3.1. Writing a module

A Koin module is the space to declare all your components. Use the module function to declare a Koin module:

val myModule = module {
   // your dependencies here
}

In this module, you can declare components as decribed below.

3.2. Defining a singleton

Declaring a singleton component means that Koin container will keep a unique instance of your declared component. Use the single function in a module to declare a singleton:

class MyService()

val myModule = module {

    // declare single instance for MyService class
    single { MyService() }
}

3.3. Defining your component within a lambda

single, factory & scoped keywords help you declare your components through a lambda expression. this lambda describe the way that you build your component. Usually we instantiate components via their constructors, but you can also use any expression.

single { Class constructor // Kotlin expression }

The result type of your lambda is the main type of your component

3.4. Defining a factory

A factory component declaration is a definition that will gives you a new instance each time you ask for this definition (this instance is not retrained by Koin container, as it won’t inject this instance in other definitions later). Use the factory function with a lambda expression to build a component.

class Controller()

val myModule = module {

    // declare factory instance for Controller class
    factory { Controller() }
}

Koin container doesn’t retain factory instances as it will give a new instance each time the definition is asked.

3.5. Resolving & injecting dependencies

Now that we can declare components definitions, we want to link instances with dependency injection. To resolve an instance in a Koin module, just use the get() function to the requested needed component instance. This get() function is usually used into constructor, to inject constructor values.

To make dependency injection with Koin container, we have to write it in constructor injection style: resolve dependencies in class constructors. This way, your instance will be created with injected instances from Koin.

Let’s take an example with several classes:

// Presenter <- Service
class Service()
class Controller(val view : View)

val myModule = module {

    // declare Service as single instance
    single { Service() }
    // declare Controller as single instance, resolving View instance with get()
    single { Controller(get()) }
}

3.6. Definition: binding an interface

A single or a factory definition use the type from the their given lambda definition: i.e single { T } The matched type of the definition is the only matched type from this expression.

Let’s take an example with a class and implemented interface:

// Service interface
interface Service{

    fun doSomething()
}

// Service Implementation
class ServiceImp() : Service {

    fun doSomething() { ... }
}

In a Koin module we can use the as cast Kotlin operator as follow:

val myModule = module {

    // Will match type ServiceImp only
    single { ServiceImp() }

    // Will match type Service only
    single { ServiceImp() as Service }

}

You can also use the inferred type expression:

val myModule = module {

    // Will match type ServiceImp only
    single { ServiceImp() }

    // Will match type Service only
    single<Service> { ServiceImp() }

}

This 2nd way of style declaration is preferred and will be used for the rest of the documentation.

3.7. Definition: binding an additional type

In some cases, we want to match several types from just one definition.

Let’s take an example with a class and interface:

// Service interface
interface Service{

    fun doSomething()
}

// Service Implementation
class ServiceImp() : Service{

    fun doSomething() { ... }
}

To make a definition bind additional types, we use the bind operator with a class:

val myModule = module {

    // Will match types ServiceImp & Service
    single { ServiceImp() } bind Service::class
}

3.8. Definition: naming & default bindings

You can specify a name to your definition, to help you distinguish two definitions about the same type:

Just request your definition with its name:

val myModule = module {
    single<Service>("default") { ServiceImpl() }
    single<Service>("test") { ServiceImpl() }
}

val service : Service by inject(name = "default")

get() and by inject() functions let you specify a definition name if needed.

By default Koin will bind a definition by its type or by its name, if the type is already bound to a definition.

val myModule = module {
    single<Service> { ServiceImpl1() }
    single<Service>("test") { ServiceImpl2() }
}

Then:

  • val service : Service by inject() will trigger the ServiceImpl1 definition

  • val service : Service by inject(name = "test") will trigger the ServiceImpl2 definition

3.9. Declaring injection parameters

In any single, factory or scope definition, you can use injection parameters: parameters that will be injected and used by your definition:

class Presenter(val view : View)

val myModule = module {
    single{ (view : View) -> Presenter(view) }
}

In contrary to resolved dependencies (resolved with with get()), injection parameters are parameters passed through the resolution API. This means that those parameters are values passed with get() and by inject(), with the parametersOf function:

val presenter : Presenter by inject { parametersOf(view) }

Further reading in the injection parameters section.

3.10. Using definition flags

Koin DSL also proposes some flags.

3.10.1. Create instances at start

A definition or a module can be flagged as createOnStart, to be created at start (or when you want). First set the createOnStart flag on your module or on your definition.

CreateAtStart flag on a definition
val myModuleA = module {

    single<Service> { ServiceImp() }
}

val myModuleB = module {

    // eager creation for this definition
    single<Service>(createAtStart=true) { TestServiceImp() }
}
CreateAtStart flag on a module
val myModuleA = module {

    single<Service> { ServiceImp() }
}

val myModuleB = module(createAtStart=true) {

    single<Service>{ TestServiceImp() }
}

The startKoin function will automatically create definitions instances flagged with createAtStart.

// Start Koin modules
startKoin {
    modules(myModuleA,myModuleB)
}

if you need to load some definition at a special time (in a background thread instead of UI for example), just get/inject the desired components.

3.10.2. Dealing with generics

Koin definitions doesn’t take in accounts generics type argument. For example, the module below tries to define 2 definitions of List:

module {
    single { ArrayList<Int>() }
    single { ArrayList<String>() }
}

Koin won’t start with such definitions, understanding that you want to override one definition for the other.

To allow you, use the 2 definitions you will have to differentiate them via their name, or location (module). For example:

module {
    single(name="Ints") { ArrayList<Int>() }
    single(name="Strings") { ArrayList<String>() }
}

4. Modules

By using Koin, you describe definitions in modules. In this section we will see how to declare, organize & link your modules.

4.1. What is a module?

A Koin module is a "space" to gather Koin definition. It’s declared with the module function.

val myModule = module {
    // Your definitions ...
}

4.2. Using several modules

Components doesn’t have to be necessarily in the same module. A module is a logical space to help you organize your definitions, and can depend on definitions from other module. Definitions are lazy, and then are resolved only when a a component is requesting it.

Let’s take an example, with linked components in separate modules:

// ComponentB <- ComponentA
class ComponentA()
class ComponentB(val componentA : ComponentA)

val moduleA = module {
    // Singleton ComponentA
    single { ComponentA() }
}

val moduleB = module {
    // Singleton ComponentB with linked instance ComponentA
    single { ComponentB(get()) }
}

Koin does’t have any import concept. Koin definitions are lazy: a Koin definition is started with Koin container but is not instantiated. An instance is created only a request for its type has been done.

We just have to declare list of used modules when we start our Koin container:

// Start Koin with moduleA & moduleB
startKoin{
    modules(moduleA,moduleB)
}

Koin will then resolve dependencies from all given modules.

4.3. Linking modules strategies

As definitions between modules are lazy, we can use modules to implement different strategy implementation: declare an implementation per module.

Let’s take an example, of a Repository and Datasource. A repository need a Datasource, and a Datasource can be implemented in 2 ways: Local or Remote.

class Repository(val datasource : Datasource)
interface Datasource
class LocalDatasource() : Datasource
class RemoteDatasource() : Datasource

We can declare those components in 3 modules: Repository and one per Datasource implementation:

val repositoryModule = module {
    single { Repository(get()) }
}

val localDatasourceModule = module {
    single<Datasource> { LocalDatasource() }
}

val remoteDatasourceModule = module {
    single<Datasource> { RemoteDatasource() }
}

Then we just need to launch Koin with the right combination of modules:

// Load Repository + Local Datasource definitions
startKoin {
    modules(repositoryModule,localDatasourceModule)
}

// Load Repository + Remote Datasource definitions
startKoin {
    modules(repositoryModule,remoteDatasourceModule)
}

4.4. Overriding a definition or a module

Koin won’t allow you to redefinition an already existing definition (type,name,path …​). You will an an error if you try this:

Override not allowed
val myModuleA = module {

    single<Service> { ServiceImp() }
}

val myModuleB = module {

    single<Service> { TestServiceImp() }
}

// Will throw an BeanOverrideException
startKoin {
    modules(myModuleA,myModuleB)
}

To allow definition overriding, you have to use the override parameter:

Override at definition level
val myModuleA = module {

    single<Service> { ServiceImp() }
}

val myModuleB = module {

    // override for this definition
    single<Service>(override=true) { TestServiceImp() }
}
Override at module level
val myModuleA = module {

    single<Service> { ServiceImp() }
}

// Allow override for all definitions from module
val myModuleB = module(override=true) {

    single<Service> { TestServiceImp() }
}

Order matters when listing modules and overriding definitions. You must have your overriding definitions in last of your module list.

5. Koin Components

Koin is a DSL to help describe your modules & definitions, a container to make definition resolution. What we need now is an API to retrieve our instances outside of the container. That’s the goal of Koin components.

5.1. Create a Koin Component

To give a class the capacity to use Koin features, we need to tag it with KoinComponent interface. Let’s take an example.

A module to define MyService instance
class MyService

val myModule = module {
    // Define a singleton for MyService
    single { MyService() }
}

we start Koin before using definition.

Start Koin with myModule
fun main(vararg args : String){
    // Start Koin
    startKoin {
        modules(myModule)
    }

    // Create MyComponent instance and inject from Koin container
    MyComponent()
}

Here is how we can write our MyComponent to retrieve instances from Koin container.

Use get() & by inject() to inject MyService instance
class MyComponent : KoinComponent {

    // lazy inject Koin instance
    val myService : MyService by inject()

    // or
    // eager inject Koin instance
    val myService : MyService get()
}

5.2. Unlock the Koin API with KoinComponents

Once you have tagged your class as KoinComponent, you gain access to:

  • by inject() - lazy evaluated instance from Koin container

  • get() - eager fetch instance from Koin container

  • release() - release module’s instances from its path

  • getProperty()/setProperty() - get/set property

5.3. Retrieving definitions with get & inject

Koin offers two ways of retrieving instances from the Koin container:

  • val t : T by inject() - lazy evaluated delegated instance

  • val t : T = get() - eager access for instance

// is lazy evaluated
val myService : MyService by inject()

// retrieve directly the instance
val myService : MyService get()

The lazy inject form is better to define property that need lazy evaluation.

5.4. Resolving instance from its name

If you need you can specify the following parameter with get() or by inject()

  • name - name of the definition (when specified name parameter in your definition)

Example of module using definitions names:

val module = module {
    single(name="A") { ComponentA() }
    single(name="B") { ComponentB(get()) }
}

class ComponentA
class ComponentB(val componentA: ComponentA)

We can make the following resolutions:

// retrieve from given module
val a = get<ComponentA>(name = "A")

5.5. No inject() or get() in your API?

If your are using an API and want to use Koin inside it, just tag the desired class with KoinComponent interface.

6. Start the container

Koin is a DSL, a lightweight container and a pragmatic API. Once you have declared your definitions within Koin modules, your are ready to start the Koin container.

6.1. The startKoin function

The startKoin function is the main entry point to launch Koin container. It need a list of Koin modules to run. Modules are loaded and definitions are ready to be resolved by the Koin container.

Starting Koin
// start a KoinApplication in Global context
startKoin {
    // declare used modules
    modules(coffeeAppModule)
}

Once startKoin has been called, Koin will read all your modules & definitions. Koin is then ready for any get() or by inject() call to retrieve the needed instance.

Your Koin container can have several options:

  • logger - to enable logging - see logging section

  • properties(), fileProperties( ) or environmentProperties( ) to load properties from environment, koin.properties file, extra properties …​ - see properties section

The startKoin can’t be called more than once. If you need several point to load modules, use the loadKoinModules function.

6.2. Behind the start - KoinApplication & Koin instances

When we start Koin, we create a KoinApplication instance that represents the Koin container configuration instance. Once launched, it will produce a Koin instance resulting of your modules and options. This Koin instance is then registered in the GlobalContext, to be used by any KoinComponent class.

6.3. Loading modules without startKoin function

You can’t call the startKoin function more than once. But you can use directly the loadKoinModules() functions.

This function is interesting for SDK makers who want to use Koin, because they don’t need to use the starKoin() function and just use the loadKoinModules at the start of their library.

Load Koin modules
loadKoinModules(module1,module2 ...)

6.4. Koin context isolation

For SDK Makers, you can also work with Koin in a non global way: use Koin for the DI of your library and avoid any conflict by people using your library and Koin by isolating your context.

In a standard way, we can start Koin like that:

Global Koin context
// start a KoinApplication and register it in Global context
startKoin {
    // declare used modules
    modules(coffeeAppModule)
}

From this, we can use the KoinComponent as it: it will use the GlobalContext Koin instance.

But if we want to use an isolated Koin instance, you can just declare it like follow:

Local Koin context
// create a KoinApplication
val myApp = koinApplication {
    // declare used modules
    modules(coffeeAppModule)
}

You will have to keep your myApp instance avilable in your library and pass it to your custom KoinComponent implementation:

// Get a Context for your Koin instance
object MyKoinContext {
    var koinApp : KoinApplication? = null
}

// Register the Koin context
MyKoinContext.koinApp = KoinApp
abstract class CustomKoinComponent : KoinComponent {
    // Override default Koin instance, intially target on GlobalContext to yours
    override fun getKoin(): Koin = MyKoinContext?.koinApp.koin
}

And now, you register your context and run your own isolated Koin components:

// Register the Koin context
MyKoinContext.koinApp = myApp

class ACustomKoinComponent : CustomKoinComponent(){
    // inject & get will target MyKoinContext
}

6.5. Stop Koin - closing all resources

You can close all the Koin resources and drop instances & definitions. For this you can use the stopKoin() function from anywhere, to stop the Koin GlobalContext. Else on a KoinApplication instance, just call close()

7. Injection parameters

In any definition, you can use injection parameters: parameters that will be injected and used by your definition:

7.1. Defining an injection parameter

Below is an example of injection parameters. We established that we need a view parameter to build of Presenter class:

class Presenter(val view : View)

val myModule = module {
    single{ (view : View) -> Presenter(view) }
}

7.2. Injecting with values

In contrary to resolved dependencies (resolved with with get()), injection parameters are parameters passed through the resolution API. This means that those parameters are values passed with get() and by inject(), with the parametersOf() function:

class MyComponent : View, KoinComponent {

    // inject this as View value
    val presenter : Presenter by inject { parametersOf(this) }
}

7.3. Multiple parameters

If we want to have multiple parameters in our definition, we can use the destructured declaration to list our parameters:

class Presenter(val view : View, id : String)

val myModule = module {
    single{ (view : View, id : String) -> Presenter(view,id) }
}

In a KoinComponent, just use the parametersOf function with your arguments like below:

class MyComponent : View, KoinComponent {

    val id : String ...

    // inject with view & id
    val presenter : Presenter by inject { parametersOf(this,id) }
}

8. Using Scopes

Koin brings a simple API to let you define instances that are tied to a limit lifetime.

8.1. What is a scope?

Scope is a fixed duration of time or method calls in which an object exists. Another way to look at this is to think of scope as the amount of time an object’s state persists. When the scope context ends, any objects bound under that scope cannot be injected again (they are dropped from the container).

8.2. Scope definition

By default in Koin, we have 3 kind of scopes:

  • single definition, create an object that persistent with the entire container lifetime (can’t be dropped).

  • factory definition, create a new object each time. Short live. No persistence in the container (can’t be shared).

  • scoped definition, create an object that persistent tied to the associated scope lifetime.

To declare a scoped definition, use the scoped function like follow:

module {
    scoped { Presenter() }
}

A scope gathers scoped definitions as a logical unit of time:

module {
    scope("A Scope Name"){
        scoped { Presenter() }
        // ...
    }
}

8.3. Working with a scope

A scope instance can be created with as follow: val scope = koin.createScope("myScope"). The "myScope" string here, is the id of your scope instance.

To resolve a dependency using the scope we can do it like:

  • val presenter = scope.get<Presenter>() - directly using the get/inject functions from the scope instance

  • val presenter = get<Presenter>(scope = scope) - by passing yoru scope instance to any get/inject function

A scope definition can’t be resolved if associated scope has not been created. Then get<Presenter>() would return an error.

As you may have seen, you can declare scoped definitions with or without a scope:

module {
    // open scoped definition
    scoped { Presenter() }
}

//or

module {
    // define a scope "A_SCOPE_NAME"
    scope("A_SCOPE_NAME"){
        // scoped definition tied to "A_SCOPE_NAME" scope
        // also known as closed scoped definition
        scoped { Presenter() }
    }
}

In the case where a scoped definition is not tied to a scope, you can resolve from a simple scope instance:

// create scope "myScope"
val scope = koin.createScope("myScope")
// resolve presenter instance
val presenter = scope.get<Presenter>()

in the case of scoped definition tied to a scope, we have to declare a the scope instance like follow:

// create scope instance "myScope" for scope "A_SCOPE_NAME"
val scope = koin.createScope("myScope","A_SCOPE_NAME")
// resolve presenter instance
val presenter = scope.get<Presenter>()

You can mix open or tied to scope scoped definitions instances. A scope instance is dedicated to resolve scoped dependencies from a defined scope group. Any open scoped definition can be resolved from any scope.

8.4. Create & retrieve a scope

From a KoinComponent class or where you can access your Koin instance:

  • createScope(id : String) - create an open scope instance with given id

  • createScope(id : String, scopeName : String) - create a closed scope instance with given id and scopeName

  • getScope(id : String) - retrieve a previously created scope with given id

  • getOrCreateScope(id : String) - create or retrieve if already created, the open scope instance with given id

  • getOrCreateScope(id : String, scopeName : String) - create or retrieve if already created, the closed scope instance with given id and scopeName

Make the difference between a scope instance id, which is the id to find your scope over all your scopes, and the scope name, which is the reference to the tied scope group name.

8.5. Creating scope instances

Using the id, it’s then possible to have several instances of the same scope:

// create an closed scope instance "myScope1" for scope "A_SCOPE_NAME"
val myScope1 = koin.createScope("myScope1","A_SCOPE_NAME")
// create an closed scope instance "myScope2" for scope "A_SCOPE_NAME"
val myScope2 = koin.createScope("myScope2","A_SCOPE_NAME")

8.6. Resolving dependencies within a scope

The intestest of a scope is to define a common logical unit of time for scoped definitions. It’s allow also to resolve definitions from within the given scope

// given the classes
class ComponentA
class ComponentB(val a : ComponentA)

// module with scope
module {

    scope("A_SCOPE_NAME"){
        scoped { ComponentA() }
        // will resolve from current scope instance
        scoped { ComponentB(get()) }
    }
}

The depedendecy resolution is then straight forward:

// create an closed scope instance "myScope1" for scope "A_SCOPE_NAME"
val myScope1 = koin.createScope("myScope1","A_SCOPE_NAME")

// from the same scope
val componentA = myScope1.get<ComponentA>()
val componentB = myScope1.get<ComponentB>()

8.7. Closing a scope

Once your scope instance is finished, just closed it with the close() function:

// from a KoinComponent
val session = getKoin().createScope("session")

// use it ...

// close it
session.close()

Beware that you can’t inject instances anymore from a closed scope.

8.8. Scope callback — TODO

9. Logging Koin activity

Koin has a simple logging API to log any Koin activity (allocation, lookup …​). The logging API is represented by the class below:

Koin Logger
abstract class Logger(var level: Level = Level.INFO) {

    abstract fun log(level: Level, msg: MESSAGE)

    fun debug(msg: MESSAGE) {
        log(Level.DEBUG, msg)
    }

    fun info(msg: MESSAGE) {
        log(Level.INFO, msg)
    }

    fun error(msg: MESSAGE) {
        log(Level.ERROR, msg)
    }
}

Koin proposes some implementation of logging, in function of the target platform:

  • PrintLogger - directly log into console (included in koin-core)

  • EmptyLogger - log nothing (included in koin-core)

  • SLF4JLogger - Log with SLF4J. Used by ktor and spark (koin-logger-slf4j project)

  • AndroidLogger - log into Android Logger (included in koin-android)

9.1. Set logging at start

By default, By default Koin use the EmptyLogger. You can use diectly the PrintLogger as following:

startKoin{
    logger(LEVEL.INFO)
}

10. Properties

Koin can handle properties from environment or external property file, to help you inject values into your definitions.

10.1. Loading properties at start

You can load several type of properties at start:

  • environment properties - load system properties

  • koin.properties file - load properties from /src/main/resources/koin.properties file

  • "extra" start properties - map of values passed at startKoin function

10.2. Read property from a module

In a Koin module, you can get a property by its key:

in /src/main/resoucres/koin.properties file
// Key - value
server_url=http://service_url

Just load it with getProperty function:

val myModule = module {

    // use the "server_url" key to retrieve its value
    single { MyService(getProperty("server_url")) }
}

11. Extending Koin

Koin is an agnostic dependency injection framework, offering a DSL, a lightweight container and a simple API. To suit your favorite SDK/runtime technology, just extend it with some Kotlin extensions and bring Koin features.

11.1. Extending Koin container configuration DSL

11.2. Extending Koin module DSL

11.3. Bring Koin to your SDK

12. Examples

Here are some koin-core example projects.

12.1. The Coffee Maker

You can find the project in 'examples/coffee-maker' folder.

12.1.1. The App Components

The coffee-maker is a pure Kotlin app for demoing dependency injection with Koin. This app is composed of:

  • CoffeeApp - retrieve and run the CoffeeMaker class

class CoffeeApp : KoinComponent, Runnable {

    val coffeeMaker: CoffeeMaker by inject()

    override fun run() {
        coffeeMaker.brew()
    }
}
  • CoffeeMaker - is composed of a Pump and a lazy injected Heater. Make the bre processing.

class CoffeeMaker(val pump: Pump, val lazyHeater: Lazy<Heater>) {

    // Don't want to create a possibly costly heater until we need it.
    val heater: Heater by lazy { lazyHeater.value }

    fun brew() {
        heater.on()
        pump.pump()
        println(" [_]P coffee! [_]P ")
        heater.off()
    }
}
  • ElectricHeater - is a implementation of Heater

interface Heater {
    fun on()
    fun off()
    fun isHot() : Boolean
}

class ElectricHeater : Heater {

    var heating: Boolean = false

    override fun on() {
        println("~ ~ ~ heating ~ ~ ~")
        heating = true
    }

    override fun off() {
        heating = false
    }

    override fun isHot(): Boolean = heating
}
  • Thermosiphon - is a implementation of Pump using a Heater

interface Pump {
    fun pump()
}

class Thermosiphon(val heater: Heater) : Pump{
    override fun pump() {
        if (heater.isHot()){
            println("=> => pumping => =>")
        }
    }
}

12.1.2. Assembling and running with Koin

We need to declare and assemble components:

  • CoffeeMakerPump & Heater (lazy)

  • Thermosiphon as Pump → Heater

  • ElectricHeater as Heater

Here is how we assemble it with Koin:

val coffeeMakerModule = module {
    single { CoffeeMaker(get(), lazy { get<Heater>() }) }
    single<Pump> { Thermosiphon(get()) }
    single<Heater> { ElectricHeater() }
}

CoffeeMaker is a KoinComponent to lazy inject Heater.

CoffeeApp is a KoinComponent to lazy inject CoffeeMaker. This class is also outside of the Koin container.

Just need to start Koin with coffeeMakerModule module and run the CoffeeApp class.

fun main(vararg args: String) {

    startKoin {
        modules(coffeeMakerModule)
    }
    CoffeeApp().run()
}

Testing with Koin

Arnaud Giuliani <arnaud.g@insert-koin.io> - and on twitter: @arnogiu :revnumber: 2.0.0-beta-1 :example-caption!:

Covers Koin testing features.

13. About koin-test

The koin-test project is dedicated to help you making JUnit test with Koin container and API.

13.1. Gradle setup

Add the koin-android dependency to your Gradle project:

// Add Jcenter to your repositories if needed
repositories {
    jcenter()
}
dependencies {
    // Testing with Koin
    testCompile 'org.koin:koin-test:2.0.0-beta-1'
}

14. Making your test a KoinComponent with KoinTest

By tagging your class KoinTest, your class become a KoinComponent and bring you:

  • by inject() & get() - function to retrieve yoru instances from Koin

  • check & dryRun - help you check your configuration

  • declareMock & declare - to declare a mock or a new definition in the current context

class ComponentA
class ComponentB(val a: ComponentA)

class MyTest : KoinTest {

    // Lazy inject property
    val componentB : ComponentB by inject()

    @Test
    fun `should inject my components`() {
        startKoin(listOf(module {
            single { ComponentA() }
            single { ComponentB(get()) }
        }))

        // directly request an instance
        val componentA = get<ComponentA>()

        assertNotNull(a)
        assertEquals(componentA, componentB.a)
    }

Don’t hesitate to overload Koin modules configuration to help you partly build your app.

15. Mocking out of the box

Instead of making a new module each time you need a mock, you can declare a mock on the fly with declareMock:

class MyTest : KoinTest {

    class ComponentA
    class ComponentB(val a: ComponentA)

    @Test
    fun `should inject my components`() {
        startKoin(listOf(module {
            single { ComponentA() }
            single { ComponentB(get()) }
        }))
        declareMock<ComponentA>()

        // retrieve mock
        assertNotNull(get<ComponentA>())

        // is built with mocked ComponentA
        assertNotNull(get<ComponentB>())
    }

declareMock can specify if you want a single or factory, and if you wan to have it in a module path.

16. Declaring a component on the fly

When a mock is not enough and don’t want to create a module just for this, you can use declare:

    @Test
    fun `successful declare an expression mock`() {
        startKoin(listOf())

        declare { factory { ComponentA("Test Params") } }

        Assert.assertNotEquals(get<ComponentA>(), get<ComponentA>())
    }

17. Checking your Koin configuration

Koin offers a way to test if you Koin modules are good: checkModules - walk through your definition tree and check if each definition is bound

    val MVPModule = module {
        single { Repository(get()) }

        module("view") {
            single { View() }
            single { Presenter(get()) }
        }
    }

    val DataSourceModule = module {
        single { DebugDatasource() } bind Datasource::class
    }

    @Test
    fun `check MVP hierarchy`() {
        // Check the Koin modules binding
        checkModules(listOf(MVPModule, DataSourceModule))
    }

Koin Core Ext

Arnaud Giuliani <arnaud.g@insert-koin.io> - and on twitter: @arnogiu :revnumber: 2.0.0-beta-1

Cover the extended features for koin-core project.

18. Extensions & Experimental features

The koin-core-ext brings extensions & experimental features to Koin.

18.1. Better definition declaration (Experimental)

Koin DSL can be seen as "manual", while you must fill constructors with "get()" function to resolve needed instances. When your definition don’t need any special constructor integration (injection paarameters or special scope Id), we can go with more compact writing style thanks to API below.

Using reflection is not costless. it replaces what you don"t want to write with reflection code (finding primary constructors, injecting parameters…​). Mind it before using it, if you are on performances constraints platform (Android for example)

18.2. Build any instance with create()

The first introduced function is the create() function.

Instead of declaring a definition with instantiating its constructor and retrieving instances with get()

module {
    single { ComponentA(get() ...) }
}

You can use instead, the create() function to build an instance from its primary constructor, and fill the needed dependencies.

module {
    single { create<ComponentA>() }
}

18.3. Even more simple definitions

You can also use the more "compact" notation that will use the create() function. Just use the single function without any expression:

module {
    single<ComponentA>()
}

If you have an implementation type and want to resolve with a target type, you can use the following singleBy function:

module {
    singleBy<Target,Implementation>()
}

Works for single, factory & scope

If you use custom constructors expression like injection parameters or others, don’t use the reflection API.

Features for Java developers

Arnaud Giuliani <arnaud.g@insert-koin.io> - and on twitter: @arnogiu :revnumber: 2.0.0-beta-1 :example-caption!:

Covers Java Koin features.

19. About koin-java

The koin-java project is dedicated to bring enhancement features to help java developers.

19.1. Gradle setup

Add the koin-java dependency to your Gradle project:

// Add Jcenter to your repositories if needed
repositories {
    jcenter()
}
dependencies {
    // Koin Java features
    implementation 'org.koin:koin-java:2.0.0-beta-1'
}

20. Starting a Koin module from Java

A Koin module still must be written in Kotlin, using the @JvmField to make it Java friendly:

@JvmField
val koinModule = module {

    single { ComponentA() }
}

Now just call the KoinJavaStarter.startKoin function from a Java class to start your module:

KoinJavaStarter.startKoin(singletonList(koinModule));

21. Injecting into a Java class

The koin-java project provides a static Java helper: KoinJavaComponent. This will help you make:

  • direct instance injection with get(Class)

ComponentA a = get(ComponentA.class);
  • lazy instance injection with inject(Class)

Lazy<ComponentA> lazy_a = inject(ComponentA.class);

Make sure to have the static import of class KoinJavaComponent to have a short syntax.

Koin for Android developers

Arnaud Giuliani <arnaud.g@insert-koin.io> - and on twitter: @arnogiu :revnumber: 2.0.0-beta-1 :example-caption!:

Covers Koin features for Android.

22. About koin-android

The koin-android project is dedicated to provide Koin powers to Android world.

For general Koin concepts, check the koin-core manual.

22.1. Gradle setup

Add the koin-android dependency to your Gradle project:

// Add Jcenter to your repositories if needed
repositories {
    jcenter()
}
dependencies {
    // Koin for Android
    implementation 'org.koin:koin-android:2.0.0-beta-1'
}

23. Start Koin with Android

The koin-android project is dedicated to provide Koin powers to Android world.

23.1. startKoin() from your Application

From your Application class you can use the startKoin function and inject the Android context with androidContext as follow:

class MainApplication : Application() {

    override fun onCreate() {
        super.onCreate()

        startKoin {
            //inject Android context
            androidContext(this@MainApplication)
            // use modules
            modules(myAppModules)
        }

    }
}

23.2. Starting Koin with Android context from elsewhere?

If you need to start Koin from another Android class, you can use the startKoin function and provide your Android Context instance with just like:

startKoin {
    //inject Android context
    androidContext(/* your androic context */)
    // use modules
    modules(myAppModules)
}

23.3. Koin Logging

Within your KoinApplication instance, we have an extension androidLogger which use the AndroidLogger()= This logger is an Android implementation of the Koin logger.

Up to you to change this logger if it doesn’t suits to your needs.

Shut off Koin Logger
class MainApplication : Application() {

    override fun onCreate() {
        super.onCreate()

        startKoin {
            //inject Android context
            androidContext(/* your androic context */)
            // use Android logger - Level.INFO by default
            androidLogger()
            // use modules
            modules(myAppModules)
        }
    }
}

23.4. Properties

You can use Koin properties in the assets/koin.properties file, to store keys/values:

Use Koin extra properties
// Shut off Koin Logger
class MainApplication : Application() {

    override fun onCreate() {
        super.onCreate()

        startKoin {
            //inject Android context
            androidContext(/* your androic context */)
            // use Android logger - Level.INFO by default
            androidLogger()
            // use properties from assets/koin.properties
            androidFileProperties()
            // use modules
            modules(myAppModules)
        }
    }
}

24. Retrieve your components from Koin

Once you have declared some modules and you have started Koin, how can you retrieve your instances in your Android Activity Fragments or Services?

24.1. Activity, Fragment & Service as KoinComponents

Activity, Fragment & Service are extended with the KoinComponents extension. You gain access to:

  • by inject() - lazy evaluated instance from Koin container

  • get() - eager fetch instance from Koin container

  • release() - release module’s instances from its path

  • getProperty()/setProperty() - get/set property

For a module that declares a 'presenter' component:

val androidModule = module {
    // a factory of Presenter
    factory { Presenter() }
}

We can declare a property as lazy injected:

Lazy inject a property
class DetailActivity : AppCompatActivity() {

    // Lazy injected Presenter instance
    override val presenter : Presenter by inject()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ...
    }

Or we can just directly get an instance:

Get directly an instance
class DetailActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Retrieve a Presenter instance
        val presenter : Presenter = get()
    }

24.2. Need inject() and get() anywhere else?

If you need to inject() or get() an instance from another class, just tag it with KoinComponent interface.

25. Koin DSL Extension for Android

Below, the added keywords for Koin DSL.

25.1. Getting Android context inside a Module

The androidContext() & androidApplication()` functions allows you to get the Context instance in a Koin module, to help you simply write expression that requires the Application instance.

val appModule = module {

    // create a Presenter instance with injection of R.string.mystring resources from Android
    factory {
        MyPresenter(androidContext().resources.getString(R.string.mystring))
    }
}

26. Scope features for Android

The koin-android-scope project is dedicated to bring Android scope features to the existing Scope API.

26.1. Gradle setup

Choose the koin-android-scope dependency to add to your Gradle project (android or androix version):

// Add Jcenter to your repositories if needed
repositories {
    jcenter()
}
dependencies {
    // Scope for Android
    implementation 'org.koin:koin-android-scope:2.0.0-beta-1'
    // or Scope for AndroidX
    implementation 'org.koin:koin-androidx-scope:2.0.0-beta-1'
}

26.2. Taming the Android lifecycle

Android components are mainly managed by their lifecycle: we can’t directly instantiate an Activity nor a Fragment. The system make all creation and management for us, and make callbacks on methods: onCreate, onStart…​

That’s why we can’t describe our Activity/Fragment/Service in a Koin module. We need then to inject dependencies into properties and also respect the lifecycle: Components related to the UI parts must be released on soon as we don’t need them anymore.

Then we have:

  • long live components (Services, Data Repository …​) - used by several screens, never dropped

  • medium live components (user sessions …​) - used by several screens, must be dropped after an amount of time

  • short live components (views) - used by only one screen & must be dropped at the end of the screen

Long live components can be easily described as single definitions. For medium and short live components we can have several approaches.

In the case of MVP architecture style, the Presenter is a short live component to help/support the UI. The presenter must be created each time the screen is showing, and dropped once the screen is gone.

A new Presenter is created each time
class DetailActivity : AppCompatActivity() {

    // injected Presenter
    override val presenter : Presenter by inject()

We can describe it in a module:

  • as factory - to produce a new instance each time the by inject() or get() is called

val androidModule = module {

    // Factory instance of Presenter
    factory { Presenter() }
}
  • as scope - to produce an instance tied to a scope

val androidModule = module {

    scope("scope_id") { Presenter() }
}

Most of Android memory leaks comes from referencing a UI/Android component from a non Android component. The system keeps a reference on it and can’t totally drop it via garbage collection.

26.3. Binding scope to lifecycle

Koin gives the bindScope function to bind the actual Android component lifecycle, to a given scope. On lifecycle’s end, this will close the bound scope.

class MyActivity : AppCompatActivity() {

    // inject Presenter instance, tied to current "scope_id" scope
    val presenter : Presenter by inject()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // bind current lifecycle to Activity's scope
        bindScope(getScope("scope_id"))
    }

26.4. Sharing instances between components with scopes

In a more extended usage, you can use a Scope instance across components. For example, if we need to share a UserSession instance.

First declare a scope definition:

module {
    // Shared user session data
    scope("session") { UserSession() }
}

When needed to begin use a UserSession instance, create a scope for it:

getKoin().createScope("session")

Then use it anywhere you need it:

class MyActivity1 : AppCompatActivity() {

    // inject Presenter instance, tied to current MyActivity's scope
    val userSession : UserSession by inject()
}
class MyActivity2 : AppCompatActivity() {

    // inject Presenter instance, tied to current MyActivity's scope
    val userSession : UserSession by inject()
}

or you can also inject it with Koin DSL. If a presenter need it:

class Presenter(val userSession : UserSession)

Just inject it into constructor, with the right scope:

module {
    // Shared user session data
    scope("session") { UserSession() }
    // Inject UserSession instance from "session" Scope
    factory { Presenter(get())}
}

When you have to finish with your scope, just close it:

val session = getKoin().getScope("session")
session.close()

27. Architecture Components with Koin: ViewModel

The koin-android-viewmodel project is dedicated to bring Android Architecture ViewModel features.

27.1. Gradle setup

Choose the koin-android-viewmodel dependency to add to your Gradle project (android or androix version):

// Add Jcenter to your repositories if needed
repositories {
    jcenter()
}
dependencies {
    // ViewModel for Android
    implementation 'org.koin:koin-android-viewmodel:2.0.0-beta-1'
    // or ViewModel for AndroidX
    implementation 'org.koin:koin-androidx-viewmodel:2.0.0-beta-1'
}

27.2. ViewModel DSL

The koin-android-viewmodel introduces a new viewModel DSL keyword that comes in complement of single and factory, to help declare a ViewModel component and bind it to an Android Component lifecycle.

val appModule = module {

    // ViewModel for Detail View
    viewModel { DetailViewModel(get(), get()) }
    // or
    viewModel<DetailViewModel>()

}

Your declared component must at least extends the android.arch.lifecycle.ViewModel class. You can specify how you inject the constructor of the class and use the get() function to inject dependencies.

The viewModel keyword helps declaring a factory instance of ViewModel. This instance will be handled by internal ViewModelFactory and reattach ViewModel instance if needed.

The viewModel keyword can also let you use the injection parameters.

27.3. Injecting your ViewModel

To inject a ViewModel in an Activity, Fragment or Service use:

  • by viewModel() - lazy delegate property to inject a ViewModel into a property

  • getViewModel() - directly get the ViewModel instance

class DetailActivity : AppCompatActivity() {

    // Lazy inject ViewModel
    val viewModel: DetailViewModel by viewModel()
}

27.4. Shared ViewModel

One ViewModel instance can be shared between Fragments and their host Activity.

To inject a shared ViewModel in a Fragment use:

  • by sharedViewModel() - lazy delegate property to inject shared ViewModel instance into a property

  • getSharedViewModel() - directly get the shared ViewModel instance

Just declare the ViewModel only once:

val weatherAppModule = module {

    // WeatherViewModel declaration for Weather View components
    viewModel { WeatherViewModel(get(), get()) }
    // or
    viewModel<WeatherViewModel>()
}

And reuse it in Activity and Fragments:

class WeatherActivity : AppCompatActivity() {

    /*
     * Declare WeatherViewModel with Koin and allow constructor dependency injection
     */
    private val viewModel by viewModel<WeatherViewModel>()
}

class WeatherHeaderFragment : Fragment() {

    /*
     * Declare shared WeatherViewModel with WeatherActivity
     */
    private val viewModel by sharedViewModel<WeatherViewModel>()
}

class WeatherListFragment : Fragment() {

    /*
     * Declare shared WeatherViewModel with WeatherActivity
     */
    private val viewModel by sharedViewModel<WeatherViewModel>()
}

The Activity sharing its ViewModel injects it with by viewModel() or getViewModel(). Fragments are reusing the shared ViewModel with by sharedViewModel().

27.5. ViewModel and injection parameters

the viewModel keyword and injection API is compatible with injection parameters.

In the module:

val appModule = module {

    // ViewModel for Detail View with id as parameter injection
    viewModel { (id : String) -> DetailViewModel(id, get(), get()) }
}

From the injection call site:

class DetailActivity : AppCompatActivity() {

    val id : String // id of the view

    // Lazy inject ViewModel with id parameter
    val viewModel: DetailViewModel by viewModel{ parametersOf(id)}
}

Koin for Ktor framework

Arnaud Giuliani <arnaud.g@insert-koin.io> - and on twitter: @arnogiu :revnumber: 2.0.0-beta-1 :example-caption!:

Covers Koin features for Spark project.

28. About koin-ktor

The koin-ktor project is dedicated to bring dependency injection for Ktor.

28.1. Gradle setup

Add the koin-android dependency to your Gradle project:

// Add Jcenter to your repositories if needed
repositories {
    jcenter()
}
dependencies {
    // Reflection features for Koin
    compile 'org.koin:koin-ktor:2.0.0-beta-1'
}

28.2. Install Koin & inject

To start Koin container, use the installKoin() starter function:

fun Application.main() {
    // Install Ktor features
    install(DefaultHeaders)
    install(CallLogging)
    installKoin(listOf(helloAppModule), logger = SLF4JLogger())

    //...
}

You can also start it from outside of Ktor, but you won’t be compatible with autoreload feature.

KoinComponent powers are available from Application class:

fun Application.main() {
    //...

    // Lazy inject HelloService
    val service by inject<HelloService>()

    // Routing section
    routing {
        get("/hello") {
            call.respondText(service.sayHello())
        }
    }
}

From Routing class:

fun Application.main() {
    //...

    // Lazy inject HelloService
    val service by inject<HelloService>()

    // Routing section
    routing {
        v1()
    }
}

fun Routing.v1() {

    // Lazy inject HelloService from within a Ktor Routing Node
    val service by inject<HelloService>()

    get("/v1/hello") {
        call.respondText("[/v1/hello] " + service.sayHello())
    }
}

From Route class:

fun Application.main() {
    //...

    // Lazy inject HelloService
    val service by inject<HelloService>()

    // Routing section
    routing {
        v1()
    }
}

fun Routing.v1() {
    hello()
}

fun Route.hello() {

    // Lazy inject HelloService from within a Ktor Route
    val service by inject<HelloService>()

    get("/v1/bye") {
        call.respondText("[/v1/bye] " + service.sayHello())
    }
}