diff --git a/projects/compose/koin-compose-viewmodel/build.gradle.kts b/projects/compose/koin-compose-viewmodel/build.gradle.kts index 034e6c606..d8ffdcfc9 100644 --- a/projects/compose/koin-compose-viewmodel/build.gradle.kts +++ b/projects/compose/koin-compose-viewmodel/build.gradle.kts @@ -22,6 +22,7 @@ kotlin { commonMain.dependencies { api(project(":compose:koin-compose")) api(libs.compose.viewmodel) + api(libs.compose.navigation) } } } diff --git a/projects/compose/koin-compose-viewmodel/src/commonMain/kotlin/org/koin/compose/NavViewModel.kt b/projects/compose/koin-compose-viewmodel/src/commonMain/kotlin/org/koin/compose/NavViewModel.kt new file mode 100644 index 000000000..36101c201 --- /dev/null +++ b/projects/compose/koin-compose-viewmodel/src/commonMain/kotlin/org/koin/compose/NavViewModel.kt @@ -0,0 +1,54 @@ +/* + * 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. + */ +@file:Suppress("DeprecatedCallableAddReplaceWith") + +package org.koin.compose + +import androidx.compose.runtime.Composable +import androidx.lifecycle.* +import androidx.lifecycle.viewmodel.CreationExtras +import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner +import org.koin.core.annotation.KoinExperimentalAPI +import org.koin.core.annotation.KoinInternalApi +import org.koin.core.parameter.ParametersDefinition +import org.koin.core.qualifier.Qualifier +import org.koin.core.scope.Scope + +/** + * Resolve ViewModel instance with Navigation NavBackStackEntry as extras parameters + * + * @param qualifier + * @param parameters + * + * @author Arnaud Giuliani + */ +@OptIn(KoinInternalApi::class) +@KoinExperimentalAPI +@Composable +inline fun koinNavViewModel( + qualifier: Qualifier? = null, + viewModelStoreOwner: ViewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) { + "No ViewModelStoreOwner was provided via LocalViewModelStoreOwner" + }, + key: String? = null, + extras: CreationExtras = defaultNavExtras(viewModelStoreOwner), + scope: Scope = currentKoinScope(), + noinline parameters: ParametersDefinition? = null, +): T { + return resolveViewModel( + T::class, viewModelStoreOwner.viewModelStore, key, extras, qualifier, scope, parameters + ) +} \ No newline at end of file diff --git a/projects/compose/koin-compose-viewmodel/src/commonMain/kotlin/org/koin/compose/NavViewModelInternals.kt b/projects/compose/koin-compose-viewmodel/src/commonMain/kotlin/org/koin/compose/NavViewModelInternals.kt new file mode 100644 index 000000000..ff9368a4d --- /dev/null +++ b/projects/compose/koin-compose-viewmodel/src/commonMain/kotlin/org/koin/compose/NavViewModelInternals.kt @@ -0,0 +1,62 @@ +/* + * 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. + */ +@file:Suppress("DeprecatedCallableAddReplaceWith") + +package org.koin.compose + +import androidx.compose.runtime.Composable +import androidx.core.bundle.Bundle +import androidx.lifecycle.* +import androidx.lifecycle.viewmodel.CreationExtras +import androidx.lifecycle.viewmodel.MutableCreationExtras +import androidx.navigation.NavBackStackEntry +import androidx.savedstate.SavedStateRegistryOwner +import org.koin.core.annotation.KoinInternalApi + +/** + * Resolve ViewModel instance + * + * @param qualifier + * @param parameters + * + * @author Arnaud Giuliani + */ +@OptIn(KoinInternalApi::class) +@Composable +fun defaultNavExtras(viewModelStoreOwner: ViewModelStoreOwner): CreationExtras = when { + //TODO To be fully verified + viewModelStoreOwner is NavBackStackEntry && viewModelStoreOwner.arguments != null -> viewModelStoreOwner.arguments?.toExtras(viewModelStoreOwner) ?: CreationExtras.Empty + viewModelStoreOwner is HasDefaultViewModelProviderFactory -> viewModelStoreOwner.defaultViewModelCreationExtras + else -> CreationExtras.Empty +} + +/** + * Convert current Bundle to CreationExtras + * @param viewModelStoreOwner + */ +@KoinInternalApi +fun Bundle.toExtras(viewModelStoreOwner: ViewModelStoreOwner): CreationExtras? { + return if (keySet().isEmpty()) null + else { + runCatching { + MutableCreationExtras().also { extras -> + extras[DEFAULT_ARGS_KEY] = this + extras[VIEW_MODEL_STORE_OWNER_KEY] = viewModelStoreOwner + extras[SAVED_STATE_REGISTRY_OWNER_KEY] = viewModelStoreOwner as SavedStateRegistryOwner + } + }.getOrNull() + } +} \ No newline at end of file diff --git a/projects/gradle/libs.versions.toml b/projects/gradle/libs.versions.toml index 2e6144447..c13f589bc 100644 --- a/projects/gradle/libs.versions.toml +++ b/projects/gradle/libs.versions.toml @@ -19,7 +19,8 @@ androidx-navigation = "2.7.7" # Compose # /!\ Compose compiler in gradle.properties /!\ composeJB = "1.6.10-rc02" -composeLifecycke = "2.8.0-rc02" +composeLifecycle = "2.8.0-rc02" +composeNavigation = "2.7.0-alpha05" composeJetpackRuntime = "1.6.7" # Test stately = "2.0.6" @@ -59,7 +60,8 @@ ktor-testHost = { module = "io.ktor:ktor-server-test-host", version.ref = "ktor" ktor-slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } # Compose compose-jb = { module = "org.jetbrains.compose.runtime:runtime", version.ref = "composeJB" } -compose-viewmodel = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "composeLifecycke" } +compose-viewmodel = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "composeLifecycle" } +compose-navigation = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "composeNavigation" } androidx-composeRuntime = { module = "androidx.compose.runtime:runtime", version.ref = "composeJetpackRuntime" } androidx-composeViewModel = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" } androidx-composeNavigation = { module = "androidx.navigation:navigation-compose", version.ref = "androidx-navigation" }