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 instancebind/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 typesbind to request an entity thanks to its primary and secondary type single { MyComponent() } bind Component::class can be found via bind<MyComponent,Component>()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.
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>()) }
}
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 definitionsbind<>() to resolve a component in the DSLgetAll<Simple.ComponentInterface1>() give us the 2 instancesIn 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.
already online: https://bintray.com/ekito/koin
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!
Most helpful comment
normally, before the end of the month 馃憤