Using Scopes

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

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

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

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

Important

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>()
Important

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.

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

Important

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.

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

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

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()
Important

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

Scope callback — TODO