This document covers the all koin projects.

Koin core: DSL, Container & API

Arnaud Giuliani <arnaud.g@insert-koin.io> - and on twitter: @arnogiu :revnumber: 1.0.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 ok Koin, ready to be embedded in any Kotlin Runtime/SDK.

1.1. What is Koin?

Koin is pragmatic lightweight dependency injection framework for Kotlin developers. Written in pure Kotlin, using functional resolution only: no proxy, no code generation, no reflection. 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:1.0.1'
}

2. Koin DSL

Koin provides a DSL to help your describe your components with definitions and start the Koin container with those definitions and to instantiate them when needed.

2.1. Why a 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. Those annotations need introspection analyze, proxying/code generation. Whereas with Kotlin DSL, we can use smart functional API to achieve the same goal: make dependency injection.

2.2. The Koin DSL

Below are the Koin DSL keywords:

  • module - create a Koin Module

  • factory - provide a factory bean definition

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

  • get - resolve a component dependency

  • bind - add type to bind for given bean definition

2.3. 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 with single or factory functions, or declare inner modules with module function.

2.4. 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() }
}

single & factory 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.

factory or single { Class constructor / expression }

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

2.5. 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 containter doesn’t retain factory instances as it will give a new instance each time the definition is asked.

2.6. 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 depdendencies in class constructors. This way, your instance will be created with injected intances 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 intance
    single { Service() }
    // declare Controller as single instance, resolving View instance with get()
    single { Controller(get()) }
}

2.7. 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 form, to tell the requested type:

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 prefered and will be used for the rest of the documentation.

2.8. Binding 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
}

2.9. Naming a definition

By default, Koin will name your definition depending on modules path & class name.

module {
    single { ComponentA() }
    module("A") {
        single { ComponentB() }
    }
}

The name of ComponentA definition will be ComponentA, and for ComponentB you will have A.ComponentB.

But you can declare 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.

2.10. Declaring injection parameters

In any single or factory 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.

2.11. Conflicting definitions

Conflicting definitions occurs when you have:

  • several definitions that are matching the same type

  • defined a circular dependency

In the first case, you can name your definition to distinguish them. You can also use modules to alter resolution visibility. In last case, you can also specify a module path when you request a dependency from a KoinComponent.

val myModule = module("org.sample") {


    module("org.demo"){
        single<Service>{ ServiceImpl() }
    }

    module("org.sample"){
        single<Service>{ ServiceImpl() }
    }
}

// Request dependency from /org/sample namespace
val service : Service by inject(module = "org.sample")

For a circular dependency problem, you are in the situation where A←B & B←A. You have to review your relation between A & B, and break the circularity to resolve your situation.

2.12. Definition flags

Koin DSL also proposes some flags.

2.12.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.

CreateOnStart flag at definition level
val myModuleA = module {

    single<Service> { ServiceImp() }
}

val myModuleB = module {

    // eager creation for this definition
    single<Service>(createOnStart=true) { TestServiceImp() }
}
CreateOnStart flag at module level
val myModuleA = module {

    single<Service> { ServiceImp() }
}

val myModuleB = module(createOnStart=true) {

    single<Service>{ TestServiceImp() }
}

The startKoin function will automatically create definition instances flagged with createOnStart.

// Start Koin modules
startKoin(listOf(myModuleA,myModuleB))

If you don’t want to create instances at start, jus use the createOnStart at false from the startKoin() function:

// Start Koin modules
startKoin(listOf(myModuleA,myModuleB), createOnStart = false)

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.

2.12.2. 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(listOf(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.

2.12.3. 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>() }
}

2.13. Experimental features

2.13.1. Create instances using the create() function

Koin has the create<T>() function that allows you to create an instance of type T and inject its first constructor. Note that if you don"t provide any definition lambda to your single/factory definition, will be able to create it for you:

single<T>() == single { create<T>() }
factory<T>() == factory { create<T>() }

When you need to specify the projected type:

single<T> { R() } == single<T> { create<R>() }
factory<T>() { R() } == factory<T> { create<R>() }

This help write definition without having to fill manually the constructor with get() functions. Compare from the 'manual' to the 'automated' created instances.

definitions without create() function
// Presenter <- Service
class Service()
class Controller(val view : View)

val myModule = module {

    // declare Service as single intance
    single { Service() }
    // declare Controller as single instance, resolving View instance with get()
    single<Controller> { ControllerImpl(get()) }
}
definitions with create() function
// Presenter <- Service
class Service()
class Controller(val view : View)

val myModule = module {

    // declare Service as single intance
    single<Service>()
    // declare Controller as single instance, resolving View instance with get()
    single<Controller> { create<ControllerImpl>() }
}

3. Modules & namespaces

By using Koin, you describe definitions in modules. In this section we will see how to declare, organize & link your modules. A module represents a namespace, a way of avoid name conflicts and has some visibility rules.

3.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 ...
}

3.2. Module’s path

A module has a path, which represents a namespace to help you organize your definitions. The path is an optional parameter from the module { } function and its default value is root namespace.

Modules with paths:
// definitions in / (root) namespace
val aRootModule = module { ... }

// definitions in /org/sample namespace
val sampleModule = module("org.sample") { ... }

The default namespace separator is "."

If several modules are declaring the same namespace, their definitions will be in the same namespace. The Koin module’s path is also a good way to separate definitions in Koin.

You can also directly use a class to reuse its path as module name, with moduleName property:

Modules from class:
// definitions in /org/sample/UserSession
val sampleModule = module(UserSession::class.moduleName) { ... }

3.3. Inner modules

A module can also contains inner modules. An inner is a module declaration inside an existing module declaration. It’s also declared with module function, be an inner module must specify a path.

Module and an inner module:
// definitions in / (root) namespace
val aModule = module {

    // definitions in /org/sample namespace
    module("org.sample") {

    }
}

The example above is the equivalent of previous section’s example.

Modules path equivalent:
val sampleModule = module("org.sample") { ... }

// is equivalent to
val sampleModule = module {
    module("org") {
        module("sample") {
            // ...
        }
    }
}

3.4. Implicit definitions naming

For a given definition, you can either give a name (with the name attribute), or let Koin give a name for you. Let’s take a simple module example:

module {
    module("B") {
        single { ComponentA() }
        single { ComponentB(get()) }
    }

    module("C") {
        single { ComponentA() }
        single { ComponentC(get()) }
    }
}

If you try to resolve ComponentA directly, Koin won’t understand which definition you need to use. Koin gives a default name to each definition, prefixing it with modules path.

From the module above, Koin will start like that:

[module] declare Single [name='B.ComponentA',class='org.koin.test.module.ImplicitNamingTest.ComponentA', path:'B']
[module] declare Single [name='B.ComponentB',class='org.koin.test.module.ImplicitNamingTest.ComponentB', path:'B']
[module] declare Single [name='C.ComponentA',class='org.koin.test.module.ImplicitNamingTest.ComponentA', path:'C']
[module] declare Single [name='C.ComponentC',class='org.koin.test.module.ImplicitNamingTest.ComponentC', path:'C']

You can directly request ComponentA from 2 paths: B.ComponentA or C.ComponentA. Just use the name value when requesting your instance:

get<ComponentA>(name = "B.ComponentA")

3.5. Linking definitions between 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 instanciated. 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(listOf(moduleA,moduleB))

Koin will then resolve dependencies from all given modules.

3.6. 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(listOf(repositoryModule,localDatasourceModule))

// Load Repository + Remote Datasource definitions
startKoin(listOf(repositoryModule,remoteDatasourceModule))

3.7. Visibility rules

Visibility rule is quite simple: child modules can see their parents, but not the inverse. A definition from a child module, can see definitions in parents modules. Modules can’t share their definitions in divergent paths.

Let’s take an example:

// definitions in /
val rootModule = module {
    single { ComponentA() }
}
// definitions in /org
val orgModule = module("org") {
    single { ComponentB(...) }
}
// definitions in /org/sample
val sampleModule = module("org.sample") {
    single { ComponentC(...) }
}
// definitions in /org/demo
val demoModule = module("org.demo") {
    single { ComponentD(...) }
}

We have the following resolution possibility:

  • ComponentA can only see definitions from root (can only see `/, can’t see ComponentB, ComponentC & `ComponentD)

  • ComponentB can see definitions from org & root (can see / and /org - can resolve ComponentA - can’t see ComponentC & `ComponentD )

  • ComponentC can see definitions from sample, org & root (can see /, /org, /org/sample - can resolve ComponentA, ComponentB - can’t see ComponentD)

  • ComponentD can see definitions from demo, org & root (can see /, /org, /org/demo - can resolve ComponentA`, ComponentB - can’t see ComponentC)

By declaring definitions in a module with a path, your component are then not visible from outside of this namespace. This is then very useful to protect visibility between modules.

3.8. (Deprecated) Releasing instances from a path

From a KoinComponent, you can the release() function to release single instances for a given path. For example;

module {
    module(path = "A") {
        single { ComponentA() }

        module(path = "B") {
            single { ComponentB() }

            module(path = "C") {
                single { ComponentC() }
            }
        }
    }
}
  • release("A"): will release ComponentA, ComponentB & ComponentC

  • release("B"): will release ComponentB & ComponentC

  • release("C"): will release ComponentC

This API is deprecated. In next version, Koin won’t allow to drop single. Better use the Scope API

4. Using Scopes

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

4.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).

4.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).

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

To declare a scope definition, use the scope function with a given scope Id:

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

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

4.3. Create & retrieve a scope

From a KoinComponent class, just use the getKoin() function to have access to following functions

  • createScope(id : String) - create a scope with given id in the Koin scope registry

  • getScope(id : String) - retrieve a previously created scope with given id, from the Koin scope registry

  • getOrCreateScope(id : String) - create or retrieve if already created, the scope with given id in the Koin scope registry

4.4. Using a scope

Once you have create a Scope instance, let use it to resolve a component:

from a KoinComponent class
module {
    scope("scope_id") { Presenter() }
}

// create a scope
val session = getKoin().createScope("scope_id")

// or get scope if already created before
val session = getKoin().getScope("scope_id")

// will return the same instance of Presenter until Scope 'scope_id' is closed
val presenter = get<Presenter>()

Just resolving it: by inject() or get(). The DSL will indicate which scope to use.

4.5. Inject it with DSL

If one of your definition need to inject a scope instance, just resolve it …​ but be sure to have created the scope before:

class Presenter(val userSession : UserSession)

Just inject it into constructor, with the right scope:

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

4.6. Closing a scope

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

// from a KoinComponent
val session = getKoin().createScope("session")
// will return the same instance of Presenter until 'session' is closed
val presenter = get<Presenter>()

// close it
session.close()
// instance of presenter has been dropped

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

4.7. Scope closing callback

It’s also possible to listen scope closing. Just use the registerScopeCallback() function to register a ScopeCallback instance.

This will notify your ScopeCallback on the onClose(id: String) function, when a scope has been closed.

registerScopeCallback(object : ScopeCallback{
        override fun onClose(id: String) {
            // scope id has been closed
        }
    })

5. 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 Koi container.

5.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
startKoin(listOf(module1,module2 ...))

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.

At start you can also specify several options:

  • logging - see logging section

  • properties loading (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.

5.2. Behind the start

When we start Koin, we create a KoinContext instance in the StandAloneContext holder instance which allows us to be agnostic.

This KoinContext instance gather all components that forms the Koin container. This class is built by the Koin class, the Koin instance builder. The Koin class is responsible to read modules, properties and build a KoinContext instance. This instance is then stored in our StandAloneContext holder.

5.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 ...)

5.4. 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.

6. Logging Koin activity

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

Koin Logger
interface Logger {
    /**
     * Normal log
     */
    fun log(msg : String)

    /**
     * Debug log
     */
    fun debug(msg : String)

    /**
     * Error log
     */
    fun err(msg : String)
}

6.1. Set logging at start

By default, koin-core logs directly into default console output. But you can specify a logger parameter to setup your own Logger implementation:

startKoin(listof(...), logger = EmptyLogger())

6.2. Loggers implementation

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)

6.3. Your own Logger

If you don’t want to log any info, you can use the EmptyLogger class.

You can easily implement the Logger interface to adapt to your logging technology.

7. Properties

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

7.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

7.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")) }
}

7.3. Read/Write property from a KoinComponent

If your class is a KoinComponent, you can interact with Koin properties directly from it with getProperty() and setProperty:

class MyComponent : KoinComponent {

    fun doSomething(){

        // Read a Koin property
        val serviceUrl = getProperty("server_url")

        // Set a Koin property
        setProperty("isDebug",false)
    }
}

8. 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.

8.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(listOf(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()
}

8.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

8.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.

8.4. Resolving instance from a name or a module

If you need to filter about visibility (search a definition by name or module), you can specify the following parameter with get() or by inject()

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

  • module - path of the module

Example of module using classes names as modules:

val module = module {
    module(ComponentB::class.moduleName) {
        single { ComponentA() }
        single { ComponentB(get()) }
    }

    module(ComponentC::class.moduleName) {
        single { ComponentA() }
        single { ComponentC(get()) }
    }
}

class ComponentA
class ComponentB(val componentA: ComponentA)
class ComponentC(val componentA: ComponentA)

We can make the following resolutions:

// retrieve from given module
val a_b = get<ComponentA>(module = ComponentB::class.moduleName)
val a_c = get<ComponentA>(module = ComponentC::class.moduleName)

8.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.

8.6. Releasing instances

From a KoinComponent you can use the release() function to drop instances for a given module path:

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

// Will release all instances from /ComponentB
release("ComponentB")

8.6.1. release() vs closeKoin()

The release() function release instances for a given module path. The closeKoin() function drop every thing in Koin, from definitions to instances.

9. Injection parameters

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

9.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) }
}

9.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) }
}

9.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) }
}

10. 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.

10.1. Extend the DSL

The DSL is mainly defined in org.koin.dsl.context.ModuleDefinition class. Just make a Kotlin extension on type ModuleDefinition to make a new keyword.

Below, an example of viewModel keyword for koin-android-viewmodel.

viewModel keyword example
inline fun <reified T : ViewModel> ModuleDefinition.viewModel(
    name: String = "",
    noinline definition: Definition<T>
) { ... }

10.2. Bring Koin to your SDK

You can extend your actual Runtime/SDK to add Kotlin extension, to bring KoinComponent powers. Check the org.koin.standalone.KoinComponent.kt file to see all the existing Koin features available to port.

Below, an example of by inject() function ported to Android world (extension on ComponentCallbacks to be usable by Activity, Fragment and Service).

Koin inject function for Android
inline fun <reified T> ComponentCallbacks.inject(
    name: String = "",
    module: String? = null,
    noinline parameters: ParameterDefinition = emptyParameterDefinition()
) = lazy { (StandAloneContext.koinContext as KoinContext).get<T>(name, module, parameters) }

10.3. Need a new startKoin?

If you need to adapt the startKoin() function to your SDK, up to you to make a new one and call the original startKoin behind it.

Below, the startKoin() adapted for Android world (extension on Application class).

startKoin from Android Application class
fun Application.startKoin(
    application: Application,
    modules: List<Module>,
    extraProperties: Map<String, Any> = HashMap(),
    loadProperties: Boolean = true,
    logger: Logger = AndroidLogger()
) {
    Koin.logger = logger
    val koin = StandAloneContext.startKoin(
        modules,
        extraProperties = extraProperties,
        useKoinPropertiesFile = false
    ).with(application)
    if (loadProperties) koin.bindAndroidProperties(application)
}

11. Examples

Here are some koin-core example projects.

11.1. The Coffee Maker

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

11.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 => =>")
        }
    }
}

11.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(listOf(coffeeMakerModule))

    CoffeeApp().run()
}

Testing with Koin

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

Covers Koin testing features.

12. About koin-test

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

12.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:1.0.1'
}

13. 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 hceck 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.

14. 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.

15. 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>())
    }

16. 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: 1.0.1

Cover the extended features for koin-core project.

17. Extensions & Experimental features

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

17.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)

17.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>() }
}

17.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: 1.0.1 :example-caption!:

Covers Java Koin features.

18. About koin-java

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

18.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:1.0.1'
}

19. 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));

20. 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: 1.0.1 :example-caption!:

Covers Koin features for Android.

21. 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.

21.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:1.0.1'
}

22. Start Koin with Android

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

22.1. startKoin() from your Application

From your Application class you can use the startKoin() function:

class MainApplication : Application() {

    override fun onCreate() {
        super.onCreate()

        startKoin(this, myAppModules)
    }
}

22.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:

// Start Koin and init Application instance definition
startKoin(androidContext, myAppModules)

Also, there is a with operator that can help you bind the Context instance in the case of non Android class (Junit for example). It can help like follow:

Example of mocking Android context in a JUnit
// Start Koin and init Context instance definition
startKoin(myAppModules) with (mock(Context::class.java))

22.3. Koin Logging

With the startKoin() function, we have a parameter logger which has a default value: 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(this, myAppModules, logger = EmptyLogger())
    }
}

22.4. Properties

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

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

    override fun onCreate() {
        super.onCreate()

        startKoin(this, myAppModules, extraProperties = mapOf( ... ))
    }
}

23. 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?

23.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()
    }

23.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.

24. Koin DSL Extension for Android

Below, the added keywords for Koin DSL.

24.1. Getting Android context inside a Module

The androidContext() function 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))
    }
}

25. Scope features for Android

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

25.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:1.0.1'
    // or Scope for AndroidX
    implementation 'org.koin:koin-androidx-scope:1.0.1'
}

25.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.

25.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"))
    }

25.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()

26. Architecture Components with Koin: ViewModel

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

26.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:1.0.1'
    // or ViewModel for AndroidX
    implementation 'org.koin:koin-androidx-viewmodel:1.0.1'
}

26.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.

26.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()
}

26.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().

26.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 Spark web framework

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

Covers Koin features for Spark project.

27. About koin-spark

The koin-spark project is dedicated to bring dependency injection for Spark.

27.1. Gradle setup

Add the koin-ktor dependency to your Gradle project:

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

27.2. DSL enhancements

The project brings the controller keyword to declare a single instance of your controller:

val helloAppModule = module {
    single { HelloServiceImpl(get()) as HelloService }
    single { HelloRepositoryImpl() as HelloRepository }

    // Declare a controller
    controller { HelloController(get()) }
}

You controller class must extend SparkController interface:

class HelloController(val service: HelloService) : SparkController {
    init {
        get("/hello") {
            service.sayHello()
        }
    }
}

27.3. Spark & Koin

The koin-spark project also introduces start() and stop() function. This first help you start Spark server with Koin modules:

fun main(vararg args: String) {
    // Spark with Koin
    start(modules = listOf(helloAppModule))
}

Koin for Ktor framework

Arnaud Giuliani <arnaud.g@insert-koin.io> - and on twitter: @arnogiu :revnumber: 1.0.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:1.0.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())
    }
}