Koin: [2.0.0-GA] Just before the release!

Created on 6 May 2019  路  23Comments  路  Source: InsertKoinIO/koin

Hi Koin users,

the Koin 2.0.0-GA has been published with the last changes for the final release. I need now to polish/update/write documentation to help you use all of that.

You can check the last changes in the changelog: https://github.com/InsertKoinIO/koin/blob/master/CHANGELOG.md

Some latest updates:

  • injectOrNull & getOrNull to retrieve a potential instance
  • bind/binds operator has been simplified to tag secondary types for definition. This secondary type can be reached with getAll & bind functions
  • getAll to request all definitions regarding primary & secondary types
  • bind to request an entity thanks to its primary and secondary type single { MyComponent() } bind Component::class can be found via bind<MyComponent,Component>()
  • internals has been reworked to use scope mechanism as primary primitive work level: Koin works now with its root scope by default. If you want to request a dependency from a scope, just create/retrieve it and use it directly.
  • Scope DSL has been limited. Now you can't try to declare single/factory in scope section.
  • now possible to declare a type in different scopes
val koin = koinApplication {
            modules(
                    module {
                        scope(named("B")) {
                            scoped { Simple.ComponentA() }
                        }
                        scope(named("A")) {
                            scoped { Simple.ComponentA() }
                        }
                    }
            )
        }.koin

        val scopeA = koin.createScope("A", named("A")).get<Simple.ComponentA>()
        val scopeB = koin.createScope("B", named("B")).get<Simple.ComponentA>()
        scopeA & scopeB => are different!

If you find a problem, help us & give your feedback!

Final release 2.0.0 is on the road 馃憤

Cheers.

team

Most helpful comment

normally, before the end of the month 馃憤

All 23 comments

Good morning Arnaud,

_I can't use the scope feature with my MVVM architecture_.

It has been said that the instance of my view model can't be created.

Could not create instance for [type:Factory,class:'MyActivityViewModel']

More precisely, the first error in the stacktrace concerns the definition of the view model constructor's parameter which is not found :

No definition found for 'EventCollectorInterface' has been found. Check your module definitions.

My injections file

    scope(named<MyActivity>()) {
        scoped { EventCollectorService() as EventCollectorInterface }
        viewModel { MyActivityViewModel(get()) }
    }

My activity

class MyActivity : AppCompatActivity() {
    private val viewModel by viewModel<MyActivityViewModel>()
    ...
}

My view model definition

class MyActivityViewModel(
    private val eventCollectorInterface: EventCollectorInterface
) : ViewModel()

A ViewModel is not tied to a scope. You can't declare a viewModel in a scope. To use a scoped definition instance from a ViewModel, pass it the scopeId that you need:

// Your ViewModel that use a defintion from a scope
viewModel { (scopeId : ScopeID) -> MyActivityViewModel(getScope(scopedId).get()) }

// Your scope
scope(named<MyActivity>()) {
    scoped { EventCollectorService() as EventCollectorInterface }
}

// retrieve your ViewModel by passing the scopeId
val viewModel by viewModel<MyActivityViewModel> { parametersOf(currentScope.id) }

It looks like qualifiers are not cached in combination with the type, making this code fail:

module {
    single<String>(named("foo")) { "hello world" }
    single<Long>(named("foo")) { 1L }
}
org.koin.core.error.DefinitionOverrideException: Already existing definition or try to override an existing one with qualifier 'foo' with [type:Single,name:'foo', class:'java.lang.Long'] but has already registered [type:Single,name:'foo', class:'java.lang.String']
at org.koin.core.registry.BeanRegistry.saveDefinitionForName(BeanRegistry.kt:110)

This worked before, and I would expect it to still work since the same qualifiers are typically used across domains.

Edit:
Works fine if I change koin-core to use qualifier.toString() + "@" + kClass.getFullName() instead of qualifier.toString(), is this change desired? Happy to put up the actual change.

A ViewModel is not tied to a scope. You can't declare a viewModel in a scope. To use a scoped definition instance from a ViewModel, pass it the scopeId that you need:

// Your ViewModel that use a defintion from a scope
viewModel { (scopeId : ScopeID) -> MyActivityViewModel(getScope(scopedId).get()) }

// Your scope
scope(named<MyActivity>()) {
    scoped { EventCollectorService() as EventCollectorInterface }
}

// retrieve your ViewModel by passing the scopeId
val viewModel by viewModel<MyActivityViewModel> { parametersOf(currentScope.id) }

It works perfectly 馃憤

Thank you for all your work!

module { single { SettingsImpl(get()) as Settings } bind SettingsReadOnly::class }
in Android Fragment
private val settingsReadOnly: SettingsReadOnly by inject()
got

NoBeanDefFoundException: No definition found for 'info.dvkr.screenstream.data.settings.SettingsReadOnly' has been found. Check your module definitions.

yes, secondary types are now found by bind & getAll. You will find your component by: getKoin().bind<Settings,SettingsReadOnly>()

This way you can have several components bound your secondary type, here SettingsReadOnly

but let me check to get it back in get()... to allow you to retrieve it naturally if you only have one component for a secondary type.

publishing 2.0.0-GA2 with several fixes for additional types:

To sum up:

module {
    single { Simple.Component1() } bind Simple.ComponentInterface1::class
    single { Simple.Component2() } bind Simple.ComponentInterface1::class
    single { Simple.UserComponent(bind<Simple.Component1, Simple.ComponentInterface1>()) }
}
  • you can get directly the instances with get<Simple.Component1>(), get<Simple.Component2>()
  • get<Simple.ComponentInterface1>() will throw an error and tell you to use bind because can鈥檛 choose between the 2 definitions
  • you can use bind<>() to resolve a component in the DSL
  • getAll<Simple.ComponentInterface1>() give us the 2 instances

In a situation with only one definition for a secondary type, get would return the right instance directly:

module {
    single { Simple.Component1() } bind Simple.ComponentInterface1::class
}

// returns the right instance
get<Simple.ComponentInterface1>() 

@arnaudgiuliani : Regarding your last point above, the following does not work for me (self-contained example):

interface TransactionManager
interface ConnectionProvider
class TransactionManagerImpl: TransactionManager, ConnectionProvider

val koinDbModule = module {
    single<TransactionManager> { TransactionManagerImpl() } bind ConnectionProvider::class
}

class AppTest: KoinComponent {
    @Test
    fun run() {
        startKoin {
            modules(koinDbModule)
        }

        val connectionProvider: ConnectionProvider = get()

    }
}

Results in:

No definition found for 'koinplayground.ConnectionProvider' has been found. Check your module definitions.
org.koin.core.error.NoBeanDefFoundException: No definition found for 'koinplayground.ConnectionProvider' has been found. Check your module definitions.
    at org.koin.core.scope.Scope.findDefinition(Scope.kt:146)
    at org.koin.core.scope.Scope.resolveInstance(Scope.kt:140)
    at org.koin.core.scope.Scope.get(Scope.kt:131)
    at koinplayground.AppTest.run(AppTest.kt:39)

Why is that? I only have one definition for the secondary type.

check with 2.0.0-GA2

@arnaudgiuliani : Ok, I will try to upgrade as soon as you publish those artifacts.

Sorry, I missed that. It would be nice if you could always publish to JCenter and create a release on Github (like you did for 2.0.0-GA).

@arnaudgiuliani In RC2, we had possibility to pass a scope reference to KoinJavaComponent get and inject methods. As of GA2, it is no longer possible? How can I reference a scoped instance from Java code from now on?

@alexbchr you have to create a scope, but API is less Java friendly. Can you open a ticket?

Yeah we have the same issue, extremely tricky to get a scope from Java and to get a dependency with it.

For future reference, here is the related issue #451

@alexbchr thanks 馃憤

KoinJavaComponent fixed back with scope property in 2.0.0-GA3

Removing the scope parameter from get() / inject() is definitely a nice step forward. It was confusing at best and, worst, could introduce some nasty side effects since the scope was lazily evaluated using inject().

Any ideas on when the final 2.0.0 will be published?

normally, before the end of the month 馃憤

Below is the code I am using for using Koin components in some legacy Java classes with Koin 2.0.

import org.koin.core.Koin
import org.koin.core.context.GlobalContext
import org.koin.core.parameter.ParametersDefinition
import org.koin.core.scope.ScopeInstance

object KoinJavaUtils {


    /**
     * Retrieve given dependency lazily
     * @param clazz - dependency class
     * @param name - bean canonicalName / optional
     * @param scope
     * @param parameters - dependency parameters / optional
     */
    @JvmOverloads
    @JvmStatic
    fun <T : Any> inject(
            clazz: Class<T>,
            name: String? = null,
            scope: ScopeInstance? = null,
            parameters: ParametersDefinition? = null
    ): Lazy<T> {
        return lazy { get(clazz, name, scope, parameters) }
    }

    /**
     * Retrieve given dependency
     * @param clazz - dependency class
     * @param name - bean canonicalName / optional
     * @param scope - scope
     * @param parameters - dependency parameters / optional
     */
    @JvmOverloads
    @JvmStatic
    fun <T : Any> get(
            clazz: Class<T>,
            name: String? = null,
            scope: ScopeInstance? = null,
            parameters: ParametersDefinition? = null
    ): T {
        return getKoin().get(
                clazz.kotlin,
                name,
                scope,
                parameters
        )
    }

    /**
     * inject lazily given property
     * @param key - key property
     */
    @JvmStatic
    fun getKoin(): Koin = GlobalContext.get().koin
}

During the process of migration to 2, I've get to see org.koin.core.scope.ScopeInstance package unresolved reference.
.
It would be great if anyone can give me a hand for this class migration. I ran out of options due to not much doco. Appreciated your help!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

pchmielowski picture pchmielowski  路  3Comments

AHarazim picture AHarazim  路  3Comments

hkelidari picture hkelidari  路  3Comments

sankarsana picture sankarsana  路  4Comments

fmobus picture fmobus  路  4Comments