diff --git a/docs/reference/koin-android/scope.md b/docs/reference/koin-android/scope.md index 2cfe3c6bb..554bc4586 100644 --- a/docs/reference/koin-android/scope.md +++ b/docs/reference/koin-android/scope.md @@ -60,7 +60,7 @@ val androidModule = module { on it and can't totally drop it via garbage collection. ::: -## Scope for Android Components (3.2.1 update) +## Scope for Android Components (since 3.2.1) ### Declare an Android Scope @@ -164,6 +164,76 @@ class MyActivity() : AppCompatActivity(contentLayoutId), AndroidScopeComponent { If you try to access Scope from `onDestroy()` function, scope will be already closed. ::: +### ViewModel Scope (since 3.5.4) + +ViewModel is only created against root scope to avoid any leaking (leaking Activity or Fragment ...). This guard for the visibility problem, where ViewModel could have access to incompatible scopes. + +:::note +If you ViewModel can't get access to a dependency, check in which scope it has been declared. +::: + + +:::note +If you _really_ need to bridge a dependency from outside a ViewModel scope, you can use "injected parameters" to pass some objects to your ViewModel. +::: + +`ScopeViewModel` is a new class to help work on ViewModel scope. This handle ViewModel's scope creation, and provide `scope` property to allow inject with `by scope.inject()`: + +```kotlin +module { + viewModelOf(::MyScopeViewModel) + scope { + scopedOf(::Session) + } +} + +class MyScopeViewModel : ScopeViewModel() { + + // on onCleared, scope is closed + + // injected from current MyScopeViewModel's scope + val session by scope.inject() + +} +``` + +By using `ScopeViewModel` you can also overrode `onCloseScope()` function, to run code before scope is being closed. + +:::note +All instances inside a ViewModel scope have the same visibility and will survive for lifetime of ViewModel instance, until ViewModel's onCleared function is called +::: + +For example, Once an Activity or fragment has created a ViewModel, the associated scope is created: + +```kotlin +class MyActivity : AppCompatActivity() { + + // Create ViewModel and its scope + val myViewModel by viewModel() + +} +``` + +Once your ViewModel is created, all associated dependencies from within this scope can be created and injected. + +To implement manually your ViewModel scope without `ScopeViewModel` class proceed as follow: + +```kotlin +class MyScopeViewModel : ViewModel(), KoinScopeComponent { + + override val scope: Scope = createScope(this) + + // inject your dependency + val session by scope.inject() + + // clear scope + override fun onCleared() { + super.onCleared() + scope.close() + } +} +``` + ## Scope Links Scope links allow to share instances between components with custom scopes. diff --git a/docs/reference/koin-android/viewmodel.md b/docs/reference/koin-android/viewmodel.md index 931bbd9f7..a2e4cbbdf 100644 --- a/docs/reference/koin-android/viewmodel.md +++ b/docs/reference/koin-android/viewmodel.md @@ -41,6 +41,10 @@ class DetailActivity : AppCompatActivity() { } ``` +:::note +ViewModel key is calculated against Key and/or Qualifier +::: + ## Activity Shared ViewModel One ViewModel instance can be shared between Fragments and their host Activity. @@ -175,6 +179,10 @@ class NavFragment : Fragment() { } ``` +## ViewModel Scope API + +see all API to be used for ViewModel and Scopes: [ViewModel Scope](/docs/reference/koin-android/scope.md#viewmodel-scope-since-354) + ## ViewModel Generic API Koin provides some "under the hood" API to directly tweak your ViewModel instance. The available functions are `viewModelForClass` for `ComponentActivity` and `Fragment`: diff --git a/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/components/mvvm/ExtSimpleViewModel.kt b/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/components/mvvm/ExtSimpleViewModel.kt deleted file mode 100644 index bd8b7d75d..000000000 --- a/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/components/mvvm/ExtSimpleViewModel.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.koin.sample.sandbox.components.mvvm - -import androidx.lifecycle.ViewModel -import org.koin.sample.sandbox.components.scope.Session - -class ExtSimpleViewModel(val session: Session) : ViewModel() \ No newline at end of file diff --git a/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/components/mvvm/MyScopeViewModel.kt b/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/components/mvvm/MyScopeViewModel.kt new file mode 100644 index 000000000..e4953becd --- /dev/null +++ b/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/components/mvvm/MyScopeViewModel.kt @@ -0,0 +1,19 @@ +package org.koin.sample.sandbox.components.mvvm + +import androidx.lifecycle.ViewModel +import org.koin.core.component.KoinScopeComponent +import org.koin.core.component.createScope +import org.koin.core.scope.Scope +import org.koin.sample.sandbox.components.scope.Session + +class MyScopeViewModel : ViewModel(), KoinScopeComponent { + + override val scope: Scope = createScope(this) + + val session by scope.inject() + + override fun onCleared() { + super.onCleared() + scope.close() + } +} \ No newline at end of file diff --git a/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/components/mvvm/MyScopeViewModel2.kt b/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/components/mvvm/MyScopeViewModel2.kt new file mode 100644 index 000000000..c94f2c574 --- /dev/null +++ b/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/components/mvvm/MyScopeViewModel2.kt @@ -0,0 +1,12 @@ +package org.koin.sample.sandbox.components.mvvm + +import org.koin.androidx.scope.ScopeViewModel +import org.koin.core.annotation.KoinExperimentalAPI +import org.koin.sample.sandbox.components.scope.Session + +@OptIn(KoinExperimentalAPI::class) +class MyScopeViewModel2 : ScopeViewModel() { + + val session by scope.inject() + +} \ No newline at end of file diff --git a/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/components/mvvm/SavedStateViewModel.kt b/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/components/mvvm/SavedStateViewModel.kt index 8bf281ee0..cbd2b4d00 100644 --- a/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/components/mvvm/SavedStateViewModel.kt +++ b/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/components/mvvm/SavedStateViewModel.kt @@ -9,6 +9,6 @@ class SavedStateViewModel(val handle: SavedStateHandle, val id: String, val serv init { val get = handle.get(id) println("handle: $get") - handle.set(id,UUID.randomUUID().toString()) + handle[id] = UUID.randomUUID().toString() } } \ No newline at end of file diff --git a/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/components/scope/Session.kt b/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/components/scope/Session.kt index c1813767d..be37d41c7 100644 --- a/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/components/scope/Session.kt +++ b/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/components/scope/Session.kt @@ -6,6 +6,12 @@ class Session { var id: String = UUID.randomUUID().toString() } +class SessionConsumer(private val session: Session){ + + fun getSessionId() = session.id + +} + class SessionActivity { val id: String = UUID.randomUUID().toString() } diff --git a/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/di/AppModule.kt b/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/di/AppModule.kt index 955523505..3676d3aef 100644 --- a/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/di/AppModule.kt +++ b/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/di/AppModule.kt @@ -8,7 +8,6 @@ import org.koin.core.module.dsl.* import org.koin.core.module.includes import org.koin.core.qualifier.named import org.koin.dsl.lazyModule -import org.koin.dsl.module import org.koin.sample.sandbox.components.Counter import org.koin.sample.sandbox.components.SCOPE_ID import org.koin.sample.sandbox.components.SCOPE_SESSION @@ -21,6 +20,7 @@ import org.koin.sample.sandbox.components.mvp.ScopedPresenter import org.koin.sample.sandbox.components.mvvm.* import org.koin.sample.sandbox.components.scope.Session import org.koin.sample.sandbox.components.scope.SessionActivity +import org.koin.sample.sandbox.components.scope.SessionConsumer import org.koin.sample.sandbox.mvp.MVPActivity import org.koin.sample.sandbox.mvvm.MVVMActivity import org.koin.sample.sandbox.mvvm.MVVMFragment @@ -45,13 +45,13 @@ val mvpModule = lazyModule { scope { scopedOf(::ScopedPresenter)// { (id: String) -> ScopedPresenter(id, get()) } + } } val mvvmModule = lazyModule { viewModelOf(::SimpleViewModel)// { (id: String) -> SimpleViewModel(id, get()) } - viewModelOf(::SimpleViewModel) { named("vm1") } //{ (id: String) -> SimpleViewModel(id, get()) } viewModel(named("vm2")) { (id: String) -> SimpleViewModel(id, get()) } @@ -61,22 +61,30 @@ val mvvmModule = lazyModule { // viewModel { ViewModelImpl(get()) } viewModelOf(::ViewModelImpl) { bind() } - scope { + viewModelOf(::MyScopeViewModel) + scope { + scopedOf(::Session) + } + viewModelOf(::MyScopeViewModel2) + scope { + scopedOf(::Session) + scopedOf(::SessionConsumer) + } + + viewModelOf(::SavedStateViewModel) { named("vm2") } + + scope { scopedOf(::Session) fragmentOf(::MVVMFragment) // { MVVMFragment(get()) } - viewModelOf(::ExtSimpleViewModel) - viewModelOf(::ExtSimpleViewModel) { named("ext") } - viewModelOf(::SavedStateViewModel) { named("vm2") } scoped { MVVMPresenter1(get()) } scoped { MVVMPresenter2(get()) } } scope { scoped { (id: String) -> ScopedPresenter(id, get()) } + // to retrieve from parent // scopedOf(::Session) - viewModelOf(::ExtSimpleViewModel) - viewModelOf(::ExtSimpleViewModel) { named("ext") } } } diff --git a/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/mvvm/MVVMActivity.kt b/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/mvvm/MVVMActivity.kt index 9da57428c..4372212b3 100644 --- a/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/mvvm/MVVMActivity.kt +++ b/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/mvvm/MVVMActivity.kt @@ -8,35 +8,28 @@ import org.koin.android.ext.android.inject import org.koin.androidx.fragment.android.replace import org.koin.androidx.fragment.android.setupKoinFragmentFactory import org.koin.androidx.scope.ScopeActivity -import org.koin.androidx.viewmodel.ext.android.getViewModel import org.koin.androidx.viewmodel.ext.android.viewModel -import org.koin.androidx.viewmodel.ext.android.viewModelForClass import org.koin.core.parameter.parametersOf import org.koin.core.qualifier.named import org.koin.sample.sandbox.R -import org.koin.sample.sandbox.components.ID import org.koin.sample.sandbox.components.mvp.FactoryPresenter import org.koin.sample.sandbox.components.mvvm.* import org.koin.sample.sandbox.components.scope.Session +import org.koin.sample.sandbox.components.scope.SessionConsumer import org.koin.sample.sandbox.scope.ScopedActivityA import org.koin.sample.sandbox.utils.navigateTo class MVVMActivity : ScopeActivity(contentLayoutId = R.layout.mvvm_activity) { - lateinit var simpleViewModel: SimpleViewModel //by viewModel { parametersOf(ID) } - + val vm: SimpleViewModel by viewModel { parametersOf("vm") } val vm1: SimpleViewModel by viewModel(named("vm1")) { parametersOf("vm1") } val vm2: SimpleViewModel by viewModel(named("vm2")) { parametersOf("vm2") } - val scopeVm: ExtSimpleViewModel by viewModel() - val extScopeVm: ExtSimpleViewModel by viewModel(named("ext")) - - // val savedVm: SavedStateViewModel by stateViewModel { parametersOf("vm1") } - val savedVm: SavedStateViewModel by viewModel { parametersOf("vm1") } + val scopeVm1: MyScopeViewModel by viewModel() + val scopeVm2: MyScopeViewModel2 by viewModel() - // val state = Bundle().apply { putString("id", "vm1") } -// val stateVM: SavedStateBundleViewModel by stateViewModel(state = { state }) val stateVM: SavedStateBundleViewModel by viewModel() + val savedVm: SavedStateViewModel by viewModel { parametersOf("vm1") } val abstractVM : AbstractViewModel by viewModel() @@ -60,8 +53,6 @@ class MVVMActivity : ScopeActivity(contentLayoutId = R.layout.mvvm_activity) { navigateTo(isRoot = true) } -// simpleViewModel = viewModelForClass(SimpleViewModel::class, owner = this).value - checks() } @@ -77,14 +68,18 @@ class MVVMActivity : ScopeActivity(contentLayoutId = R.layout.mvvm_activity) { private fun checks() { assert(abstractVM is ViewModelImpl) - assert(scopeVm.session.id == extScopeVm.session.id) assert(stateVM.result == "vm1") + assert(vm1.id != vm.id) assert(vm1.id != vm2.id) val p1 = scope.get() val p2 = scope.get() - assert(p1.ctx == this) assert(p2.ctx == getKoin().get()) + + assert(scopeVm1.session.id == scopeVm1.scope.get().id) + assert(scopeVm2.session.id == scopeVm2.scope.get().id) + assert(scopeVm2.scope.get().getSessionId() == scopeVm2.scope.get().id) + assert(scopeVm1.session.id != scopeVm2.session.id) } } \ No newline at end of file diff --git a/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/mvvm/MVVMFragment.kt b/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/mvvm/MVVMFragment.kt index a816ca157..2137d06fb 100644 --- a/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/mvvm/MVVMFragment.kt +++ b/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/mvvm/MVVMFragment.kt @@ -11,13 +11,10 @@ import org.koin.androidx.scope.requireScopeActivity import org.koin.androidx.viewmodel.ext.android.activityViewModel import org.koin.androidx.viewmodel.ext.android.getActivityViewModel import org.koin.androidx.viewmodel.ext.android.viewModel -import org.koin.androidx.viewmodel.ext.android.viewModelForClass import org.koin.core.parameter.parametersOf -import org.koin.core.qualifier.named import org.koin.core.scope.Scope import org.koin.sample.sandbox.R import org.koin.sample.sandbox.components.ID -import org.koin.sample.sandbox.components.mvvm.ExtSimpleViewModel import org.koin.sample.sandbox.components.mvvm.SavedStateViewModel import org.koin.sample.sandbox.components.mvvm.SimpleViewModel import org.koin.sample.sandbox.components.scope.Session @@ -27,14 +24,8 @@ class MVVMFragment(private val session: Session) : Fragment(R.layout.mvvm_fragme override val scope: Scope by fragmentScope() val simpleViewModel: SimpleViewModel by viewModel { parametersOf(ID) } - - // Generic KClass Access - val scopeVm: ExtSimpleViewModel by viewModelForClass(ExtSimpleViewModel::class) - val extScopeVm: ExtSimpleViewModel by viewModel(named("ext")) - val shared: SimpleViewModel by activityViewModel { parametersOf(ID) } - val sharedSaved: SavedStateViewModel by activityViewModel { parametersOf(ID) } val saved by viewModel { parametersOf(ID) } val saved2 by viewModel { parametersOf(ID) } @@ -48,13 +39,8 @@ class MVVMFragment(private val session: Session) : Fragment(R.layout.mvvm_fragme checkNotNull(session) assert(shared != simpleViewModel) - // TODO Handle shared isntance - out of Scope -// assert((requireActivity() as MVVMActivity).simpleViewModel == shared) -// assert((requireActivity() as MVVMActivity).savedVm == sharedSaved) - assert((requireActivity() as MVVMActivity).savedVm != saved) assert((requireActivity() as MVVMActivity).savedVm != saved2) - assert(scopeVm.session.id == extScopeVm.session.id) val shared2 = getActivityViewModel { parametersOf(ID) } diff --git a/examples/gradle/versions.gradle b/examples/gradle/versions.gradle index b71de1cf8..edb1b8d3f 100644 --- a/examples/gradle/versions.gradle +++ b/examples/gradle/versions.gradle @@ -2,8 +2,10 @@ ext { // Kotlin kotlin_version = '1.9.21' // Koin Versions - koin_version = '3.6.0-alpha1' + koin_version = '3.5.4-RC-1' koin_android_version = koin_version + koin_compose_version = "1.1.3-RC-1" + coroutines_version = "1.7.3" ktor_version = "2.3.7" // Compose diff --git a/examples/sample-desktop-compose/build.gradle.kts b/examples/sample-desktop-compose/build.gradle.kts index 986c5b913..f8a070ef0 100755 --- a/examples/sample-desktop-compose/build.gradle.kts +++ b/examples/sample-desktop-compose/build.gradle.kts @@ -7,11 +7,12 @@ plugins { repositories { mavenCentral() + mavenLocal() maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") google() } -val koin_version : String by project +val koin_compose_version : String by project dependencies { // Note, if you develop a library, you should use compose.desktop.common. @@ -19,7 +20,7 @@ dependencies { // (in a separate module for demo project and in testMain). // With compose.desktop.common you will also lose @Preview functionality implementation(compose.desktop.currentOs) - implementation("io.insert-koin:koin-compose:$koin_version") + implementation("io.insert-koin:koin-compose:$koin_compose_version") } compose.desktop { diff --git a/projects/android/koin-android/src/main/java/org/koin/androidx/scope/ScopeViewModel.kt b/projects/android/koin-android/src/main/java/org/koin/androidx/scope/ScopeViewModel.kt new file mode 100644 index 000000000..917ca6f05 --- /dev/null +++ b/projects/android/koin-android/src/main/java/org/koin/androidx/scope/ScopeViewModel.kt @@ -0,0 +1,34 @@ +package org.koin.androidx.scope + +import androidx.lifecycle.ViewModel +import org.koin.core.annotation.KoinExperimentalAPI +import org.koin.core.component.KoinScopeComponent +import org.koin.core.component.createScope +import org.koin.core.scope.Scope + +/** + * Class to help support Koin Scope in a ViewModel + * create directly a scope instance for current ViewModel + * + * allow to intercept before scope closing with `onCloseScope`, to be overriden + * + * Destroy linked scope with `onCleared` + * + * @author Arnaud Giuliani + */ +@KoinExperimentalAPI +abstract class ScopeViewModel : ViewModel(), KoinScopeComponent { + + override val scope: Scope = createScope(this) + + /** + * To override to add behavior before closing Scope + */ + open fun onCloseScope(){} + + override fun onCleared() { + super.onCleared() + onCloseScope() + scope.close() + } +} \ No newline at end of file diff --git a/projects/android/koin-android/src/main/java/org/koin/androidx/viewmodel/GetViewModel.kt b/projects/android/koin-android/src/main/java/org/koin/androidx/viewmodel/GetViewModel.kt index 1e8a9ac1c..2fbe5f51b 100644 --- a/projects/android/koin-android/src/main/java/org/koin/androidx/viewmodel/GetViewModel.kt +++ b/projects/android/koin-android/src/main/java/org/koin/androidx/viewmodel/GetViewModel.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelStore import androidx.lifecycle.viewmodel.CreationExtras import org.koin.androidx.viewmodel.factory.KoinViewModelFactory +import org.koin.core.Koin import org.koin.core.annotation.KoinInternalApi import org.koin.core.parameter.ParametersDefinition import org.koin.core.parameter.ParametersHolder @@ -25,6 +26,7 @@ import kotlin.reflect.KClass * @param parameters - for instance building injection */ @KoinInternalApi +@Deprecated("scope is not used for ViewModel creation") fun resolveViewModel( vmClass: KClass, viewModelStore: ViewModelStore, @@ -33,11 +35,38 @@ fun resolveViewModel( qualifier: Qualifier? = null, scope: Scope, parameters: ParametersDefinition? = null, +): T { + return resolveViewModel(vmClass, viewModelStore, key, extras, qualifier, scope.getKoin(), parameters) +} + +/** + * Resolve a ViewModel instance + * + * @param vmClass + * @param viewModelStore + * @param key + * @param extras - @see CreationExtras + * @param qualifier + * @param koin + * @param parameters - for instance building injection + */ +@KoinInternalApi +fun resolveViewModel( + vmClass: KClass, + viewModelStore: ViewModelStore, + key: String? = null, + extras: CreationExtras, + qualifier: Qualifier? = null, + koin : Koin, + parameters: ParametersDefinition? = null, ): T { val modelClass: Class = vmClass.java - val factory = KoinViewModelFactory(vmClass, scope, qualifier, parameters) + val factory = KoinViewModelFactory(vmClass, koin.scopeRegistry.rootScope, qualifier, parameters) val provider = ViewModelProvider(viewModelStore, factory, extras) - val vmKey = getViewModelKey(qualifier, scope, key) + val vmKey = getViewModelKey(qualifier, key) + + //To help track Keys +// koin.logger.debug("[vm_key] - provider:$provider - class:$modelClass = $vmKey (q:'${qualifier?.value}', k:'$key')") return when { vmKey != null -> provider[vmKey, modelClass] else -> provider[modelClass] @@ -45,14 +74,11 @@ fun resolveViewModel( } @KoinInternalApi -internal fun getViewModelKey(qualifier: Qualifier?, scope: Scope, key: String?): String? { - return if (qualifier == null && key == null && scope.isRoot) { - null - } else { - val q = qualifier?.value ?: "" - val k = key ?: "" - val s = if (!scope.isRoot) scope.id else "" - "$q$k$s" +internal fun getViewModelKey(qualifier: Qualifier?, key: String?): String? { + return when { + qualifier != null -> qualifier.value + (key?.let { "_$it" } ?: "") + key != null -> key + else -> null } } diff --git a/projects/android/koin-android/src/test/java/org/koin/test/android/viewmodel/ViewModelKeyTest.kt b/projects/android/koin-android/src/test/java/org/koin/test/android/viewmodel/ViewModelKeyTest.kt index 7882d5b87..54a708ded 100644 --- a/projects/android/koin-android/src/test/java/org/koin/test/android/viewmodel/ViewModelKeyTest.kt +++ b/projects/android/koin-android/src/test/java/org/koin/test/android/viewmodel/ViewModelKeyTest.kt @@ -13,32 +13,20 @@ class ViewModelKeyTest { @OptIn(KoinInternalApi::class) @Test fun generate_right_key() { - val koin = koinApplication().koin - val root = koin.scopeRegistry.rootScope - val q = StringQualifier("_qualifier_") - val scope = Scope(StringQualifier("_q_"), id = "_id_", _koin = koin, isRoot = false) val key = "_KEY_" assertEquals( - null, getViewModelKey(qualifier = null, scope = root, key = null) - ) - assertEquals( - q.value, getViewModelKey(qualifier = q, scope = root, key = null) + null, getViewModelKey(qualifier = null, key = null) ) assertEquals( - key, getViewModelKey(qualifier = null, scope = root, key = key) + q.value, getViewModelKey(qualifier = q, key = null) ) assertEquals( - scope.id, getViewModelKey(qualifier = null, scope = scope, key = null) + q.value + "_$key", getViewModelKey(qualifier = q, key = key) ) - - assertEquals( - key + scope.id, getViewModelKey(qualifier = null, scope = scope, key = key) - ) - assertEquals( - q.value + key + scope.id, getViewModelKey(qualifier = q, scope = scope, key = key) + key, getViewModelKey(qualifier = null, key = key) ) } } \ No newline at end of file diff --git a/projects/android/koin-android/src/test/java/org/koin/test/androidx/viewmodel/GetViewModelTest.kt b/projects/android/koin-android/src/test/java/org/koin/test/androidx/viewmodel/GetViewModelTest.kt index e12e7818d..5cc338b02 100644 --- a/projects/android/koin-android/src/test/java/org/koin/test/androidx/viewmodel/GetViewModelTest.kt +++ b/projects/android/koin-android/src/test/java/org/koin/test/androidx/viewmodel/GetViewModelTest.kt @@ -12,6 +12,7 @@ import org.koin.androidx.viewmodel.resolveViewModel import org.koin.core.annotation.KoinInternalApi import org.koin.core.qualifier.Qualifier import org.koin.core.scope.Scope +import org.koin.dsl.koinApplication @KoinInternalApi class GetViewModelTest { @@ -19,12 +20,12 @@ class GetViewModelTest { private val viewModelStore: ViewModelStore = mockk(relaxed = true) private val extras: CreationExtras = mockk() private val qualifier: Qualifier = mockk() - private val scope: Scope = mockk() + private val koin = koinApplication { }.koin + private val scope: Scope = koin.scopeRegistry.rootScope @Test fun `should return a ViewModel with a default Android key`() { val classToTest = ViewModel::class - every { scope.isRoot } returns true val firstViewModel = resolveViewModel( classToTest, @@ -47,10 +48,7 @@ class GetViewModelTest { val classToTest = ViewModel::class val qualifierName = "qualifier" val key = "key" - val scopeId = "scopeId" - every { scope.isRoot } returns false - every { scope.id } returns scopeId every { qualifier.value } returns qualifierName val viewModel = resolveViewModel( @@ -63,41 +61,17 @@ class GetViewModelTest { null ) - val koinVmKey = "$qualifierName$key$scopeId" + val koinVmKey = "${qualifierName}_$key" assertNotNull(viewModel) verify { viewModelStore[koinVmKey] } } - @Test - fun `should return a ViewModel considering only a non root scope identifier`() { - val classToTest = ViewModel::class - val scopeId = "scopeId" - - every { scope.isRoot } returns false - every { scope.id } returns scopeId - - val viewModel = resolveViewModel( - classToTest, - viewModelStore, - null, - extras, - null, - scope, - null - ) - - assertNotNull(viewModel) - verify { viewModelStore[scopeId] } - } - @Test fun `should return a ViewModel considering a key and a root scope identifier`() { val classToTest = ViewModel::class val key = "key" - every { scope.isRoot } returns true - val viewModel = resolveViewModel( classToTest, viewModelStore, @@ -117,7 +91,6 @@ class GetViewModelTest { val classToTest = ViewModel::class val qualifierValue = "qualifier" - every { scope.isRoot } returns true every { qualifier.value } returns qualifierValue val viewModel = resolveViewModel( diff --git a/projects/gradle.properties b/projects/gradle.properties index c754d2c3b..06aed45c5 100644 --- a/projects/gradle.properties +++ b/projects/gradle.properties @@ -8,8 +8,8 @@ org.gradle.parallel=true kotlin.code.style=official #Koin -koinVersion=3.5.4-rc-1 -koinComposeVersion=1.1.3-rc-1 +koinVersion=3.5.4-RC-1 +koinComposeVersion=1.1.3-RC-1 #Compose composeCompiler=1.5.7