From b0af4f50c8ccbb94e3a6c5a3f078fb711c55ac55 Mon Sep 17 00:00:00 2001 From: PGochachko <39626534+PGochachko@users.noreply.github.com> Date: Thu, 2 Mar 2023 00:11:31 +0300 Subject: [PATCH 01/13] Update ViewModelState.swift Using weak self into stateFlow subscribe for correct clearing memory --- .../apple/xcode/mokoMvvmFlowSwiftUI/ViewModelState.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mvvm-flow/apple/xcode/mokoMvvmFlowSwiftUI/ViewModelState.swift b/mvvm-flow/apple/xcode/mokoMvvmFlowSwiftUI/ViewModelState.swift index 696151b..1764ba7 100644 --- a/mvvm-flow/apple/xcode/mokoMvvmFlowSwiftUI/ViewModelState.swift +++ b/mvvm-flow/apple/xcode/mokoMvvmFlowSwiftUI/ViewModelState.swift @@ -21,10 +21,10 @@ public extension ObservableObject where Self: ViewModel { var disposable: DisposableHandle? = nil - disposable = stateFlow.subscribe(onCollect: { value in + disposable = stateFlow.subscribe(onCollect: { [weak self] value in if !equals(lastValue, value) { lastValue = value - self.objectWillChange.send() + self?.objectWillChange.send() disposable?.dispose() } }) From a286ef66202f7587d735805bd3b639c38ae01d71 Mon Sep 17 00:00:00 2001 From: PGochachko <39626534+PGochachko@users.noreply.github.com> Date: Thu, 2 Mar 2023 00:14:49 +0300 Subject: [PATCH 02/13] Update ViewModelStateNullable.swift Using weak self into stateFlow subscribe for correct clearing memory --- .../xcode/mokoMvvmFlowSwiftUI/ViewModelStateNullable.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mvvm-flow/apple/xcode/mokoMvvmFlowSwiftUI/ViewModelStateNullable.swift b/mvvm-flow/apple/xcode/mokoMvvmFlowSwiftUI/ViewModelStateNullable.swift index 688acd0..d03737d 100644 --- a/mvvm-flow/apple/xcode/mokoMvvmFlowSwiftUI/ViewModelStateNullable.swift +++ b/mvvm-flow/apple/xcode/mokoMvvmFlowSwiftUI/ViewModelStateNullable.swift @@ -19,10 +19,10 @@ extension ObservableObject where Self: ViewModel { var disposable: DisposableHandle? = nil - disposable = stateFlow.subscribe(onCollect: { value in + disposable = stateFlow.subscribe(onCollect: { [weak self] value in if !equals(lastValue, value) { lastValue = value - self.objectWillChange.send() + self?.objectWillChange.send() disposable?.dispose() } }) From 0e8e18a5c2bf97619d72e47450c1a2fd55cf07ad Mon Sep 17 00:00:00 2001 From: PGochachko <39626534+PGochachko@users.noreply.github.com> Date: Thu, 2 Mar 2023 00:15:53 +0300 Subject: [PATCH 03/13] Update ViewModelBinding.swift Using weak self into stateFlow subscribe for correct clearing memory --- .../apple/xcode/mokoMvvmFlowSwiftUI/ViewModelBinding.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mvvm-flow/apple/xcode/mokoMvvmFlowSwiftUI/ViewModelBinding.swift b/mvvm-flow/apple/xcode/mokoMvvmFlowSwiftUI/ViewModelBinding.swift index 9feccf9..1a0fa55 100644 --- a/mvvm-flow/apple/xcode/mokoMvvmFlowSwiftUI/ViewModelBinding.swift +++ b/mvvm-flow/apple/xcode/mokoMvvmFlowSwiftUI/ViewModelBinding.swift @@ -22,10 +22,10 @@ public extension ObservableObject where Self: ViewModel { var disposable: DisposableHandle? = nil - disposable = stateFlow.subscribe(onCollect: { value in + disposable = stateFlow.subscribe(onCollect: { [weak self] value in if !equals(lastValue, value) { lastValue = value - self.objectWillChange.send() + self?.objectWillChange.send() disposable?.dispose() } }) From b0dc5ef0d10fd5d896def14c094e8e22e6f60da2 Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Sun, 2 Apr 2023 19:34:56 +0600 Subject: [PATCH 04/13] #230 update moko-resources and other dependencies --- gradle.properties | 4 +-- gradle/libs.versions.toml | 29 +++++++++---------- gradle/wrapper/gradle-wrapper.properties | 4 ++- mvvm-build-logic/settings.gradle.kts | 4 +-- .../kotlin/kmm-library-convention.gradle.kts | 4 +-- .../dev/icerock/moko/mvvm/ViewModelFactory.kt | 13 ++++----- mvvm-databinding/build.gradle.kts | 2 +- mvvm-livedata-glide/build.gradle.kts | 1 - mvvm-livedata-swiperefresh/build.gradle.kts | 1 - mvvm-viewbinding/build.gradle.kts | 2 +- settings.gradle.kts | 3 +- 11 files changed, 32 insertions(+), 35 deletions(-) diff --git a/gradle.properties b/gradle.properties index 48b050a..e0b158f 100755 --- a/gradle.properties +++ b/gradle.properties @@ -3,9 +3,7 @@ org.gradle.configureondemand=false org.gradle.parallel=true kotlin.code.style=official -kotlin.native.enableDependencyPropagation=false -kotlin.mpp.enableGranularSourceSetsMetadata=true -kotlin.mpp.enableCompatibilityMetadataVariant=true +kotlin.mpp.androidSourceSetLayoutVersion=2 android.useAndroidX=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 93eb389..a6ad508 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,19 +1,18 @@ [versions] -kotlinVersion = "1.6.10" -androidLifecycleVersion = "2.2.0" -coroutinesVersion = "1.6.0-native-mt" -mokoResourcesVersion = "0.18.0" +kotlinVersion = "1.8.10" +androidLifecycleVersion = "2.6.1" +coroutinesVersion = "1.6.4" +mokoResourcesVersion = "0.21.1" mokoTestVersion = "0.6.1" -mokoMvvmVersion = "0.15.0" -mokoKSwiftVersion = "0.4.0" -composeVersion = "1.1.1" -composeJetBrainsVersion = "1.1.1" +mokoMvvmVersion = "0.16.0" +mokoKSwiftVersion = "0.6.1" +composeVersion = "1.4.0" +composeJetBrainsVersion = "1.3.1" [libraries] # android -appCompat = { module = "androidx.appcompat:appcompat", version = "1.2.0" } -material = { module = "com.google.android.material:material", version = "1.2.1" } -lifecycle = { module = "androidx.lifecycle:lifecycle-extensions", version.ref = "androidLifecycleVersion" } +appCompat = { module = "androidx.appcompat:appcompat", version = "1.6.1" } +material = { module = "com.google.android.material:material", version = "1.8.0" } lifecycleKtx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidLifecycleVersion" } androidViewModel = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidLifecycleVersion" } androidLiveData = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "androidLifecycleVersion" } @@ -35,14 +34,14 @@ mokoKSwift = { module = "dev.icerock.moko:kswift-runtime", version.ref = "mokoKS # tests kotlinTestJUnit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlinVersion" } -androidCoreTesting = { module = "androidx.arch.core:core-testing", version = "2.1.0" } +androidCoreTesting = { module = "androidx.arch.core:core-testing", version = "2.2.0" } # gradle plugins dokkaGradlePlugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "kotlinVersion" } kotlinGradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlinVersion" } -mobileMultiplatformGradlePlugin = { module = "dev.icerock:mobile-multiplatform", version = "0.13.0" } -androidGradlePlugin = { module = "com.android.tools.build:gradle", version = "7.2.2" } -detektGradlePlugin = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version = "1.19.0" } +mobileMultiplatformGradlePlugin = { module = "dev.icerock:mobile-multiplatform", version = "0.14.2" } +androidGradlePlugin = { module = "com.android.tools.build:gradle", version = "7.4.2" } +detektGradlePlugin = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version = "1.22.0" } kswiftGradlePlugin = { module = "dev.icerock.moko:kswift-gradle-plugin", version.ref = "mokoKSwiftVersion" } composeJetBrainsGradlePlugin = { module = "org.jetbrains.compose:compose-gradle-plugin", version.ref = "composeJetBrainsVersion" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 00e33ed..f927018 100755 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip +# can't update to gradle 8 with kotlin 1.8 +# https://youtrack.jetbrains.com/issue/KT-55751 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/mvvm-build-logic/settings.gradle.kts b/mvvm-build-logic/settings.gradle.kts index 3c46823..31e6848 100644 --- a/mvvm-build-logic/settings.gradle.kts +++ b/mvvm-build-logic/settings.gradle.kts @@ -2,8 +2,6 @@ * Copyright 2022 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. */ -enableFeaturePreview("VERSION_CATALOGS") - dependencyResolutionManagement { repositories { mavenCentral() @@ -16,3 +14,5 @@ dependencyResolutionManagement { } } } + +rootProject.name = "mvvm-build-logic" diff --git a/mvvm-build-logic/src/main/kotlin/kmm-library-convention.gradle.kts b/mvvm-build-logic/src/main/kotlin/kmm-library-convention.gradle.kts index d6b9f5d..f1d5b21 100644 --- a/mvvm-build-logic/src/main/kotlin/kmm-library-convention.gradle.kts +++ b/mvvm-build-logic/src/main/kotlin/kmm-library-convention.gradle.kts @@ -41,13 +41,13 @@ kotlin { val mobileMain by creating val mobileTest by creating val androidMain by getting - val androidTest by getting + val androidUnitTest by getting mobileMain.dependsOn(commonMain) mobileTest.dependsOn(commonTest) androidMain.dependsOn(mobileMain) - androidTest.dependsOn(mobileTest) + androidUnitTest.dependsOn(mobileTest) iosMain.dependsOn(mobileMain) iosTest.dependsOn(mobileTest) diff --git a/mvvm-core/src/androidMain/kotlin/dev/icerock/moko/mvvm/ViewModelFactory.kt b/mvvm-core/src/androidMain/kotlin/dev/icerock/moko/mvvm/ViewModelFactory.kt index 35154f1..c19694c 100644 --- a/mvvm-core/src/androidMain/kotlin/dev/icerock/moko/mvvm/ViewModelFactory.kt +++ b/mvvm-core/src/androidMain/kotlin/dev/icerock/moko/mvvm/ViewModelFactory.kt @@ -7,11 +7,12 @@ package dev.icerock.moko.mvvm import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelStoreOwner +import androidx.lifecycle.viewmodel.CreationExtras class ViewModelFactory( private val viewModelBlock: () -> ViewModel ) : ViewModelProvider.Factory { - override fun create(modelClass: Class): T { + override fun create(modelClass: Class, extras: CreationExtras): T { @Suppress("UNCHECKED_CAST") return viewModelBlock() as T } @@ -20,12 +21,10 @@ class ViewModelFactory( inline fun ViewModelStoreOwner.getViewModel( noinline viewModelBlock: () -> T ): T = ViewModelProvider( - this, - ViewModelFactory { viewModelBlock() } -).get(T::class.java) + owner = this, + factory = ViewModelFactory(viewModelBlock) +)[T::class.java] inline fun createViewModelFactory( noinline viewModelBlock: () -> T -): ViewModelFactory = ViewModelFactory { - viewModelBlock() -} +): ViewModelFactory = ViewModelFactory(viewModelBlock) diff --git a/mvvm-databinding/build.gradle.kts b/mvvm-databinding/build.gradle.kts index f5dde77..b1c0840 100644 --- a/mvvm-databinding/build.gradle.kts +++ b/mvvm-databinding/build.gradle.kts @@ -21,7 +21,7 @@ dependencies { api(libs.mokoResources) api(libs.appCompat) - api(libs.lifecycle) + api(libs.lifecycleKtx) api(libs.material) api(libs.coroutines) diff --git a/mvvm-livedata-glide/build.gradle.kts b/mvvm-livedata-glide/build.gradle.kts index d9f71ce..24ca4aa 100644 --- a/mvvm-livedata-glide/build.gradle.kts +++ b/mvvm-livedata-glide/build.gradle.kts @@ -16,7 +16,6 @@ dependencies { api(libs.mokoResources) api(libs.appCompat) - api(libs.lifecycle) api(libs.glide) api(libs.coroutines) } diff --git a/mvvm-livedata-swiperefresh/build.gradle.kts b/mvvm-livedata-swiperefresh/build.gradle.kts index 429ffe0..1f5563a 100644 --- a/mvvm-livedata-swiperefresh/build.gradle.kts +++ b/mvvm-livedata-swiperefresh/build.gradle.kts @@ -16,7 +16,6 @@ dependencies { api(libs.mokoResources) api(libs.appCompat) - api(libs.lifecycle) api(libs.swipeRefresh) api(libs.coroutines) } diff --git a/mvvm-viewbinding/build.gradle.kts b/mvvm-viewbinding/build.gradle.kts index d7a4b89..f9698ab 100644 --- a/mvvm-viewbinding/build.gradle.kts +++ b/mvvm-viewbinding/build.gradle.kts @@ -21,6 +21,6 @@ dependencies { api(libs.mokoResources) api(libs.appCompat) - api(libs.lifecycle) + api(libs.lifecycleKtx) api(libs.coroutines) } diff --git a/settings.gradle.kts b/settings.gradle.kts index 2c2bfe6..c77f0ce 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,9 +2,10 @@ * Copyright 2019 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. */ -enableFeaturePreview("VERSION_CATALOGS") enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") +rootProject.name = "moko-mvvm" + dependencyResolutionManagement { repositories { mavenCentral() From e47bdfe3d902b93308566a103256d741f9a67445 Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Sun, 2 Apr 2023 21:01:28 +0600 Subject: [PATCH 05/13] #232 getViewModel draft --- gradle/libs.versions.toml | 1 + mvvm-compose/build.gradle.kts | 39 +++++++++++++ .../moko/mvvm/compose/getViewModel.android.kt | 55 +++++++++++++++++++ .../icerock/moko/mvvm/compose/getViewModel.kt | 22 ++++++++ .../mvvm/compose/getViewModel.nonAndroid.kt | 26 +++++++++ .../dev/icerock/moko/mvvm/ViewModelFactory.kt | 26 ++++++++- settings.gradle.kts | 1 + 7 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 mvvm-compose/build.gradle.kts create mode 100644 mvvm-compose/src/androidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.android.kt create mode 100644 mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.kt create mode 100644 mvvm-compose/src/nonAndroidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.nonAndroid.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a6ad508..3c856e2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,6 +12,7 @@ composeJetBrainsVersion = "1.3.1" [libraries] # android appCompat = { module = "androidx.appcompat:appcompat", version = "1.6.1" } +androidxCore = { module = "androidx.core:core", version = "1.9.0" } material = { module = "com.google.android.material:material", version = "1.8.0" } lifecycleKtx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidLifecycleVersion" } androidViewModel = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidLifecycleVersion" } diff --git a/mvvm-compose/build.gradle.kts b/mvvm-compose/build.gradle.kts new file mode 100644 index 0000000..293f623 --- /dev/null +++ b/mvvm-compose/build.gradle.kts @@ -0,0 +1,39 @@ +/* + * Copyright 2022 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. + */ + +plugins { + id("kmp-library-convention") + id("detekt-convention") + id("org.jetbrains.compose") + id("javadoc-stub-convention") + id("publication-convention") +} + +android { + namespace = "dev.icerock.moko.mvvm.compose" +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(11)) + } +} + +kotlin { + sourceSets { + commonMain { + dependencies { + api(projects.mvvmCore) + api(compose.runtime) + } + } + + androidMain { + dependencies { + implementation(libs.composeFoundation) + implementation(libs.androidxCore) + } + } + } +} diff --git a/mvvm-compose/src/androidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.android.kt b/mvvm-compose/src/androidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.android.kt new file mode 100644 index 0000000..ca010c4 --- /dev/null +++ b/mvvm-compose/src/androidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.android.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2023 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. + */ + +package dev.icerock.moko.mvvm.compose + +import android.content.Context +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalContext +import androidx.core.app.ComponentActivity +import androidx.lifecycle.ViewModelStore +import androidx.lifecycle.ViewModelStoreOwner +import dev.icerock.moko.mvvm.getViewModel +import dev.icerock.moko.mvvm.viewmodel.ViewModel +import kotlin.reflect.KClass + +@Composable +actual fun getViewModel( + key: String, + klass: KClass, + viewModelBlock: () -> T +): T { + val context: Context = LocalContext.current + val viewModel: T = remember(key, context) { + val viewModelStore: ViewModelStoreOwner = context as? ViewModelStoreOwner + ?: throw IllegalStateException("context not implement ViewModelStoreOwner") + + viewModelStore.getViewModel( + key = key, + klass = klass, + viewModelBlock = viewModelBlock + ) + } + + DisposableEffect(viewModel, context) { + onDispose { + val componentActivity: ComponentActivity = context as? ComponentActivity + ?: throw IllegalStateException("context should be ComponentActivity") + val viewModelStore: ViewModelStoreOwner = context as? ViewModelStoreOwner + ?: throw IllegalStateException("context not implement ViewModelStoreOwner") + + if (!componentActivity.isChangingConfigurations) { + viewModel.onCleared() + } + } + } + + return viewModel +} + +private class StoreViewModel : androidx.lifecycle.ViewModel(), ViewModelStoreOwner { + override val viewModelStore: ViewModelStore = ViewModelStore() +} diff --git a/mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.kt b/mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.kt new file mode 100644 index 0000000..c133b4a --- /dev/null +++ b/mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2023 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. + */ + +package dev.icerock.moko.mvvm.compose + +import androidx.compose.runtime.Composable +import dev.icerock.moko.mvvm.viewmodel.ViewModel +import kotlin.reflect.KClass + +@Composable +expect fun getViewModel( + key: String, + klass: KClass, + viewModelBlock: () -> T +): T + +@Composable +inline fun getViewModel( + key: String, + noinline viewModelBlock: () -> T +): T = getViewModel(key, T::class, viewModelBlock) diff --git a/mvvm-compose/src/nonAndroidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.nonAndroid.kt b/mvvm-compose/src/nonAndroidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.nonAndroid.kt new file mode 100644 index 0000000..bc6f93b --- /dev/null +++ b/mvvm-compose/src/nonAndroidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.nonAndroid.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2023 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. + */ + +package dev.icerock.moko.mvvm.compose + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.remember +import dev.icerock.moko.mvvm.viewmodel.ViewModel +import kotlin.reflect.KClass + +@Composable +actual fun getViewModel( + key: String, + klass: KClass, + viewModelBlock: () -> T +): T { + val viewModel: T = remember(key, viewModelBlock) + + DisposableEffect(viewModel) { + onDispose { viewModel.onCleared() } + } + + return viewModel +} diff --git a/mvvm-core/src/androidMain/kotlin/dev/icerock/moko/mvvm/ViewModelFactory.kt b/mvvm-core/src/androidMain/kotlin/dev/icerock/moko/mvvm/ViewModelFactory.kt index c19694c..e717cc3 100644 --- a/mvvm-core/src/androidMain/kotlin/dev/icerock/moko/mvvm/ViewModelFactory.kt +++ b/mvvm-core/src/androidMain/kotlin/dev/icerock/moko/mvvm/ViewModelFactory.kt @@ -8,6 +8,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelStoreOwner import androidx.lifecycle.viewmodel.CreationExtras +import kotlin.reflect.KClass class ViewModelFactory( private val viewModelBlock: () -> ViewModel @@ -18,12 +19,31 @@ class ViewModelFactory( } } -inline fun ViewModelStoreOwner.getViewModel( - noinline viewModelBlock: () -> T +fun ViewModelStoreOwner.getViewModel( + klass: KClass, + viewModelBlock: () -> T +): T = ViewModelProvider( + owner = this, + factory = ViewModelFactory(viewModelBlock) +)[klass.java] + +fun ViewModelStoreOwner.getViewModel( + key: String, + klass: KClass, + viewModelBlock: () -> T ): T = ViewModelProvider( owner = this, factory = ViewModelFactory(viewModelBlock) -)[T::class.java] +).get(key = key, klass.java) + +inline fun ViewModelStoreOwner.getViewModel( + key: String, + noinline viewModelBlock: () -> T +): T = getViewModel(key, T::class, viewModelBlock) + +inline fun ViewModelStoreOwner.getViewModel( + noinline viewModelBlock: () -> T +): T = getViewModel(T::class, viewModelBlock) inline fun createViewModelFactory( noinline viewModelBlock: () -> T diff --git a/settings.gradle.kts b/settings.gradle.kts index c77f0ce..8fa3c07 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -17,6 +17,7 @@ includeBuild("mvvm-build-logic") include(":mvvm-internal") include(":mvvm-core") +include(":mvvm-compose") include(":mvvm-flow") include(":mvvm-flow:apple") include(":mvvm-flow-compose") From 20b0a488592c4b6b6a19ac8177cadfd0e206e885 Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Sun, 2 Apr 2023 21:24:49 +0600 Subject: [PATCH 06/13] #232 persist viewmodel on configuration change --- .../moko/mvvm/compose/getViewModel.android.kt | 48 ++++++++++++++----- .../icerock/moko/mvvm/compose/getViewModel.kt | 4 +- .../mvvm/compose/getViewModel.nonAndroid.kt | 2 +- 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/mvvm-compose/src/androidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.android.kt b/mvvm-compose/src/androidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.android.kt index ca010c4..e4808fc 100644 --- a/mvvm-compose/src/androidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.android.kt +++ b/mvvm-compose/src/androidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.android.kt @@ -10,39 +10,43 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import androidx.core.app.ComponentActivity +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelStore import androidx.lifecycle.ViewModelStoreOwner +import androidx.lifecycle.viewmodel.viewModelFactory import dev.icerock.moko.mvvm.getViewModel import dev.icerock.moko.mvvm.viewmodel.ViewModel import kotlin.reflect.KClass @Composable actual fun getViewModel( - key: String, + key: Any, klass: KClass, viewModelBlock: () -> T ): T { val context: Context = LocalContext.current - val viewModel: T = remember(key, context) { + val storeHolder: ViewModelStoreHolder = remember(context, key) { val viewModelStore: ViewModelStoreOwner = context as? ViewModelStoreOwner ?: throw IllegalStateException("context not implement ViewModelStoreOwner") - viewModelStore.getViewModel( - key = key, - klass = klass, - viewModelBlock = viewModelBlock - ) + val storeViewModel: StoreViewModel = viewModelStore.getViewModel { StoreViewModel() } + storeViewModel.get(key) + } + val viewModel: T = remember(storeHolder) { + ViewModelProvider( + store = storeHolder.viewModelStore, + factory = viewModelFactory { addInitializer(klass) { viewModelBlock() } } + )[klass.java] } - DisposableEffect(viewModel, context) { + DisposableEffect(context, storeHolder) { onDispose { val componentActivity: ComponentActivity = context as? ComponentActivity ?: throw IllegalStateException("context should be ComponentActivity") - val viewModelStore: ViewModelStoreOwner = context as? ViewModelStoreOwner - ?: throw IllegalStateException("context not implement ViewModelStoreOwner") if (!componentActivity.isChangingConfigurations) { - viewModel.onCleared() + storeHolder.viewModelStore.clear() + storeHolder.disposeStore() } } } @@ -50,6 +54,24 @@ actual fun getViewModel( return viewModel } -private class StoreViewModel : androidx.lifecycle.ViewModel(), ViewModelStoreOwner { - override val viewModelStore: ViewModelStore = ViewModelStore() +private class StoreViewModel : androidx.lifecycle.ViewModel() { + private val stores: MutableMap = mutableMapOf() + + fun get(key: Any): ViewModelStoreHolder { + val store: ViewModelStore = stores[key] ?: ViewModelStore() + + if (stores.containsKey(key).not()) { + stores[key] = store + } + + return ViewModelStoreHolder( + viewModelStore = store, + disposeStore = { stores.remove(key) } + ) + } } + +private data class ViewModelStoreHolder( + val viewModelStore: ViewModelStore, + val disposeStore: () -> Unit +) diff --git a/mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.kt b/mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.kt index c133b4a..6345672 100644 --- a/mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.kt +++ b/mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.kt @@ -10,13 +10,13 @@ import kotlin.reflect.KClass @Composable expect fun getViewModel( - key: String, + key: Any, klass: KClass, viewModelBlock: () -> T ): T @Composable inline fun getViewModel( - key: String, + key: Any, noinline viewModelBlock: () -> T ): T = getViewModel(key, T::class, viewModelBlock) diff --git a/mvvm-compose/src/nonAndroidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.nonAndroid.kt b/mvvm-compose/src/nonAndroidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.nonAndroid.kt index bc6f93b..94ab114 100644 --- a/mvvm-compose/src/nonAndroidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.nonAndroid.kt +++ b/mvvm-compose/src/nonAndroidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.nonAndroid.kt @@ -12,7 +12,7 @@ import kotlin.reflect.KClass @Composable actual fun getViewModel( - key: String, + key: Any, klass: KClass, viewModelBlock: () -> T ): T { From 1ad2b3df93358eb1868c0ea38e2c73825739606e Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Mon, 3 Apr 2023 14:01:28 +0600 Subject: [PATCH 07/13] #232 mark bug inline fun --- .../kotlin/dev/icerock/moko/mvvm/compose/getViewModel.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.kt b/mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.kt index 6345672..d45c272 100644 --- a/mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.kt +++ b/mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.kt @@ -15,6 +15,12 @@ expect fun getViewModel( viewModelBlock: () -> T ): T +// with this inline function we got error +// Module "dev.icerock.moko:mvvm-compose (dev.icerock.moko:mvvm-compose-iossimulatorarm64)" has a +// reference to symbol [ dev.icerock.moko.mvvm.compose/getViewModel|-1374970681312300217[0] +// <- local Local[,0 | TYPE_PARAMETER name:T index:0 variance: +// superTypes:[dev.icerock.moko.mvvm.viewmodel.ViewModel] reified:true] ]. +// Neither the module itself nor its dependencies contain such declaration. @Composable inline fun getViewModel( key: Any, From a812ea2302beb90c9376cfe155db5647a2366574 Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Mon, 3 Apr 2023 14:33:52 +0600 Subject: [PATCH 08/13] #232 link to youtrack with ios bug --- .../kotlin/dev/icerock/moko/mvvm/compose/getViewModel.kt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.kt b/mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.kt index d45c272..53e147d 100644 --- a/mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.kt +++ b/mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.kt @@ -15,12 +15,7 @@ expect fun getViewModel( viewModelBlock: () -> T ): T -// with this inline function we got error -// Module "dev.icerock.moko:mvvm-compose (dev.icerock.moko:mvvm-compose-iossimulatorarm64)" has a -// reference to symbol [ dev.icerock.moko.mvvm.compose/getViewModel|-1374970681312300217[0] -// <- local Local[,0 | TYPE_PARAMETER name:T index:0 variance: -// superTypes:[dev.icerock.moko.mvvm.viewmodel.ViewModel] reified:true] ]. -// Neither the module itself nor its dependencies contain such declaration. +// at now function can't be used on iOS because of https://youtrack.jetbrains.com/issue/KT-57727 @Composable inline fun getViewModel( key: Any, From 53b708ee5a6930242cc06912e079a4b9d25039e2 Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Mon, 3 Apr 2023 21:19:16 +0600 Subject: [PATCH 09/13] #230 fix detekt, android lint and remove deprecated --- gradle/libs.versions.toml | 2 +- .../apple/src/commonMain/kotlin/Greeting.kt | 2 - .../moko/mvvm/internal/WeakReference.kt | 4 +- .../resources/deprecated/UIButtonBindings.kt | 19 --------- .../resources/deprecated/UILabelBindings.kt | 21 ---------- .../deprecated/UITextFieldBindings.kt | 21 ---------- .../deprecated/UITextViewBindings.kt | 21 ---------- .../livedata/deprecated/UIButtonBindings.kt | 30 -------------- .../livedata/deprecated/UIControlBindings.kt | 27 ------------- .../livedata/deprecated/UILabelBindings.kt | 19 --------- .../livedata/deprecated/UISwitchBindings.kt | 29 -------------- .../deprecated/UITextFieldBindings.kt | 29 -------------- .../livedata/deprecated/UITextViewBindings.kt | 39 ------------------ .../livedata/deprecated/UIViewBindings.kt | 40 ------------------- sample/android-app/build.gradle.kts | 1 + 15 files changed, 4 insertions(+), 300 deletions(-) delete mode 100644 mvvm-livedata-resources/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/resources/deprecated/UIButtonBindings.kt delete mode 100644 mvvm-livedata-resources/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/resources/deprecated/UILabelBindings.kt delete mode 100644 mvvm-livedata-resources/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/resources/deprecated/UITextFieldBindings.kt delete mode 100644 mvvm-livedata-resources/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/resources/deprecated/UITextViewBindings.kt delete mode 100644 mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UIButtonBindings.kt delete mode 100644 mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UIControlBindings.kt delete mode 100644 mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UILabelBindings.kt delete mode 100644 mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UISwitchBindings.kt delete mode 100644 mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UITextFieldBindings.kt delete mode 100644 mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UITextViewBindings.kt delete mode 100644 mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UIViewBindings.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a6ad508..559feff 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -16,7 +16,7 @@ material = { module = "com.google.android.material:material", version = "1.8.0" lifecycleKtx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidLifecycleVersion" } androidViewModel = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidLifecycleVersion" } androidLiveData = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "androidLifecycleVersion" } -glide = { module = "com.github.bumptech.glide:glide", version = "4.11.0" } +glide = { module = "com.github.bumptech.glide:glide", version = "4.15.1" } swipeRefresh = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version = "1.1.0" } # compose diff --git a/mvvm-flow/apple/src/commonMain/kotlin/Greeting.kt b/mvvm-flow/apple/src/commonMain/kotlin/Greeting.kt index a7658b4..f9643b3 100644 --- a/mvvm-flow/apple/src/commonMain/kotlin/Greeting.kt +++ b/mvvm-flow/apple/src/commonMain/kotlin/Greeting.kt @@ -2,7 +2,5 @@ * Copyright 2022 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. */ -package dev.icerock.moko.mvvm.flow.apple - // just for compile this module fun helloWorld() = println("hello world") diff --git a/mvvm-internal/src/nonAndroidMain/kotlin/dev/icerock/moko/mvvm/internal/WeakReference.kt b/mvvm-internal/src/nonAndroidMain/kotlin/dev/icerock/moko/mvvm/internal/WeakReference.kt index d48155c..736c2d7 100644 --- a/mvvm-internal/src/nonAndroidMain/kotlin/dev/icerock/moko/mvvm/internal/WeakReference.kt +++ b/mvvm-internal/src/nonAndroidMain/kotlin/dev/icerock/moko/mvvm/internal/WeakReference.kt @@ -4,6 +4,6 @@ package dev.icerock.moko.mvvm.internal -expect class WeakReference(referred: T) { +expect class WeakReference(referred: T) { fun get(): T? -} \ No newline at end of file +} diff --git a/mvvm-livedata-resources/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/resources/deprecated/UIButtonBindings.kt b/mvvm-livedata-resources/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/resources/deprecated/UIButtonBindings.kt deleted file mode 100644 index e0bc078..0000000 --- a/mvvm-livedata-resources/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/resources/deprecated/UIButtonBindings.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:Suppress("PackageDirectoryMismatch") - -package dev.icerock.moko.mvvm.livedata - -import dev.icerock.moko.mvvm.livedata.resources.bindTitle -import dev.icerock.moko.resources.desc.StringDesc -import platform.UIKit.UIButton - -@Deprecated( - "Use UIButton.bindTitle", - replaceWith = ReplaceWith("UIButton.bindTitle") -) -fun LiveData.bindStringDescToButtonTitle(button: UIButton): Closeable { - return button.bindTitle(this) -} diff --git a/mvvm-livedata-resources/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/resources/deprecated/UILabelBindings.kt b/mvvm-livedata-resources/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/resources/deprecated/UILabelBindings.kt deleted file mode 100644 index 7f091c2..0000000 --- a/mvvm-livedata-resources/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/resources/deprecated/UILabelBindings.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:Suppress("PackageDirectoryMismatch") - -package dev.icerock.moko.mvvm.livedata - -import dev.icerock.moko.mvvm.livedata.resources.bindText -import dev.icerock.moko.resources.desc.StringDesc -import platform.UIKit.UILabel - -@Deprecated( - "Use UILabel.bindText", - replaceWith = ReplaceWith("UILabel.bindText") -) -fun LiveData.bindStringDescToLabelText( - label: UILabel -): Closeable { - return label.bindText(this) -} diff --git a/mvvm-livedata-resources/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/resources/deprecated/UITextFieldBindings.kt b/mvvm-livedata-resources/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/resources/deprecated/UITextFieldBindings.kt deleted file mode 100644 index 885c76c..0000000 --- a/mvvm-livedata-resources/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/resources/deprecated/UITextFieldBindings.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:Suppress("PackageDirectoryMismatch") - -package dev.icerock.moko.mvvm.livedata - -import dev.icerock.moko.mvvm.livedata.resources.bindText -import dev.icerock.moko.resources.desc.StringDesc -import platform.UIKit.UITextField - -@Deprecated( - "Use UITextField.bindText", - replaceWith = ReplaceWith("UITextField.bindText") -) -fun LiveData.bindStringDescToTextFieldText( - textField: UITextField -): Closeable { - return textField.bindText(this) -} diff --git a/mvvm-livedata-resources/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/resources/deprecated/UITextViewBindings.kt b/mvvm-livedata-resources/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/resources/deprecated/UITextViewBindings.kt deleted file mode 100644 index fd0a0d6..0000000 --- a/mvvm-livedata-resources/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/resources/deprecated/UITextViewBindings.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:Suppress("PackageDirectoryMismatch") - -package dev.icerock.moko.mvvm.livedata - -import dev.icerock.moko.mvvm.livedata.resources.bindText -import dev.icerock.moko.resources.desc.StringDesc -import platform.UIKit.UITextView - -@Deprecated( - "Use UITextView.bindText", - replaceWith = ReplaceWith("UITextView.bindText") -) -fun LiveData.bindStringDescToTextViewText( - textView: UITextView -): Closeable { - return textView.bindText(this) -} diff --git a/mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UIButtonBindings.kt b/mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UIButtonBindings.kt deleted file mode 100644 index c1424e2..0000000 --- a/mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UIButtonBindings.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:Suppress("PackageDirectoryMismatch") - -package dev.icerock.moko.mvvm.livedata - -import platform.UIKit.UIButton -import platform.UIKit.UIImage - -@Deprecated( - "Use UIButton.bindTitle", - replaceWith = ReplaceWith("UIButton.bindTitle") -) -fun LiveData.bindStringToButtonTitle(button: UIButton): Closeable { - return button.bindTitle(this) -} - -@Deprecated( - "Use UIButton.bindImage", - replaceWith = ReplaceWith("UIButton.bindImage") -) -fun LiveData.bindBoolToButtonImage( - button: UIButton, - trueImage: UIImage, - falseImage: UIImage -): Closeable { - return button.bindImage(this, trueImage, falseImage) -} diff --git a/mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UIControlBindings.kt b/mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UIControlBindings.kt deleted file mode 100644 index 7d33057..0000000 --- a/mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UIControlBindings.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:Suppress("PackageDirectoryMismatch") - -package dev.icerock.moko.mvvm.livedata - -import platform.UIKit.UIControl - -@Deprecated( - "Use UIControl.bindEnabled", - replaceWith = ReplaceWith("UIControl.bindEnabled") -) -fun LiveData.bindBoolToControlEnabled( - control: UIControl -): Closeable { - return control.bindEnabled(this) -} - -@Deprecated( - "Use UIControl.bindFocusTwoWay", - replaceWith = ReplaceWith("UIControl.bindFocusTwoWay") -) -fun MutableLiveData.bindBoolTwoWayToControlFocus(control: UIControl): Closeable { - return control.bindFocusTwoWay(this) -} diff --git a/mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UILabelBindings.kt b/mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UILabelBindings.kt deleted file mode 100644 index d0649b6..0000000 --- a/mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UILabelBindings.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:Suppress("PackageDirectoryMismatch") - -package dev.icerock.moko.mvvm.livedata - -import platform.UIKit.UILabel - -@Deprecated( - "Use UILabel.bindText", - replaceWith = ReplaceWith("UILabel.bindText") -) -fun LiveData.bindStringToLabelText( - label: UILabel -): Closeable { - return label.bindText(this) -} diff --git a/mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UISwitchBindings.kt b/mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UISwitchBindings.kt deleted file mode 100644 index 2de0c16..0000000 --- a/mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UISwitchBindings.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:Suppress("PackageDirectoryMismatch") - -package dev.icerock.moko.mvvm.livedata - -import platform.UIKit.UISwitch - -@Deprecated( - "Use UISwitch.bindSwitchOn", - replaceWith = ReplaceWith("UISwitch.bindSwitchOn") -) -fun LiveData.bindBoolToSwitchOn( - switch: UISwitch -): Closeable { - return switch.bindSwitchOn(this) -} - -@Deprecated( - "Use UISwitch.bindSwitchOnTwoWay", - replaceWith = ReplaceWith("UISwitch.bindSwitchOnTwoWay") -) -fun MutableLiveData.bindBoolTwoWayToSwitchOn( - switch: UISwitch -): Closeable { - return switch.bindSwitchOnTwoWay(this) -} diff --git a/mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UITextFieldBindings.kt b/mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UITextFieldBindings.kt deleted file mode 100644 index bd591a9..0000000 --- a/mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UITextFieldBindings.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:Suppress("PackageDirectoryMismatch") - -package dev.icerock.moko.mvvm.livedata - -import platform.UIKit.UITextField - -@Deprecated( - "Use UITextField.bindText", - replaceWith = ReplaceWith("UITextField.bindText") -) -fun LiveData.bindStringToTextFieldText( - textField: UITextField -): Closeable { - return textField.bindText(this) -} - -@Deprecated( - "Use UITextField.bindTextTwoWay", - replaceWith = ReplaceWith("UITextField.bindTextTwoWay") -) -fun MutableLiveData.bindStringTwoWayToTextFieldText( - textField: UITextField -): Closeable { - return textField.bindTextTwoWay(this) -} diff --git a/mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UITextViewBindings.kt b/mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UITextViewBindings.kt deleted file mode 100644 index 5752199..0000000 --- a/mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UITextViewBindings.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:Suppress("PackageDirectoryMismatch") - -package dev.icerock.moko.mvvm.livedata - -import platform.UIKit.UITextView - -@Deprecated( - "Use UITextView.bindText", - replaceWith = ReplaceWith("UITextView.bindText") -) -fun LiveData.bindStringToTextViewText( - textView: UITextView -): Closeable { - return textView.bindText(this) -} - -@Deprecated( - "Use UITextView.bindTextTwoWay", - replaceWith = ReplaceWith("UITextView.bindTextTwoWay") -) -fun MutableLiveData.bindStringTwoWayToTextViewText( - textView: UITextView -): Closeable { - return textView.bindTextTwoWay(this) -} - -@Deprecated( - "Use UITextView.bindFocusTwoWay", - replaceWith = ReplaceWith("UITextView.bindFocusTwoWay") -) -fun MutableLiveData.bindBoolTwoWayToTextViewFocus( - textView: UITextView -): Closeable { - return textView.bindFocusTwoWay(this) -} diff --git a/mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UIViewBindings.kt b/mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UIViewBindings.kt deleted file mode 100644 index 6994222..0000000 --- a/mvvm-livedata/src/iosMain/kotlin/dev/icerock/moko/mvvm/livedata/deprecated/UIViewBindings.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:Suppress("PackageDirectoryMismatch") - -package dev.icerock.moko.mvvm.livedata - -import platform.UIKit.UIColor -import platform.UIKit.UIView - -@Deprecated( - "Use UIView.bindBackgroundColor", - replaceWith = ReplaceWith("UIView.bindBackgroundColor") -) -fun LiveData.bindBoolToViewBackgroundColor( - view: UIView, - trueColor: UIColor, - falseColor: UIColor -): Closeable { - return view.bindBackgroundColor(this, trueColor, falseColor) -} - -@Deprecated( - "Use UIView.bindHidden", - replaceWith = ReplaceWith("UIView.bindHidden") -) -fun LiveData.bindBoolToViewHidden( - view: UIView -): Closeable { - return view.bindHidden(this) -} - -@Deprecated( - "Use UIView.bindFocus", - replaceWith = ReplaceWith("UIView.bindFocus") -) -fun LiveData.bindBoolToViewFocus(view: UIView): Closeable { - return view.bindFocus(this) -} diff --git a/sample/android-app/build.gradle.kts b/sample/android-app/build.gradle.kts index 3f446c1..4971def 100644 --- a/sample/android-app/build.gradle.kts +++ b/sample/android-app/build.gradle.kts @@ -16,6 +16,7 @@ android { lintOptions { disable("Instantiatable") // bug Error: SimpleActivity must extend android.app.Activity [Instantiatable] + disable("NotificationPermission") // https://github.com/bumptech/glide/issues/4940 } } From b91541f73c4b9a4b5e4d30fef6517c4c5ec8b161 Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Sat, 8 Apr 2023 19:10:35 +0600 Subject: [PATCH 10/13] #232 improve api for getViewModel of compose multiplatform --- .../moko/mvvm/compose/ViewModelFactory.kt | 23 ++++++++++++ .../icerock/moko/mvvm/compose/getViewModel.kt | 36 +++++++++++++++++-- .../mvvm/compose/getViewModel.nonAndroid.kt | 2 +- 3 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/ViewModelFactory.kt diff --git a/mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/ViewModelFactory.kt b/mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/ViewModelFactory.kt new file mode 100644 index 0000000..0ba105f --- /dev/null +++ b/mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/ViewModelFactory.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2023 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. + */ + +package dev.icerock.moko.mvvm.compose + +import dev.icerock.moko.mvvm.viewmodel.ViewModel +import kotlin.reflect.KClass + +interface ViewModelFactory { + val kClass: KClass + + fun createViewModel(): T +} + +inline fun viewModelFactory( + crossinline builder: () -> T +): ViewModelFactory { + return object : ViewModelFactory { + override val kClass: KClass = T::class + override fun createViewModel(): T = builder() + } +} diff --git a/mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.kt b/mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.kt index 53e147d..69bde4a 100644 --- a/mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.kt +++ b/mvvm-compose/src/commonMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.kt @@ -16,8 +16,38 @@ expect fun getViewModel( ): T // at now function can't be used on iOS because of https://youtrack.jetbrains.com/issue/KT-57727 +//@Composable +//inline fun getViewModel( +// key: Any, +// noinline viewModelBlock: @DisallowComposableCalls () -> T +//): T = remember(key, T::class, viewModelBlock) + +/** + * While https://youtrack.jetbrains.com/issue/KT-57727 it's best API that i can do. + * + * Created ViewModel will be saved during configuration change on Android and will call + * `onCleared` when composition will destroyed. + * + * Usage: + * ``` + * getViewModel( + * key = "some-key-of-current-navigation-screen", + * factory = viewModelFactory { SimpleViewModel() } + * ) + * ``` + * @param key if key have same value with last recomposition - function return same ViewModel instance + * @param factory ViewModel factory, used when key changed or ViewModel not created at all + * + * @return new created, or already exist ViewModel instance + */ @Composable -inline fun getViewModel( +fun getViewModel( key: Any, - noinline viewModelBlock: () -> T -): T = getViewModel(key, T::class, viewModelBlock) + factory: ViewModelFactory +): T { + return getViewModel( + key = key, + klass = factory.kClass, + viewModelBlock = factory::createViewModel + ) +} diff --git a/mvvm-compose/src/nonAndroidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.nonAndroid.kt b/mvvm-compose/src/nonAndroidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.nonAndroid.kt index 94ab114..79c9751 100644 --- a/mvvm-compose/src/nonAndroidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.nonAndroid.kt +++ b/mvvm-compose/src/nonAndroidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.nonAndroid.kt @@ -16,7 +16,7 @@ actual fun getViewModel( klass: KClass, viewModelBlock: () -> T ): T { - val viewModel: T = remember(key, viewModelBlock) + val viewModel: T = remember(key, klass, viewModelBlock) DisposableEffect(viewModel) { onDispose { viewModel.onCleared() } From 6c51a2a534630632571e37d034ec7f2b28aac1c4 Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Sat, 8 Apr 2023 19:10:52 +0600 Subject: [PATCH 11/13] #232 compose modules now supports compose multiplatform --- mvvm-flow-compose/build.gradle.kts | 11 +++++---- mvvm-livedata-compose/build.gradle.kts | 31 +++++--------------------- 2 files changed, 10 insertions(+), 32 deletions(-) diff --git a/mvvm-flow-compose/build.gradle.kts b/mvvm-flow-compose/build.gradle.kts index c748d26..eece12c 100644 --- a/mvvm-flow-compose/build.gradle.kts +++ b/mvvm-flow-compose/build.gradle.kts @@ -3,13 +3,17 @@ */ plugins { - id("org.jetbrains.kotlin.multiplatform") + id("kmp-library-convention") id("detekt-convention") id("org.jetbrains.compose") id("javadoc-stub-convention") id("publication-convention") } +android { + namespace = "dev.icerock.moko.mvvm.flow.compose" +} + java { toolchain { languageVersion.set(JavaLanguageVersion.of(11)) @@ -17,11 +21,6 @@ java { } kotlin { - jvm() - js(IR) { - browser() - } - sourceSets { commonMain { dependencies { diff --git a/mvvm-livedata-compose/build.gradle.kts b/mvvm-livedata-compose/build.gradle.kts index 41532c1..94ac55f 100644 --- a/mvvm-livedata-compose/build.gradle.kts +++ b/mvvm-livedata-compose/build.gradle.kts @@ -3,35 +3,24 @@ */ plugins { - id("com.android.library") - id("org.jetbrains.kotlin.multiplatform") - id("android-base-convention") - id("dev.icerock.mobile.multiplatform.android-manifest") + id("kmp-library-convention") id("detekt-convention") id("org.jetbrains.compose") id("javadoc-stub-convention") id("publication-convention") } +android { + namespace = "dev.icerock.moko.mvvm.livedata.compose" +} + java { toolchain { languageVersion.set(JavaLanguageVersion.of(11)) } } -android { - defaultConfig { - minSdk = 21 - } -} - kotlin { - android() - jvm() - js(IR) { - browser() - } - sourceSets { val commonMain by getting { dependencies { @@ -45,15 +34,5 @@ kotlin { api(libs.composeLiveData) } } - - val nonAndroidMain by creating { - dependsOn(commonMain) - } - val jvmMain by getting { - dependsOn(nonAndroidMain) - } - val jsMain by getting { - dependsOn(nonAndroidMain) - } } } From 6e620d1923b5bdfe81a8f3bae625c97ccfee3587 Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Sat, 8 Apr 2023 19:28:45 +0600 Subject: [PATCH 12/13] #232 fix detekt --- mvvm-compose/build.gradle.kts | 3 +++ .../dev/icerock/moko/mvvm/compose/getViewModel.android.kt | 4 ++-- mvvm-flow-compose/build.gradle.kts | 3 +++ mvvm-livedata-compose/build.gradle.kts | 3 +++ mvvm-livedata-compose/src/androidMain/AndroidManifest.xml | 2 -- 5 files changed, 11 insertions(+), 4 deletions(-) delete mode 100755 mvvm-livedata-compose/src/androidMain/AndroidManifest.xml diff --git a/mvvm-compose/build.gradle.kts b/mvvm-compose/build.gradle.kts index 293f623..7154e23 100644 --- a/mvvm-compose/build.gradle.kts +++ b/mvvm-compose/build.gradle.kts @@ -12,6 +12,9 @@ plugins { android { namespace = "dev.icerock.moko.mvvm.compose" + defaultConfig { + minSdk = 21 + } } java { diff --git a/mvvm-compose/src/androidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.android.kt b/mvvm-compose/src/androidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.android.kt index e4808fc..a83d124 100644 --- a/mvvm-compose/src/androidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.android.kt +++ b/mvvm-compose/src/androidMain/kotlin/dev/icerock/moko/mvvm/compose/getViewModel.android.kt @@ -27,7 +27,7 @@ actual fun getViewModel( val context: Context = LocalContext.current val storeHolder: ViewModelStoreHolder = remember(context, key) { val viewModelStore: ViewModelStoreOwner = context as? ViewModelStoreOwner - ?: throw IllegalStateException("context not implement ViewModelStoreOwner") + ?: error("context not implement ViewModelStoreOwner") val storeViewModel: StoreViewModel = viewModelStore.getViewModel { StoreViewModel() } storeViewModel.get(key) @@ -42,7 +42,7 @@ actual fun getViewModel( DisposableEffect(context, storeHolder) { onDispose { val componentActivity: ComponentActivity = context as? ComponentActivity - ?: throw IllegalStateException("context should be ComponentActivity") + ?: error("context should be ComponentActivity") if (!componentActivity.isChangingConfigurations) { storeHolder.viewModelStore.clear() diff --git a/mvvm-flow-compose/build.gradle.kts b/mvvm-flow-compose/build.gradle.kts index eece12c..82bd4d9 100644 --- a/mvvm-flow-compose/build.gradle.kts +++ b/mvvm-flow-compose/build.gradle.kts @@ -12,6 +12,9 @@ plugins { android { namespace = "dev.icerock.moko.mvvm.flow.compose" + defaultConfig { + minSdk = 21 + } } java { diff --git a/mvvm-livedata-compose/build.gradle.kts b/mvvm-livedata-compose/build.gradle.kts index 94ac55f..bf6e001 100644 --- a/mvvm-livedata-compose/build.gradle.kts +++ b/mvvm-livedata-compose/build.gradle.kts @@ -12,6 +12,9 @@ plugins { android { namespace = "dev.icerock.moko.mvvm.livedata.compose" + defaultConfig { + minSdk = 21 + } } java { diff --git a/mvvm-livedata-compose/src/androidMain/AndroidManifest.xml b/mvvm-livedata-compose/src/androidMain/AndroidManifest.xml deleted file mode 100755 index 513479d..0000000 --- a/mvvm-livedata-compose/src/androidMain/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file From 5c04bc6414388802b81e44c8a6785452d3d0e4d9 Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Sat, 8 Apr 2023 20:03:15 +0600 Subject: [PATCH 13/13] up version --- README.md | 41 ++++++++++++++++++++----------------- mokoMvvmFlowSwiftUI.podspec | 2 +- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index beb2f66..d5203c3 100755 --- a/README.md +++ b/README.md @@ -50,22 +50,25 @@ allprojects { project build.gradle ```groovy dependencies { - commonMainApi("dev.icerock.moko:mvvm-core:0.15.0") // only ViewModel, EventsDispatcher, Dispatchers.UI - commonMainApi("dev.icerock.moko:mvvm-flow:0.15.0") // api mvvm-core, CFlow for native and binding extensions - commonMainApi("dev.icerock.moko:mvvm-livedata:0.15.0") // api mvvm-core, LiveData and extensions - commonMainApi("dev.icerock.moko:mvvm-state:0.15.0") // api mvvm-livedata, ResourceState class and extensions - commonMainApi("dev.icerock.moko:mvvm-livedata-resources:0.15.0") // api mvvm-core, moko-resources, extensions for LiveData with moko-resources - commonMainApi("dev.icerock.moko:mvvm-flow-resources:0.15.0") // api mvvm-core, moko-resources, extensions for Flow with moko-resources + commonMainApi("dev.icerock.moko:mvvm-core:0.16.0") // only ViewModel, EventsDispatcher, Dispatchers.UI + commonMainApi("dev.icerock.moko:mvvm-flow:0.16.0") // api mvvm-core, CFlow for native and binding extensions + commonMainApi("dev.icerock.moko:mvvm-livedata:0.16.0") // api mvvm-core, LiveData and extensions + commonMainApi("dev.icerock.moko:mvvm-state:0.16.0") // api mvvm-livedata, ResourceState class and extensions + commonMainApi("dev.icerock.moko:mvvm-livedata-resources:0.16.0") // api mvvm-core, moko-resources, extensions for LiveData with moko-resources + commonMainApi("dev.icerock.moko:mvvm-flow-resources:0.16.0") // api mvvm-core, moko-resources, extensions for Flow with moko-resources - androidMainApi("dev.icerock.moko:mvvm-flow-compose:0.15.0") // api mvvm-flow, binding extensions for Jetpack Compose (jvm, js, android) - androidMainApi("dev.icerock.moko:mvvm-livedata-compose:0.15.0") // api mvvm-livedata, binding extensions for Jetpack Compose (jvm, js, android) - androidMainApi("dev.icerock.moko:mvvm-livedata-material:0.15.0") // api mvvm-livedata, Material library android extensions - androidMainApi("dev.icerock.moko:mvvm-livedata-glide:0.15.0") // api mvvm-livedata, Glide library android extensions - androidMainApi("dev.icerock.moko:mvvm-livedata-swiperefresh:0.15.0") // api mvvm-livedata, SwipeRefreshLayout library android extensions - androidMainApi("dev.icerock.moko:mvvm-databinding:0.15.0") // api mvvm-livedata, DataBinding support for Android - androidMainApi("dev.icerock.moko:mvvm-viewbinding:0.15.0") // api mvvm-livedata, ViewBinding support for Android + // compose multiplatform + commonMainApi("dev.icerock.moko:mvvm-compose:0.16.0") // api mvvm-core, getViewModel for Compose Multiplatfrom + commonMainApi("dev.icerock.moko:mvvm-flow-compose:0.16.0") // api mvvm-flow, binding extensions for Compose Multiplatfrom + commonMainApi("dev.icerock.moko:mvvm-livedata-compose:0.16.0") // api mvvm-livedata, binding extensions for Compose Multiplatfrom + + androidMainApi("dev.icerock.moko:mvvm-livedata-material:0.16.0") // api mvvm-livedata, Material library android extensions + androidMainApi("dev.icerock.moko:mvvm-livedata-glide:0.16.0") // api mvvm-livedata, Glide library android extensions + androidMainApi("dev.icerock.moko:mvvm-livedata-swiperefresh:0.16.0") // api mvvm-livedata, SwipeRefreshLayout library android extensions + androidMainApi("dev.icerock.moko:mvvm-databinding:0.16.0") // api mvvm-livedata, DataBinding support for Android + androidMainApi("dev.icerock.moko:mvvm-viewbinding:0.16.0") // api mvvm-livedata, ViewBinding support for Android - commonTestImplementation("dev.icerock.moko:mvvm-test:0.15.0") // test utilities + commonTestImplementation("dev.icerock.moko:mvvm-test:0.16.0") // test utilities } ``` @@ -75,10 +78,10 @@ kotlin { // export correct artifact to use all classes of library directly from Swift targets.withType(org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget::class.java).all { binaries.withType(org.jetbrains.kotlin.gradle.plugin.mpp.Framework::class.java).all { - export("dev.icerock.moko:mvvm-core:0.15.0") - export("dev.icerock.moko:mvvm-livedata:0.15.0") - export("dev.icerock.moko:mvvm-livedata-resources:0.15.0") - export("dev.icerock.moko:mvvm-state:0.15.0") + export("dev.icerock.moko:mvvm-core:0.16.0") + export("dev.icerock.moko:mvvm-livedata:0.16.0") + export("dev.icerock.moko:mvvm-livedata-resources:0.16.0") + export("dev.icerock.moko:mvvm-state:0.16.0") } } } @@ -94,7 +97,7 @@ generation enabled. All `LiveData` to `UIView` bindings is extensions for UI ele To use MOKO MVVM with SwiftUI set name of your kotlin framework to `MultiPlatformLibrary` and add dependency to CocoaPods: ```ruby -pod 'mokoMvvmFlowSwiftUI', :podspec => 'https://raw.githubusercontent.com/icerockdev/moko-mvvm/release/0.15.0/mokoMvvmFlowSwiftUI.podspec' +pod 'mokoMvvmFlowSwiftUI', :podspec => 'https://raw.githubusercontent.com/icerockdev/moko-mvvm/release/0.16.0/mokoMvvmFlowSwiftUI.podspec' ``` required export of `mvvm-core` and `mvvm-flow`. diff --git a/mokoMvvmFlowSwiftUI.podspec b/mokoMvvmFlowSwiftUI.podspec index c049137..dd1b1ab 100644 --- a/mokoMvvmFlowSwiftUI.podspec +++ b/mokoMvvmFlowSwiftUI.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'mokoMvvmFlowSwiftUI' - s.version = '0.15.0' + s.version = '0.16.0' s.summary = 'MOKO MVVM SwiftUI additions for Flow' s.description = 'some description here' s.homepage = 'localhost'