├── .editorconfig ├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── build.yml │ ├── deploy-release-docs.yml │ ├── gradle-wrapper-validation.yml │ ├── publish-release.yml │ ├── sample.yml │ └── standalone-sample.yml ├── .gitignore ├── .idea ├── .gitignore ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── icon.png ├── jsonSchemas.xml ├── kotlinc.xml ├── migrations.xml └── vcs.xml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── RELEASING.md ├── build.gradle.kts ├── detekt.yml ├── docs ├── .idea │ └── vcs.xml ├── images │ └── logo.jpeg ├── multiplatform.md ├── requirements.txt ├── samples.md ├── stylesheets │ └── extra.css ├── swift-interop.md ├── viewmodel-compose-navigation.md ├── viewmodel-compose.md ├── viewmodel-koin-compose.md ├── viewmodel-koject-compose.md ├── viewmodel-savedstate-safe.md ├── viewmodel-savedstate.md └── viewmodel.md ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── kotlin-js-store └── yarn.lock ├── logo.png ├── mkdocs.yml ├── renovate.json5 ├── sample ├── .gitignore ├── app │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── hoc081098 │ │ │ └── kmpviewmodelsample │ │ │ └── android │ │ │ ├── MyApp.kt │ │ │ ├── Route.kt │ │ │ ├── StartActivity.kt │ │ │ ├── StartViewModel.kt │ │ │ ├── common │ │ │ ├── CollectWithLifecycleEffect.kt │ │ │ ├── MyApplicationTheme.kt │ │ │ ├── OnLifecycleEvent.kt │ │ │ └── commonUi.kt │ │ │ ├── product_detail │ │ │ └── ProductDetailScreen.kt │ │ │ ├── products │ │ │ └── ProductsScreen.kt │ │ │ └── search_products │ │ │ └── SearchProductsScreen.kt │ │ └── res │ │ └── values │ │ └── styles.xml ├── iosApp │ ├── .gitignore │ ├── Podfile │ ├── Podfile.lock │ ├── iosApp-RxSwift │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ ├── AccentColor.colorset │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Info.plist │ │ ├── Products │ │ │ ├── IosProductsViewModel.swift │ │ │ └── ProductsViewController.swift │ │ ├── SceneDelegate.swift │ │ └── Utils │ │ │ ├── DIContainer+get.swift │ │ │ ├── Napier.swift │ │ │ └── kotlinxCoroutinesFlow+RxSwift.swift │ ├── iosApp.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ ├── iosApp-RxSwift.xcscheme │ │ │ └── iosApp.xcscheme │ ├── iosApp.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── iosApp │ │ ├── Assets.xcassets │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ │ ├── CommonUi.swift │ │ ├── Info.plist │ │ ├── Others │ │ ├── async_let.swift │ │ └── callback_to_async.swift │ │ ├── Preview Content │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ │ ├── Product Detail │ │ ├── IosProductDetailViewModel.swift │ │ └── ProductDetailView.swift │ │ ├── Products │ │ ├── IosProductsViewModel.swift │ │ └── ProductsView.swift │ │ ├── Search Products │ │ ├── IosSearchProductsViewModel.swift │ │ └── SearchProductsView.swift │ │ ├── Snippets │ │ └── SnippetsView.swift │ │ ├── Utils │ │ ├── DIContainer+get.swift │ │ ├── Napier.swift │ │ ├── String+toDate.swift │ │ └── kotlinxCoroutinesFlowExtensions.swift │ │ └── iOSApp.swift └── shared │ ├── build.gradle.kts │ ├── shared.podspec │ └── src │ ├── androidMain │ └── kotlin │ │ └── com │ │ └── hoc081098 │ │ └── kmpviewmodelsample │ │ ├── common │ │ ├── AndroidAppDispatchers.kt │ │ ├── WeakReference.android.kt │ │ └── identityHashCode.android.kt │ │ └── di.android.kt │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── hoc081098 │ │ └── kmpviewmodelsample │ │ ├── common │ │ ├── AppDispatchers.kt │ │ ├── SingleEventFlow.kt │ │ ├── WeakReference.kt │ │ ├── debugCheckImmediateMainDispatcher.kt │ │ └── identityHashCode.kt │ │ ├── data │ │ ├── ProductResponse.kt │ │ └── fakeJson.kt │ │ ├── di.kt │ │ ├── domain │ │ └── ProductItem.kt │ │ ├── product_detail │ │ ├── GetProductById.kt │ │ └── ProductDetailViewModel.kt │ │ ├── products │ │ ├── GetProducts.kt │ │ └── ProductsViewModel.kt │ │ ├── search_products │ │ ├── SearchProducts.kt │ │ └── SearchProductsViewModel.kt │ │ ├── snippets │ │ ├── SearchViewModel.kt │ │ └── UserViewModel.kt │ │ └── ui │ │ └── ProductItemUi.kt │ └── iosMain │ └── kotlin │ └── com │ └── hoc081098 │ └── kmpviewmodelsample │ ├── CoroutinesUtils.kt │ ├── DIContainer.kt │ ├── NSError.kt │ ├── common │ ├── IosAppDispatchers.kt │ ├── WeakReference.ios.kt │ └── identityHashCode.ios.kt │ └── di.ios.kt ├── scripts └── update_docs_url.sh ├── settings.gradle.kts ├── standalone-sample ├── kmpviewmodel_compose_koin_sample │ ├── .gitignore │ ├── .idea │ │ ├── .gitignore │ │ ├── codeStyles │ │ │ ├── Project.xml │ │ │ └── codeStyleConfig.xml │ │ ├── deploymentTargetDropDown.xml │ │ ├── kotlinc.xml │ │ ├── migrations.xml │ │ ├── misc.xml │ │ ├── runConfigurations │ │ │ └── kmpviewmodel_compose_sample__desktop_runMain_.xml │ │ └── vcs.xml │ ├── README.md │ ├── android │ │ ├── build.gradle.kts │ │ └── src │ │ │ └── androidMain │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── hoc081098 │ │ │ │ └── android │ │ │ │ ├── KmpViewModelComposeKoinApp.kt │ │ │ │ ├── MainActivity.kt │ │ │ │ └── MainActivityViewModel.kt │ │ │ └── res │ │ │ └── values │ │ │ └── strings.xml │ ├── build.gradle.kts │ ├── common │ │ ├── build.gradle.kts │ │ └── src │ │ │ ├── androidMain │ │ │ └── AndroidManifest.xml │ │ │ ├── commonMain │ │ │ └── kotlin │ │ │ │ └── com │ │ │ │ └── hoc081098 │ │ │ │ └── common │ │ │ │ ├── App.kt │ │ │ │ ├── domain │ │ │ │ └── DemoRepository.kt │ │ │ │ └── screens │ │ │ │ ├── ScreenA.kt │ │ │ │ ├── ScreenB.kt │ │ │ │ └── ScreenC.kt │ │ │ ├── desktopMain │ │ │ └── kotlin │ │ │ │ └── com │ │ │ │ └── hoc081098 │ │ │ │ └── common │ │ │ │ └── DesktopApp.kt │ │ │ └── iosMain │ │ │ └── kotlin │ │ │ └── com │ │ │ └── hoc081098 │ │ │ └── common │ │ │ └── IosApp.kt │ ├── desktop │ │ ├── build.gradle.kts │ │ └── src │ │ │ └── jvmMain │ │ │ └── kotlin │ │ │ └── Main.kt │ ├── gradle.properties │ ├── gradle │ │ ├── libs.versions.toml │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── iosApp │ │ ├── iosApp.xcodeproj │ │ │ └── project.pbxproj │ │ └── iosApp │ │ │ ├── Assets.xcassets │ │ │ ├── AccentColor.colorset │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ │ ├── ContentView.swift │ │ │ ├── KmpViewModelComposeKoinApp.swift │ │ │ └── Preview Content │ │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ └── settings.gradle.kts └── kmpviewmodel_compose_koject_sample │ ├── .editorconfig │ ├── .fleet │ └── receipt.json │ ├── .github │ └── workflows │ │ └── gradle.yml │ ├── .gitignore │ ├── .run │ └── main [desktop].run.xml │ ├── README.md │ ├── build.gradle.kts │ ├── composeApp │ ├── build.gradle.kts │ └── src │ │ ├── androidMain │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── hoc081098 │ │ │ │ └── solivagant │ │ │ │ └── sample │ │ │ │ └── todo │ │ │ │ ├── AndroidTodoApp.kt │ │ │ │ ├── MainActivity.kt │ │ │ │ └── features │ │ │ │ └── utils │ │ │ │ ├── WeakReference.android.kt │ │ │ │ ├── isDebug.android.kt │ │ │ │ └── utils.android.kt │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ └── ic_launcher_background.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ └── strings.xml │ │ ├── commonMain │ │ ├── composeResources │ │ │ └── drawable │ │ │ │ └── compose-multiplatform.xml │ │ └── kotlin │ │ │ └── com │ │ │ └── hoc081098 │ │ │ └── solivagant │ │ │ └── sample │ │ │ └── todo │ │ │ ├── TodoApp.kt │ │ │ ├── data │ │ │ └── InMemoryTodoItemRepository.kt │ │ │ ├── di.kt │ │ │ ├── domain │ │ │ ├── TodoItem.kt │ │ │ └── TodoItemRepository.kt │ │ │ └── features │ │ │ ├── add │ │ │ ├── AddScreen.kt │ │ │ ├── AddViewModel.kt │ │ │ ├── domain │ │ │ │ └── AddTodoItem.kt │ │ │ └── navigation.kt │ │ │ ├── detail │ │ │ ├── DetailScreen.kt │ │ │ ├── DetailViewModel.kt │ │ │ ├── domain │ │ │ │ ├── ObserveTodoItemById.kt │ │ │ │ └── ToggleItemById.kt │ │ │ └── navigation.kt │ │ │ ├── edit │ │ │ ├── EditScreen.kt │ │ │ ├── EditViewModel.kt │ │ │ ├── domain │ │ │ │ ├── ObserveTodoItemById.kt │ │ │ │ └── UpdateTodoItem.kt │ │ │ └── navigation.kt │ │ │ ├── home │ │ │ ├── HomeScreen.kt │ │ │ ├── HomeViewModel.kt │ │ │ ├── domain │ │ │ │ ├── ObserveAllTodoItems.kt │ │ │ │ ├── RemoveItemById.kt │ │ │ │ └── ToggleItemById.kt │ │ │ └── navigation.kt │ │ │ └── utils │ │ │ ├── CollectWithLifecycleEffectDispatcher.kt │ │ │ ├── SingleEventFlow.kt │ │ │ ├── WeakReference.kt │ │ │ ├── debugCheckImmediateMainDispatcher.kt │ │ │ ├── isDebug.kt │ │ │ └── utils.kt │ │ ├── desktopMain │ │ └── kotlin │ │ │ └── com │ │ │ └── hoc081098 │ │ │ └── solivagant │ │ │ └── sample │ │ │ └── todo │ │ │ ├── features │ │ │ └── utils │ │ │ │ ├── WeakReference.desktop.kt │ │ │ │ ├── isDebug.desktop.kt │ │ │ │ └── utils.desktop.kt │ │ │ └── main.kt │ │ ├── iosMain │ │ └── kotlin │ │ │ └── com │ │ │ └── hoc081098 │ │ │ └── solivagant │ │ │ └── sample │ │ │ └── todo │ │ │ └── MainViewController.kt │ │ └── wasmJsMain │ │ ├── kotlin │ │ └── main.kt │ │ └── resources │ │ └── index.html │ ├── gradle.properties │ ├── gradle │ ├── libs.versions.toml │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── iosApp │ ├── Configuration │ │ └── Config.xcconfig │ ├── iosApp.xcodeproj │ │ └── project.pbxproj │ └── iosApp │ │ ├── Assets.xcassets │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ └── app-icon-1024.png │ │ └── Contents.json │ │ ├── ContentView.swift │ │ ├── Info.plist │ │ ├── Preview Content │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ │ └── iOSApp.swift │ ├── kotlin-js-store │ └── yarn.lock │ └── settings.gradle.kts ├── viewmodel-compose ├── api │ ├── android │ │ └── viewmodel-compose.api │ └── jvm │ │ └── viewmodel-compose.api ├── build.gradle.kts ├── gradle.properties └── src │ ├── androidInstrumentedTestDebug │ └── kotlin │ │ └── com │ │ └── hoc081098 │ │ └── kmp │ │ └── viewmodel │ │ └── compose │ │ └── KmpViewModelTest.android.kt │ ├── androidMain │ ├── AndroidManifest.xml │ └── kotlin │ │ └── com │ │ └── hoc081098 │ │ └── kmp │ │ └── viewmodel │ │ └── compose │ │ ├── internal │ │ └── getClassName.android.kt │ │ ├── kmpViewModel.android.kt │ │ └── viewModelStoreOwner.android.kt │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── hoc081098 │ │ └── kmp │ │ └── viewmodel │ │ └── compose │ │ ├── LocalSavedStateHandleFactory.kt │ │ ├── LocalViewModelStoreOwner.kt │ │ ├── defaultViewModelStoreOwner.kt │ │ ├── internal │ │ ├── getClassName.kt │ │ └── kClassOf.kt │ │ ├── kmpViewModel.kt │ │ ├── platformDefaults.kt │ │ └── rememberViewModelFactory.kt │ ├── jsAndWasmMain │ └── kotlin │ │ └── com │ │ └── hoc081098 │ │ └── kmp │ │ └── viewmodel │ │ └── compose │ │ └── canonicalName.jsAndWasm.kt │ ├── jvmTest │ └── kotlin │ │ └── com │ │ └── hoc081098 │ │ └── kmp │ │ └── viewmodel │ │ └── compose │ │ └── KmpViewModelTest.jvm.kt │ ├── nonAndroidMain │ └── kotlin │ │ └── com │ │ └── hoc081098 │ │ └── kmp │ │ └── viewmodel │ │ └── compose │ │ ├── DefaultViewModelStoreOwner.nonAndroid.kt │ │ ├── internal │ │ └── getClassName.nonAndroid.kt │ │ ├── kmpViewModel.nonAndroid.kt │ │ └── rememberDefaultViewModelKey.nonAndroid.kt │ └── nonJsAndNonAndroidMain │ └── kotlin │ └── com │ └── hoc081098 │ └── kmp │ └── viewmodel │ └── compose │ └── canonicalName.nonJsAndNonAndroid.kt ├── viewmodel-koin-compose ├── api │ ├── android │ │ └── viewmodel-koin-compose.api │ └── jvm │ │ └── viewmodel-koin-compose.api ├── build.gradle.kts ├── gradle.properties └── src │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── hoc081098 │ │ └── kmp │ │ └── viewmodel │ │ └── koin │ │ └── compose │ │ └── koinKmpViewModel.kt │ ├── commonTest │ └── kotlin │ │ └── com │ │ └── hoc081098 │ │ └── kmp │ │ └── viewmodel │ │ └── koin │ │ └── compose │ │ └── ViewModelKeyTest.kt │ └── jvmTest │ └── kotlin │ └── com │ └── hoc081098 │ └── kmp │ └── viewmodel │ └── koin │ └── compose │ └── KoinKmpViewModelTest.jvm.kt ├── viewmodel-koin ├── api │ ├── android │ │ └── viewmodel-koin.api │ └── jvm │ │ └── viewmodel-koin.api ├── build.gradle.kts ├── gradle.properties └── src │ ├── androidMain │ └── AndroidManifest.xml │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── hoc081098 │ │ └── kmp │ │ └── viewmodel │ │ └── koin │ │ └── koinViewModelFactory.kt │ └── commonTest │ └── kotlin │ └── com │ └── hoc081098 │ └── kmp │ └── viewmodel │ └── koin │ ├── KoinViewModelFactoryTest.common.kt │ └── test │ ├── TestSavedStateViewModel.kt │ ├── TestViewModel.kt │ ├── TestViewModelWithExtras.kt │ └── TestViewModelWithParams.kt ├── viewmodel-koject-compose ├── api │ ├── android │ │ └── viewmodel-koject-compose.api │ └── jvm │ │ └── viewmodel-koject-compose.api ├── build.gradle.kts ├── gradle.properties └── src │ └── commonMain │ └── kotlin │ └── com │ └── hoc081098 │ └── kmp │ └── viewmodel │ └── koject │ └── compose │ └── kojectKmpViewModel.kt ├── viewmodel-koject ├── api │ ├── android │ │ └── viewmodel-koject.api │ └── jvm │ │ └── viewmodel-koject.api ├── build.gradle.kts ├── gradle.properties └── src │ ├── androidMain │ └── AndroidManifest.xml │ └── commonMain │ └── kotlin │ └── com │ └── hoc081098 │ └── kmp │ └── viewmodel │ └── koject │ ├── ViewModelComponent.kt │ ├── ViewModelComponentExtras.kt │ └── kojectViewModelFactory.kt ├── viewmodel-savedstate ├── api │ ├── android │ │ └── viewmodel-savedstate.api │ └── jvm │ │ └── viewmodel-savedstate.api ├── build.gradle.kts ├── gradle.properties └── src │ ├── androidMain │ ├── AndroidManifest.xml │ └── kotlin │ │ └── com │ │ └── hoc081098 │ │ └── kmp │ │ └── viewmodel │ │ ├── SavedStateHandle.android.kt │ │ ├── SavedStateHandleSupport.android.kt │ │ ├── parcelable │ │ ├── IgnoredOnParcel.kt │ │ ├── Parcelable.kt │ │ ├── Parceler.kt │ │ ├── Parcelize.kt │ │ ├── TypeParceler.kt │ │ └── WriteWith.kt │ │ └── safe │ │ ├── SafeSavedStateHandle.android.kt │ │ └── internal │ │ └── parcelableArrayTransform.android.kt │ ├── androidUnitTest │ └── kotlin │ │ └── com │ │ └── hoc081098 │ │ └── kmp │ │ └── viewmodel │ │ └── safe │ │ ├── NonNullSavedStateHandleKeyAndroidTest.kt │ │ ├── NullableSavedStateHandleKeyAndroidTest.kt │ │ └── assertValue.kt │ ├── commonJvmMain │ └── kotlin │ │ └── com │ │ └── hoc081098 │ │ └── kmp │ │ └── viewmodel │ │ └── serializable │ │ └── JvmSerializable.commonJvm.kt │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── hoc081098 │ │ └── kmp │ │ └── viewmodel │ │ ├── SavedStateHandle.kt │ │ ├── SavedStateHandleFactory.kt │ │ ├── SavedStateHandleSupport.kt │ │ ├── parcelable │ │ ├── IgnoredOnParcel.kt │ │ ├── Parcelable.kt │ │ ├── Parceler.kt │ │ ├── Parcelize.kt │ │ ├── TypeParceler.kt │ │ └── WriteWith.kt │ │ ├── safe │ │ ├── InternalKmpViewModelApi.kt │ │ ├── NonNullSavedStateHandleKey.kt │ │ ├── NonNullSavedStateHandleKeys.kt │ │ ├── NullableSavedStateHandleKey.kt │ │ ├── NullableSavedStateHandleKeys.kt │ │ ├── SafeSavedStateHandle.kt │ │ └── internal │ │ │ ├── mapState.kt │ │ │ └── parcelableArrayTransform.kt │ │ └── serializable │ │ └── JvmSerializable.kt │ ├── commonTest │ └── kotlin │ │ └── com │ │ └── hoc081098 │ │ └── kmp │ │ └── viewmodel │ │ ├── SavedStateHandleTest.kt │ │ ├── TestParcelable.kt │ │ ├── TestSerializable.kt │ │ ├── extendedAssertEquals.kt │ │ └── safe │ │ ├── NonNullSavedStateHandleKeyTest.kt │ │ └── NullableSavedStateHandleKeyTest.kt │ ├── nonAndroidMain │ └── kotlin │ │ └── com │ │ └── hoc081098 │ │ └── kmp │ │ └── viewmodel │ │ ├── SavedStateHandle.nonAndroid.kt │ │ ├── SavedStateHandleSupport.nonAndroid.kt │ │ ├── parcelable │ │ ├── IgnoredOnParcel.kt │ │ ├── Parcelable.kt │ │ ├── Parceler.kt │ │ ├── Parcelize.kt │ │ ├── TypeParceler.kt │ │ └── WriteWith.kt │ │ └── safe │ │ └── internal │ │ └── parcelableArrayTransform.nonAndroid.kt │ └── nonJvmMain │ └── kotlin │ └── com │ └── hoc081098 │ └── kmp │ └── viewmodel │ └── serializable │ └── JvmSerializable.nonJvm.kt └── viewmodel ├── api ├── android │ └── viewmodel.api └── jvm │ └── viewmodel.api ├── build.gradle.kts ├── gradle.properties └── src ├── androidMain ├── AndroidManifest.xml └── kotlin │ └── com │ └── hoc081098 │ └── kmp │ └── viewmodel │ ├── Closeable.android.kt │ ├── CreationExtras.android.kt │ ├── MainThread.android.kt │ ├── ViewModel.android.kt │ ├── ViewModelFactory.android.kt │ ├── ViewModelStore.android.kt │ └── ViewModelStoreOwner.android.kt ├── androidUnitTest └── kotlin │ └── com │ └── hoc081098 │ └── kmp │ └── viewmodel │ ├── ViewModelStoreTest.android.kt │ └── utils │ ├── TestAtomicBoolean.android.kt │ └── runBlockInNewThread.android.kt ├── commonMain └── kotlin │ └── com │ └── hoc081098 │ └── kmp │ └── viewmodel │ ├── Closeable.kt │ ├── CreationExtras.kt │ ├── InternalKmpViewModelApi.kt │ ├── MainThread.kt │ ├── ViewModel.kt │ ├── ViewModelFactory.kt │ ├── ViewModelStore.kt │ ├── ViewModelStoreOwner.kt │ ├── creationExtrasKtx.kt │ └── wrapper │ ├── AbstractFlowWrapper.kt │ ├── Joinable.kt │ ├── NonNullFlowWrapper.kt │ ├── NonNullStateFlowWrapper.kt │ ├── NullableFlowWrapper.kt │ ├── NullableStateFlowWrapper.kt │ └── flow.kt ├── commonTest └── kotlin │ └── com │ └── hoc081098 │ └── kmp │ └── viewmodel │ ├── CreationExtrasTest.kt │ ├── ViewModelStoreTest.kt │ ├── ViewModelTest.kt │ ├── utils │ ├── TestAtomicBoolean.kt │ ├── TestCloseable.kt │ └── runBlockInNewThread.kt │ └── wrapper │ ├── FlowWrapperTest.kt │ ├── NonNullFlowWrapperTest.kt │ ├── NonNullStateFlowWrapperTest.kt │ ├── NullableFlowWrapperTest.kt │ └── NullableStateFlowWrapperTest.kt ├── jsAndWasmMain └── kotlin │ └── com │ └── hoc081098 │ └── kmp │ └── viewmodel │ ├── Closable.jsAndWasm.kt │ ├── Lockable.jsAndWasm.kt │ └── internal │ ├── AtomicBoolean.jsAndWasm.kt │ └── synchronized.jsAndWasm.kt ├── jsAndWasmTest └── kotlin │ └── com │ └── hoc081098 │ └── kmp │ └── viewmodel │ └── utils │ └── runBlockInNewThread.jsAndWasm.kt ├── jvmMain └── kotlin │ └── com │ └── hoc081098 │ └── kmp │ └── viewmodel │ ├── Closeable.jvm.kt │ ├── Lockable.jvm.kt │ └── internal │ ├── AtomicBoolean.kt │ └── synchronized.kt ├── jvmTest └── kotlin │ └── com │ └── hoc081098 │ └── kmp │ └── viewmodel │ └── utils │ └── runBlockInNewThread.jvm.kt ├── nativeMain └── kotlin │ └── com │ └── hoc081098 │ └── kmp │ └── viewmodel │ ├── Closeable.native.kt │ ├── Lockable.native.kt │ └── internal │ ├── AtomicBoolean.native.kt │ └── synchronized.native.kt ├── nativeTest └── kotlin │ └── com │ └── hoc081098 │ └── kmp │ └── viewmodel │ ├── SynchronizedTest.native.kt │ └── utils │ └── runBlockInNewThread.native.kt ├── nonAndroidMain └── kotlin │ └── com │ └── hoc081098 │ └── kmp │ └── viewmodel │ ├── CreationExtras.nonAndroid.kt │ ├── Lockable.kt │ ├── MainThread.nonAndroid.kt │ ├── ViewModel.nonAndroid.kt │ ├── ViewModelStore.nonAndroid.kt │ └── internal │ ├── AtomicBoolean.kt │ └── synchronized.kt └── nonAndroidTest └── kotlin └── com └── hoc081098 └── kmp └── viewmodel ├── LockableObjectTest.nonAndroid.kt ├── ViewModelStoreTest.nonAndroid.kt ├── ViewModelTest.nonAndroid.kt ├── internal └── AtomicBooleanTest.nonAndroid.kt └── utils └── TestAtomicBoolean.nonAndroid.kt /.editorconfig: -------------------------------------------------------------------------------- 1 | root=true 2 | [*] 3 | indent_size=2 4 | end_of_line=lf 5 | charset=utf-8 6 | trim_trailing_whitespace=true 7 | insert_final_newline=true 8 | [*.{kt,kts}] 9 | ij_kotlin_imports_layout=* 10 | ij_continuation_indent_size=4 11 | ktlint_standard_filename=disabled 12 | ktlint_standard_package-name=disabled 13 | ktlint_standard_property-naming=disabled 14 | ktlint_standard_function-naming=disabled 15 | filename=disabled 16 | ktlint_experimental=enabled 17 | [*.xml] 18 | indent_size=4 19 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [hoc081098] 4 | # patreon: # Replace with a single Patreon username 5 | # open_collective: # Replace with a single Open Collective username 6 | # ko_fi: # Replace with a single Ko-fi username 7 | # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | # liberapay: # Replace with a single Liberapay username 10 | # issuehunt: # Replace with a single IssueHunt username 11 | # otechie: # Replace with a single Otechie username 12 | # lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | buy_me_a_coffee: hoc081098 14 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gradle" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.github/workflows/gradle-wrapper-validation.yml: -------------------------------------------------------------------------------- 1 | name: Validate Gradle Wrapper 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | validation: 6 | name: "Validation" 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: gradle/actions/wrapper-validation@v4 11 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | /artifacts 5 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoc081098/kmp-viewmodel/b71752abffe9e851514de5265f11479631e66923/.idea/icon.png -------------------------------------------------------------------------------- /.idea/jsonSchemas.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023-2024 Petrus Nguyễn Thái Học 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | # Releasing 2 | 3 | 1. Update the `VERSION_NAME` in `gradle.properties` to the release version. 4 | 5 | 2. Update the `CHANGELOG.md`. 6 | 7 | 3. Update the `README.md` so the "Download" section reflects the new release version and the 8 | snapshot section reflects the next "SNAPSHOT" version. 9 | 10 | 4. Commit 11 | 12 | ``` 13 | $ git commit -am "Prepare version X.Y.Z" 14 | ``` 15 | 16 | 5. Tag 17 | 18 | ``` 19 | $ git tag -am "Version X.Y.Z" X.Y.Z 20 | ``` 21 | 22 | 6. Update the `VERSION_NAME` in `gradle.properties` to the next "SNAPSHOT" version. 23 | 24 | 7. Commit 25 | 26 | ``` 27 | $ git commit -am "Prepare next development version" 28 | ``` 29 | 30 | 8. Push! 31 | 32 | ``` 33 | $ git push && git push --tags 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /docs/images/logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoc081098/kmp-viewmodel/b71752abffe9e851514de5265f11479631e66923/docs/images/logo.jpeg -------------------------------------------------------------------------------- /docs/multiplatform.md: -------------------------------------------------------------------------------- 1 | # Multiplatform 2 | 3 | ## Supported targets 4 | 5 | - `android`. 6 | - `jvm` (must add `kotlinx-coroutines-swing`/`kotlinx-coroutines-javafx` to your dependencies to 7 | make sure `Dispatchers.Main` available). 8 | 9 | > [!NOTE] 10 | > If you are targeting `Desktop` and: 11 | > - not using `JetBrains Compose Multiplatform`, you should provide the dependency `org.jetbrains.kotlinx:kotlinx-coroutines-swing` **or** `org.jetbrains.kotlinx:kotlinx-coroutines-javafx`. 12 | > - using `JetBrains Compose Multiplatform`, you should provide `org.jetbrains.kotlinx:kotlinx-coroutines-swing`. 13 | > 14 | > Because the `ViewModel.viewModelScope` depends on `Dispatchers.Main` provided by that libraries on Desktop. 15 | 16 | - `js` (`IR`). 17 | - `wasmJs`. 18 | - `Darwin` targets: 19 | - `iosArm64`, `iosX64`, `iosSimulatorArm64`. 20 | - `watchosArm32`, `watchosArm64`, `watchosX64`, `watchosSimulatorArm64`. 21 | - `tvosX64`, `tvosSimulatorArm64`, `tvosArm64`. 22 | - `macosX64`, `macosArm64`. 23 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | mkdocs==1.6.1 2 | mkdocs-material==9.6.9 3 | pymdown-extensions==10.14.3 4 | mdx_truly_sane_lists==1.3 # https://github.com/radude/mdx_truly_sane_lists 5 | -------------------------------------------------------------------------------- /docs/stylesheets/extra.css: -------------------------------------------------------------------------------- 1 | /* https://github.com/UBCSailbot/sailbot_workspace/blob/main/docs/stylesheets/extra.css */ 2 | /* Styling for embedded videos*/ 3 | .video-wrapper { 4 | position: relative; 5 | display: block; 6 | height: 0; 7 | padding: 0; 8 | overflow: hidden; 9 | padding-bottom: 56.25%; 10 | } 11 | .video-wrapper > iframe { 12 | position: absolute; 13 | top: 0; 14 | bottom: 0; 15 | left: 0; 16 | width: 100%; 17 | height: 100%; 18 | border: 0; 19 | } -------------------------------------------------------------------------------- /docs/viewmodel-compose-navigation.md: -------------------------------------------------------------------------------- 1 | # ViewModel and Navigation for JetBrains Compose Multiplatform 2 | 3 | For more information check out https://github.com/hoc081098/solivagant library. 4 | **Solivagant** is a library that provides the Navigation for **JetBrains Compose Multiplatform**. 5 | It's pragmatic, type safety navigation and more... 6 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4g -Dkotlin.daemon.jvm.options=-Xmx512m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC -Dfile.encoding=UTF-8 2 | 3 | kotlin.code.style=official 4 | kotlin.js.generate.executable.default=false 5 | 6 | # android 7 | android.useAndroidX=true 8 | android.defaults.buildfeatures.buildconfig=false 9 | 10 | # gradle 11 | org.gradle.configureondemand=true 12 | org.gradle.caching=true 13 | org.gradle.parallel=true 14 | 15 | # kotlin mpp 16 | kotlin.mpp.stability.nowarn=true 17 | kotlin.js.compiler=ir 18 | kotlin.native.ignoreDisabledTargets=true 19 | 20 | # kotlin incremental 21 | kotlin.incremental.multiplatform=true 22 | kotlin.incremental.useClasspathSnapshot=true 23 | kotlin.incremental=true 24 | 25 | # POM 26 | GROUP=io.github.hoc081098 27 | # HEY! If you change the major version here be sure to update publish-release.yaml doc target folder! 28 | VERSION_NAME=0.8.1-SNAPSHOT 29 | POM_INCEPTION_YEAR=2023 30 | 31 | POM_URL=https://github.com/hoc081098/kmp-viewmodel 32 | POM_SCM_URL=https://github.com/hoc081098/kmp-viewmodel 33 | POM_SCM_CONNECTION=scm:git:git://github.com/hoc081098/kmp-viewmodel 34 | POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/hoc081098/kmp-viewmodel.git 35 | 36 | POM_LICENCE_NAME=MIT License 37 | POM_LICENCE_URL=https://opensource.org/licenses/mit-license.php 38 | POM_LICENCE_DIST=repo 39 | 40 | POM_DEVELOPER_ID=hoc081098 41 | POM_DEVELOPER_NAME=Petrus Nguyen Thai Hoc 42 | POM_DEVELOPER_URL=https://github.com/hoc081098 43 | 44 | #MPP 45 | kotlin.mpp.enableCInteropCommonization=true 46 | kotlin.mpp.androidSourceSetLayoutVersion=2 47 | 48 | # OSSRH sometimes struggles with slow deployments, so this makes Gradle 49 | # more tolerant to those delays. 50 | SONATYPE_CONNECT_TIMEOUT_SECONDS=300 51 | SONATYPE_CLOSE_TIMEOUT_SECONDS=1800 52 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoc081098/kmp-viewmodel/b71752abffe9e851514de5265f11479631e66923/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoc081098/kmp-viewmodel/b71752abffe9e851514de5265f11479631e66923/logo.png -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | .idea 4 | .DS_Store 5 | build 6 | captures 7 | .externalNativeBuild 8 | .cxx 9 | local.properties 10 | xcuserdata -------------------------------------------------------------------------------- /sample/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /sample/app/src/main/java/com/hoc081098/kmpviewmodelsample/android/MyApp.kt: -------------------------------------------------------------------------------- 1 | package com.hoc081098.kmpviewmodelsample.android 2 | 3 | import android.app.Application 4 | import coil.ImageLoader 5 | import coil.ImageLoaderFactory 6 | import com.hoc081098.kmpviewmodelsample.BuildConfig 7 | import com.hoc081098.kmpviewmodelsample.setupNapier 8 | import com.hoc081098.kmpviewmodelsample.startKoinCommon 9 | import org.koin.android.ext.koin.androidContext 10 | import org.koin.android.ext.koin.androidLogger 11 | import org.koin.core.logger.Level 12 | 13 | class MyApp : Application(), ImageLoaderFactory { 14 | override fun onCreate() { 15 | super.onCreate() 16 | 17 | setupNapier() 18 | startKoinCommon { 19 | androidContext(this@MyApp) 20 | androidLogger( 21 | if (BuildConfig.DEBUG) { 22 | Level.DEBUG 23 | } else { 24 | Level.ERROR 25 | }, 26 | ) 27 | } 28 | } 29 | 30 | override fun newImageLoader(): ImageLoader { 31 | return ImageLoader.Builder(this) 32 | .crossfade(true) 33 | .logger( 34 | if (BuildConfig.DEBUG) { 35 | coil.util.DebugLogger() 36 | } else { 37 | null 38 | }, 39 | ) 40 | .build() 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /sample/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 |