diff --git a/docs/reference/koin-compose/navigation3.md b/docs/reference/koin-compose/navigation3.md new file mode 100644 index 000000000..4f8ce71eb --- /dev/null +++ b/docs/reference/koin-compose/navigation3.md @@ -0,0 +1,382 @@ +--- +title: Navigation 3 Integration +--- + +Koin provides integration with [AndroidX Navigation 3](https://developer.android.com/jetpack/androidx/releases/navigation3) to help you build type-safe navigation graphs with dependency injection. + +## Setup + +Add the Navigation 3 integration dependency to your project: + +### For Multiplatform Projects + +```kotlin +commonMain.dependencies { + implementation("io.insert-koin:koin-compose-navigation3:$koin_version") +} +``` + +### For Android-only Projects + +```kotlin +dependencies { + implementation("io.insert-koin:koin-compose-navigation3:$koin_version") +} +``` + +:::note +This is an **experimental API** marked with `@KoinExperimentalAPI`. The Navigation 3 library is currently in alpha. +::: + +## Key Concepts + +Navigation 3 integration introduces three main components: + +- **`EntryProvider`** - A function that maps route objects to navigation entries +- **`EntryProviderInstaller`** - A function that registers navigation entries in Koin +- **`navigation`** - A DSL function to declare navigation destinations in Koin modules + +## Declaring Navigation Entries + +Use the `navigation()` DSL function in your Koin modules to declare navigation destinations: + +### Basic Module-level Navigation + +```kotlin +val appModule = module { + single { Navigator() } + viewModel { HomeViewModel() } + viewModel { DetailViewModel() } + + // Declare navigation entries + navigation { route -> + HomeScreen(viewModel = koinViewModel()) + } + + navigation { route -> + DetailScreen( + viewModel = koinViewModel(), + itemId = route.itemId + ) + } +} + +// Define your routes +@Serializable +object HomeRoute + +@Serializable +data class DetailRoute(val itemId: String) +``` + +### Scoped Navigation + +You can also declare navigation entries within Koin scopes, useful for scoping ViewModels and dependencies to specific parts of your navigation graph: + +```kotlin +val appModule = module { + // Activity scope + activityRetainedScope { + scoped { Navigator() } + viewModel { ProfileViewModel() } + + navigation { route -> + ProfileScreen(viewModel = koinViewModel()) + } + } + + // also with custom scope + // Activity scope + scope { + scoped { Navigator() } + viewModel { ProfileViewModel() } + + navigation { route -> + ProfileScreen(viewModel = koinViewModel()) + } + } +} +``` + +## Using Navigation in Compose + +### Retrieving the EntryProvider + +Use the `koinEntryProvider()` composable function to retrieve the aggregated navigation entries from Koin: + +```kotlin +@Composable +fun App() { + val entryProvider = koinEntryProvider() + + NavigationHost( + entryProvider = entryProvider, + startDestination = HomeRoute + ) { + // Navigation setup + } +} +``` + +### With Custom Scope + +You can provide a specific Koin scope to retrieve entries from: + +```kotlin +@Composable +fun CheckoutFlow() { + val checkoutScope = rememberKoinScope(named("checkout")) + val entryProvider = koinEntryProvider(scope = checkoutScope.value) + + NavigationHost( + entryProvider = entryProvider, + startDestination = CheckoutRoute.Start + ) { + // Checkout navigation + } +} +``` + +## Android-specific: ComponentCallbacks Extensions + +For Android applications, you can use the `ComponentCallbacks` extensions to retrieve the entry provider from Activities or Fragments: + +```kotlin +class MainActivity : ComponentActivity() { + + // Lazy initialization + private val entryProvider by entryProvider() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + NavigationHost( + entryProvider = entryProvider, + startDestination = HomeRoute + ) + } + } +} +``` + +Or use eager initialization: + +```kotlin +class MainActivity : ComponentActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val entryProvider = getEntryProvider() + setContent { + NavigationHost( + entryProvider = entryProvider, + startDestination = HomeRoute + ) + } + } +} +``` + +## Accessing Route Parameters + +Navigation 3 uses type-safe routes. Access route parameters directly from the route object: + +```kotlin +@Serializable +data class DetailRoute( + val itemId: String, + val fromSearch: Boolean = false +) + +val appModule = module { + navigation { route -> + DetailScreen( + itemId = route.itemId, + fromSearch = route.fromSearch, + viewModel = koinViewModel() + ) + } +} +``` + +## Combining with Koin Scopes + +Navigation entries can leverage Koin's scoping capabilities: + +```kotlin +val appModule = module { + // Define a scope archetype + scope { + scoped { UserSession() } + + viewModel { params -> + UserProfileViewModel( + userId = params.get(), + session = get() + ) + } + + navigation { route -> + UserProfileScreen( + viewModel = koinViewModel { parametersOf(route.userId) } + ) + } + } +} +``` + +## Complete Example + +Here's a complete example showing a typical navigation setup: + +```kotlin +// Define routes +@Serializable +object HomeRoute + +@Serializable +object ProfileRoute + +@Serializable +data class DetailRoute(val id: String) + +@Serializable +data class SettingsRoute(val section: String? = null) + +// Koin module +val navigationModule = module { + // Shared dependencies + single { ApiClient() } + + // ViewModels + viewModel { HomeViewModel(get()) } + viewModel { ProfileViewModel(get()) } + viewModel { params -> DetailViewModel(get(), params.get()) } + viewModel { SettingsViewModel() } + + // Navigation entries + navigation { route -> + HomeScreen(viewModel = koinViewModel()) + } + + navigation { route -> + ProfileScreen(viewModel = koinViewModel()) + } + + navigation { route -> + DetailScreen( + id = route.id, + viewModel = koinViewModel { parametersOf(route.id) } + ) + } + + navigation { route -> + SettingsScreen( + initialSection = route.section, + viewModel = koinViewModel() + ) + } +} + +// Compose app +@Composable +fun App() { + val entryProvider = koinEntryProvider() + val navController = rememberNavController() + + NavigationHost( + navController = navController, + entryProvider = entryProvider, + startDestination = HomeRoute + ) { + // Navigation configuration + } +} + +// Main entry point +fun main() = application { + KoinApplication(application = { + modules(navigationModule) + }) { + App() + } +} +``` + +## Migration from Navigation 2.x + +If you're migrating from the Navigation 2.x integration: + +### Before (Navigation 2.x) +```kotlin +NavHost(navController, startDestination = "home") { + composable("home") { + HomeScreen(viewModel = koinViewModel()) + } + composable("detail/{id}") { backStackEntry -> + val id = backStackEntry.arguments?.getString("id") + DetailScreen(id = id, viewModel = koinViewModel()) + } +} +``` + +### After (Navigation 3) +```kotlin +// Define routes +@Serializable +object HomeRoute + +@Serializable +data class DetailRoute(val id: String) + +// Declare in module +val appModule = module { + navigation { route -> + HomeScreen(viewModel = koinViewModel()) + } + + navigation { route -> + DetailScreen(id = route.id, viewModel = koinViewModel()) + } +} + +// Use in app +@Composable +fun App() { + val entryProvider = koinEntryProvider() + val navController = rememberNavController() + + NavigationHost( + navController = navController, + entryProvider = entryProvider, + startDestination = HomeRoute + ) +} +``` + +## API Reference + +### DSL Functions + +- `Module.navigation(definition)` - Declares a singleton navigation entry +- `ScopeDSL.navigation(definition)` - Declares a scoped navigation entry + +### Composable Functions + +- `koinEntryProvider(scope)` - Retrieves entry provider from given Koin scope + +### Android Extensions + +- `ComponentCallbacks.entryProvider()` - Lazy entry provider initialization +- `ComponentCallbacks.getEntryProvider()` - Eager entry provider initialization + +## Limitations + +- Navigation 3 is currently in **alpha** - API may change +- Type-safe navigation requires Kotlin serialization plugin +- Some advanced Navigation 2.x features may not be available yet + +## Resources + +- [AndroidX Navigation 3 Documentation](https://developer.android.com/jetpack/androidx/releases/navigation3) +- [Koin Compose Documentation](/docs/reference/koin-compose/compose) diff --git a/examples/gradle/versions.gradle b/examples/gradle/versions.gradle index 788a3ebf9..e38ec0ffd 100644 --- a/examples/gradle/versions.gradle +++ b/examples/gradle/versions.gradle @@ -2,7 +2,7 @@ ext { // Kotlin kotlin_version = '2.2.20' // Koin Versions - koin_version = '4.2.0-2296' + koin_version = '4.2.0-2286-1' koin_android_version = koin_version koin_compose_version = koin_version diff --git a/projects/android/koin-android/src/test/java/org/koin/core/scope/ActivityScopeArchetypeTest.kt b/projects/android/koin-android/src/test/java/org/koin/core/scope/ActivityScopeArchetypeTest.kt index 40468efe4..502b74264 100644 --- a/projects/android/koin-android/src/test/java/org/koin/core/scope/ActivityScopeArchetypeTest.kt +++ b/projects/android/koin-android/src/test/java/org/koin/core/scope/ActivityScopeArchetypeTest.kt @@ -20,6 +20,7 @@ import org.koin.core.component.getScopeId import org.koin.core.context.startKoin import org.koin.core.context.stopKoin import org.koin.core.logger.Level +import org.koin.core.qualifier.named import org.koin.dsl.module import org.koin.mp.KoinPlatform import org.koin.test.android.scope.ScopeArchetypeDSLTest.MyFactoryClass @@ -233,4 +234,28 @@ class ActivityScopeArchetypeTest { val rootValue = activityScope.get() assertEquals("root-value", rootValue) } + + private data class Entry(val value : String) + + @Test + fun `get all from archetype`() { + + val koin = KoinPlatform.getKoin() + val testModule = module { + single { "root-value" } + activityScope { + scoped(named("entry_1")) { Entry("entry_1") } + scoped(named("entry_2")) { Entry("entry_2") } + scoped(named("entry_3")) { Entry("entry_3") } + } + } + koin.loadModules(listOf(testModule)) + + val activity = FakeActivity() + val activityScope = activity.scope + + val entries = activityScope.getAll() + assertEquals(3,entries.size) + assertEquals((1..3).map { Entry("entry_$it") },entries) + } } diff --git a/projects/bom/koin-bom/build.gradle.kts b/projects/bom/koin-bom/build.gradle.kts index 349821a2b..7d057a8d1 100644 --- a/projects/bom/koin-bom/build.gradle.kts +++ b/projects/bom/koin-bom/build.gradle.kts @@ -31,6 +31,7 @@ dependencies { api(project(":android:koin-dagger-bridge")) api(project(":compose:koin-compose")) + api(project(":compose:koin-compose-navigation3")) api(project(":compose:koin-compose-viewmodel")) api(project(":compose:koin-compose-viewmodel-navigation")) api(project(":compose:koin-androidx-compose")) diff --git a/projects/compose/koin-compose-navigation3/build.gradle.kts b/projects/compose/koin-compose-navigation3/build.gradle.kts new file mode 100644 index 000000000..0a536efdd --- /dev/null +++ b/projects/compose/koin-compose-navigation3/build.gradle.kts @@ -0,0 +1,70 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + +plugins { + alias(libs.plugins.androidLibrary) + alias(libs.plugins.kotlinMultiplatform) + alias(libs.plugins.jetbrainsCompose) + alias(libs.plugins.compose.compiler) +} + +val koinVersion: String by project +version = koinVersion + +kotlin { + androidTarget { + publishLibraryVariants("release") + compilerOptions { + jvmTarget.set(JvmTarget.JVM_11) + } + } + + jvm { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_11) + } + } + + js(IR) { + nodejs() + browser() + binaries.executable() + } + + wasmJs { + nodejs() + binaries.executable() + } + + iosX64() + iosArm64() + iosSimulatorArm64() + macosX64() + macosArm64() + + sourceSets { + androidMain.dependencies { + api(project(":android:koin-android")) + } + commonMain.dependencies { + api(project(":compose:koin-compose")) + implementation(libs.androidx.navigation3.runtime) + } + } +} + +val androidCompileSDK: String by project +val androidMinSDK : String by project + +android { + namespace = "org.koin.compose.navigation3" + compileSdk = androidCompileSDK.toInt() + defaultConfig { + minSdk = androidMinSDK.toInt() + } +} + +tasks.withType().configureEach { + args.add("--ignore-engines") +} + +apply(from = file("../../gradle/publish.gradle.kts")) diff --git a/projects/compose/koin-compose-navigation3/src/androidMain/kotlin/org/koin/androidx/compose/navigation3/ComponentCallbacksExt.kt b/projects/compose/koin-compose-navigation3/src/androidMain/kotlin/org/koin/androidx/compose/navigation3/ComponentCallbacksExt.kt new file mode 100644 index 000000000..38f323890 --- /dev/null +++ b/projects/compose/koin-compose-navigation3/src/androidMain/kotlin/org/koin/androidx/compose/navigation3/ComponentCallbacksExt.kt @@ -0,0 +1,47 @@ +package org.koin.androidx.compose.navigation3 + +import android.content.ComponentCallbacks +import androidx.navigation3.runtime.entryProvider +import org.koin.android.ext.android.getKoinScope +import org.koin.compose.navigation3.EntryProvider +import org.koin.compose.navigation3.getEntryProvider +import org.koin.core.annotation.KoinExperimentalAPI +import org.koin.core.annotation.KoinInternalApi + +/** + * Creates a lazy [EntryProvider] by collecting all [EntryProviderInstaller] instances from the Koin scope + * associated with this [ComponentCallbacks]. + * + * This function aggregates all registered navigation entry providers and creates a composite provider + * that delegates to all registered installers. + * + * @param mode The lazy initialization mode. Defaults to [LazyThreadSafetyMode.SYNCHRONIZED] for thread-safe lazy initialization. + * @return A lazy delegate that provides the [EntryProvider] when first accessed + * + * @see getEntryProvider for eager initialization + * @See ComponentCallbacks + */ +@KoinExperimentalAPI +@OptIn(KoinInternalApi::class) +fun ComponentCallbacks.entryProvider( + mode: LazyThreadSafetyMode = LazyThreadSafetyMode.SYNCHRONIZED, +) : Lazy = lazy(mode) { getEntryProvider() } + +/** + * Retrieves an [EntryProvider] by collecting all [EntryProviderInstaller] instances from the Koin scope + * associated with this [ComponentCallbacks]. + * + * This function aggregates all registered navigation entry providers and creates a composite provider + * that delegates to all registered installers. Unlike [entryProvider], this function eagerly initializes + * the provider. + * + * @return An [EntryProvider] that combines all registered navigation entries + * + * @see entryProvider for lazy initialization + * @See ComponentCallbacks + */ +@KoinExperimentalAPI +@OptIn(KoinInternalApi::class) +fun ComponentCallbacks.getEntryProvider() : EntryProvider { + return getKoinScope().getEntryProvider() +} \ No newline at end of file diff --git a/projects/compose/koin-compose-navigation3/src/commonMain/kotlin/org/koin/compose/navigation3/EntryProvider.kt b/projects/compose/koin-compose-navigation3/src/commonMain/kotlin/org/koin/compose/navigation3/EntryProvider.kt new file mode 100644 index 000000000..cc245815c --- /dev/null +++ b/projects/compose/koin-compose-navigation3/src/commonMain/kotlin/org/koin/compose/navigation3/EntryProvider.kt @@ -0,0 +1,76 @@ +/* + * Copyright 2017-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.koin.compose.navigation3 + +import androidx.compose.runtime.Composable +import androidx.navigation3.runtime.NavEntry +import androidx.navigation3.runtime.entryProvider +import org.koin.compose.LocalKoinScope +import org.koin.core.annotation.KoinExperimentalAPI +import org.koin.core.annotation.KoinInternalApi +import org.koin.core.scope.Scope + +/** + * Type alias for a function that provides navigation entries based on a route/destination. + * + * An [EntryProvider] takes any route object and returns the corresponding [NavEntry] that + * contains the composable content for that destination. This enables type-safe navigation + * with Koin dependency injection. + * + * @see EntryProviderInstaller for defining navigation entries in Koin modules + */ +@KoinExperimentalAPI +typealias EntryProvider = (Any) -> NavEntry + +@KoinExperimentalAPI +internal fun Scope.getEntryProvider() : EntryProvider { + val entries = getAll() + val entryProvider: (Any) -> NavEntry = entryProvider { + entries.forEach { builder -> this.builder() } + } + return entryProvider +} + +/** + * Composable function that retrieves an [EntryProvider] from the current or specified Koin scope. + * + * This function collects all registered [EntryProviderInstaller] instances from the Koin scope + * and aggregates them into a single [EntryProvider] that can be used with Navigation 3. + * By default, it uses the scope from [LocalKoinScope], but a custom scope can be provided. + * + * Example usage: + * ```kotlin + * @Composable + * fun MyApp() { + * val entryProvider = koinEntryProvider() + * NavigationHost(entryProvider) { + * // Your navigation setup + * } + * } + * ``` + * + * @param scope The Koin scope to retrieve navigation entries from. Defaults to [LocalKoinScope.current]. + * @return An [EntryProvider] that combines all registered navigation entries from the scope + * + * @see EntryProvider for the navigation entry provider type + * @see EntryProviderInstaller for defining navigation entries in Koin modules + */ +@OptIn(KoinInternalApi::class) +@KoinExperimentalAPI +@Composable +fun koinEntryProvider(scope : Scope = LocalKoinScope.current.getValue()) : EntryProvider { + return scope.getEntryProvider() +} \ No newline at end of file diff --git a/projects/compose/koin-compose-navigation3/src/commonMain/kotlin/org/koin/compose/navigation3/EntryProviderInstaller.kt b/projects/compose/koin-compose-navigation3/src/commonMain/kotlin/org/koin/compose/navigation3/EntryProviderInstaller.kt new file mode 100644 index 000000000..5e64440ef --- /dev/null +++ b/projects/compose/koin-compose-navigation3/src/commonMain/kotlin/org/koin/compose/navigation3/EntryProviderInstaller.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2017-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.koin.compose.navigation3 + +import androidx.navigation3.runtime.EntryProviderScope +import org.koin.core.annotation.KoinExperimentalAPI + +/** + * Type alias for a function that installs navigation entries into an [EntryProviderScope]. + * + * An [EntryProviderInstaller] is an extension function on [EntryProviderScope] that allows + * registering navigation entries (routes and their composable content) within the scope. + * These installers are collected by Koin and aggregated to build the complete navigation graph. + * + * Installers are typically created using the [navigation][org.koin.dsl.navigation3.navigation] + * DSL function in Koin modules. + * + * @see EntryProvider for the aggregated navigation entry provider + */ +@KoinExperimentalAPI +typealias EntryProviderInstaller = EntryProviderScope.() -> Unit diff --git a/projects/compose/koin-compose-navigation3/src/commonMain/kotlin/org/koin/dsl/navigation3/ModuleExt.kt b/projects/compose/koin-compose-navigation3/src/commonMain/kotlin/org/koin/dsl/navigation3/ModuleExt.kt new file mode 100644 index 000000000..f0996ef9e --- /dev/null +++ b/projects/compose/koin-compose-navigation3/src/commonMain/kotlin/org/koin/dsl/navigation3/ModuleExt.kt @@ -0,0 +1,107 @@ +/* + * Copyright 2017-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.koin.dsl.navigation3 + +import androidx.compose.runtime.Composable +import org.koin.compose.navigation3.EntryProviderInstaller +import org.koin.core.annotation.KoinExperimentalAPI +import org.koin.core.annotation.KoinInternalApi +import org.koin.core.definition.KoinDefinition +import org.koin.core.module.KoinDslMarker +import org.koin.core.module.Module +import org.koin.core.module._scopedInstanceFactory +import org.koin.core.module._singleInstanceFactory +import org.koin.core.qualifier.named +import org.koin.core.scope.Scope +import org.koin.dsl.ScopeDSL + +/** + * Declares a scoped navigation entry within a Koin scope DSL. + * + * This function registers a composable navigation destination that is scoped to a specific Koin scope, + * allowing access to scoped dependencies within the composable. The route type [T] is used as both + * the navigation destination identifier and a qualifier for the entry provider. + * + * Example usage: + * ```kotlin + * activityScope { + * viewModel { MyViewModel() } + * navigation { route -> + * MyScreen(viewModel = koinViewModel()) + * } + * } + * ``` + * + * @param T The type representing the navigation route/destination + * @param definition A composable function that receives the [Scope] and route instance [T] to render the destination + * @return A [KoinDefinition] for the created [EntryProviderInstaller] + * + * @see Module.navigation for module-level navigation entries + */ +@KoinExperimentalAPI +@KoinDslMarker +@OptIn(KoinInternalApi::class) +inline fun ScopeDSL.navigation( + noinline definition: @Composable Scope.(T) -> Unit, +): KoinDefinition { + val def = _scopedInstanceFactory(named(), { + val scope = this + { + entry(content = { t -> definition(scope, t) }) + } + }, scopeQualifier) + module.indexPrimaryType(def) + return KoinDefinition(module, def) +} + +/** + * Declares a singleton navigation entry within a Koin module. + * + * This function registers a composable navigation destination as a singleton in the Koin module, + * allowing access to module-level dependencies within the composable. The route type [T] is used + * as both the navigation destination identifier and a qualifier for the entry provider. + * + * Example usage: + * ```kotlin + * module { + * viewModel { MyViewModel() } + * navigation { route -> + * HomeScreen(myViewModel = koinViewModel()) + * } + * } + * ``` + * + * @param T The type representing the navigation route/destination + * @param definition A composable function that receives the [Scope] and route instance [T] to render the destination + * @return A [KoinDefinition] for the created [EntryProviderInstaller] + * + * @see ScopeDSL.navigation for scope-level navigation entries + */ +@KoinExperimentalAPI +@KoinDslMarker +@OptIn(KoinInternalApi::class) +inline fun Module.navigation( + noinline definition: @Composable Scope.(T) -> Unit, +): KoinDefinition { + val def = _singleInstanceFactory(named(), { + val scope = this + { + entry(content = { t -> definition(scope, t) }) + } + }) + indexPrimaryType(def) + return KoinDefinition(this, def) +} diff --git a/projects/core/koin-core/build.gradle.kts b/projects/core/koin-core/build.gradle.kts index 4182e0844..e65806e97 100644 --- a/projects/core/koin-core/build.gradle.kts +++ b/projects/core/koin-core/build.gradle.kts @@ -6,6 +6,8 @@ plugins { } kotlin { + jvmToolchain(8) + jvm() js(IR) { diff --git a/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/registry/InstanceRegistry.kt b/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/registry/InstanceRegistry.kt index c3c34babd..060bc31bb 100644 --- a/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/registry/InstanceRegistry.kt +++ b/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/registry/InstanceRegistry.kt @@ -192,7 +192,9 @@ class InstanceRegistry(val _koin: Koin) { internal fun getAll(clazz: KClass<*>, instanceContext: ResolutionContext): List { return _instances.values .filter { factory -> - factory.beanDefinition.scopeQualifier == instanceContext.scope.scopeQualifier && + (factory.beanDefinition.scopeQualifier == instanceContext.scope.scopeQualifier || + factory.beanDefinition.scopeQualifier == instanceContext.scope.scopeArchetype + ) && (factory.beanDefinition.primaryType == clazz || factory.beanDefinition.secondaryTypes.contains(clazz)) } .distinct() diff --git a/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/scope/Scope.kt b/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/scope/Scope.kt index 9ef1d9d9f..ee54093dd 100644 --- a/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/scope/Scope.kt +++ b/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/scope/Scope.kt @@ -396,6 +396,7 @@ class Scope( */ fun getAll(clazz: KClass<*>): List { val context = ResolutionContext(_koin.logger, this, clazz) + context.scopeArchetype = this.scopeArchetype return _koin.instanceRegistry.getAll(clazz, context) + linkedScopes.flatMap { scope -> scope.getAll(clazz) } } diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/core/GetAllTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/core/GetAllTest.kt new file mode 100644 index 000000000..f9b4b1232 --- /dev/null +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/core/GetAllTest.kt @@ -0,0 +1,50 @@ +package org.koin.core + +import org.koin.core.qualifier.named +import org.koin.dsl.koinApplication +import org.koin.dsl.module +import kotlin.test.Test +import kotlin.test.assertEquals + +class GetAllTest { + + data class Entry(val value : String) + + @Test + fun testMe(){ + val koin = koinApplication { + modules( + module { + single(named("entry_1")) { Entry("entry_1") } + single(named("entry_2")) { Entry("entry_2") } + single(named("entry_3")) { Entry("entry_3") } + } + ) + }.koin + + val entries = koin.getAll() + assertEquals(3,entries.size) + } + + data class MyScope(val name : String) + + //TODO GetAll sur scope archetype - fail? + + @Test + fun testMeSCope(){ + val koin = koinApplication { + modules( + module { + scope { + scoped(named("entry_1")) { Entry("entry_1") } + scoped(named("entry_2")) { Entry("entry_2") } + scoped(named("entry_3")) { Entry("entry_3") } + } + } + ) + }.koin + val scope = koin.createScope() + val entries = scope.getAll() + assertEquals(3,entries.size) + } +} \ No newline at end of file diff --git a/projects/gradle.properties b/projects/gradle.properties index 0f1711552..2a8ffe9b8 100644 --- a/projects/gradle.properties +++ b/projects/gradle.properties @@ -11,7 +11,7 @@ kotlin.incremental.multiplatform=true kotlin.code.style=official #Koin -koinVersion=4.2.0-2296 +koinVersion=4.2.0-2286-1 #Compose org.jetbrains.compose.experimental.jscanvas.enabled=true @@ -20,6 +20,7 @@ org.jetbrains.compose.experimental.macos.enabled=true #Android android.useAndroidX=true androidMinSDK=21 +androidComposeMinSDK=23 androidCompileSDK=36 android.nonTransitiveRClass=true diff --git a/projects/gradle/libs.versions.toml b/projects/gradle/libs.versions.toml index 326fb490f..7f150642a 100644 --- a/projects/gradle/libs.versions.toml +++ b/projects/gradle/libs.versions.toml @@ -26,6 +26,7 @@ jb-lifecycle = "2.9.5" # Navigation androidx-navigation = "2.9.5" # Keep in sync with "jb-navigation" +androidx-navigation3 = "1.0.0-beta01" jb-navigation = "2.9.1" # Compose @@ -71,6 +72,7 @@ androidx-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", ve androidx-navigation = { module = "androidx.navigation:navigation-fragment-ktx", version.ref = "androidx-navigation" } androidx-workmanager = { module = "androidx.work:work-runtime-ktx", version.ref = "androidx-workmanager" } androidx-startup = {module ="androidx.startup:startup-runtime", version.ref = "androidx-startup" } +androidx-navigation3-runtime = { module = "androidx.navigation3:navigation3-runtime", version.ref = "androidx-navigation3" } # Ktor ktor-core = { module = "io.ktor:ktor-server-core", version.ref = "ktor" } diff --git a/projects/settings.gradle.kts b/projects/settings.gradle.kts index ba91a90f3..65c74f6fb 100644 --- a/projects/settings.gradle.kts +++ b/projects/settings.gradle.kts @@ -36,7 +36,6 @@ include( ":core:koin-core", ":core:koin-core-coroutines", ":core:koin-core-viewmodel", -// ":core:koin-core-viewmodel-navigation", ":core:koin-test", ":core:koin-test-coroutines", ":core:koin-test-junit4", @@ -60,6 +59,7 @@ include( ":android:koin-androidx-startup", // Compose ":compose:koin-compose", + ":compose:koin-compose-navigation3", ":compose:koin-compose-viewmodel", ":compose:koin-compose-viewmodel-navigation", ":compose:koin-androidx-compose",