Change Log¶
0.8.0 - Jun 9, 2024¶
Update dependencies¶
- Kotlin
2.0.0🎉. - AndroidX Lifecycle
2.8.1. - JetBrains Compose Multiplatform
1.6.11. - KotlinX Coroutines
1.8.1. - Touchlab Stately
2.0.7. - Koin Core
3.5.6, Koin Compose1.1.5.
kmp-viewmodel-savedstate¶
-
Added
JvmSerializable- multiplatform reference to Javajava.io.Serializableinterface, along withNonNullSavedStateHandleKey.Companion.serializableandNullableSavedStateHandleKey.Companion.serializable// Use `JvmSerializable` with enum classes. enum class Gender : JvmSerializable { MALE, FEMALE, } // Create a `NonNullSavedStateHandleKey` for a serializable type. private val genderKey: NonNullSavedStateHandleKey<Gender> = NonNullSavedStateHandleKey.serializable( key = "gender", defaultValue = Gender.MALE, ) // Use `SavedStateHandle.safe` extension function to access `SavedStateHandle` in a type-safe way. val genderStateFlow: NonNullStateFlowWrapper<Gender> = savedStateHandle .safe { it.getStateFlow(genderKey) } .wrap()
-
Since Kotlin 2.0.0, you must add
"plugin:org.jetbrains.kotlin.parcelize:additionalAnnotation=com.hoc081098.kmp.viewmodel.parcelable.Parcelize"as a free compiler argument to able to use@Parcelizeannotation in the common/shared module (Kotlin Multiplatform module).// build.gradle.kts plugins { id("kotlin-parcelize") // Apply the plugin for Android } // Since Kotlin 2.0.0, you must add the below code to your build.gradle.kts of the common/shared module (Kotlin Multiplatform module). kotlin { [...] // Other configurations targets.configureEach { val isAndroidTarget = platformType == org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType.androidJvm compilations.configureEach { compileTaskProvider.configure { compilerOptions { if (isAndroidTarget) { freeCompilerArgs.addAll( "-P", "plugin:org.jetbrains.kotlin.parcelize:additionalAnnotation=com.hoc081098.kmp.viewmodel.parcelable.Parcelize", ) } } } } } }
0.7.1 - March 2, 2024¶
kmp-viewmodel-compose¶
- JetBrains Compose Multiplatform
1.6.0. - New: Add support for Kotlin/Wasm (
wasmJstarget) 🎉.
Added kmp-viewmodel-koject and kmp-viewmodel-koject-compose artifacts¶
- For more information check out the docs/0.x/viewmodel-koject-compose
- The Koject dependency are used in
kmp-viewmodel-koject-compose:com.moriatsushi.koject:koject-core:1.3.0.
-
New The
kmp-viewmodel-kojectartifact provides the integration ofkmp-viewmodel,kmp-viewmodel-composeandKoject, helps us to retrieveViewModelfrom the Koject DI container without manually dependency injection.@Provides @Singleton class MyRepository @Provides @ViewModelComponent // <-- To inject SavedStateHandle class MyViewModel( val myRepository: MyRepository, val savedStateHandle: SavedStateHandle, ) : ViewModel() { // ... } @Composable fun MyScreen( viewModel: MyViewModel = kojectKmpViewModel(), ) { // ... }
Example, docs and tests¶
- Add Compose Multiplatform Koject sample
which shares
ViewModels and integrates withNavigationin Compose Multiplatform. It usesKojectfor DI.
0.7.0 - Feb 17, 2024¶
Update dependencies¶
- AndroidX Lifecycle
2.7.0. - Android target: update
Compile SDKandTarget SDKto34. - KotlinX Coroutines
1.8.0.
kmp-viewmodel and kmp-viewmodel-savedstate¶
- New: Add support for Kotlin/Wasm (
wasmJstarget) 🎉. - The behavior of
ViewModel.addCloseable(Closeable)on non-Android targets has been changed to be consistent with Android target.ViewModel’saddCloseable()now immediately closes theCloseableif theViewModelhas been cleared. This behavior is the same across all targets ✅.
kmp-viewmodel-koin¶
-
Fixed:
koinViewModelFactory:CreationExtraspassed toViewModelFactory.createwill now be passed to the constructor of the ViewModel if it’s requested.class MyViewModel(val extras: CreationExtras) : ViewModel() val myModule: Module = module { factoryOf(::MyViewModel) } val factory = koinViewModelFactory<MyViewModel>( scope = KoinPlatformTools.defaultContext().get().scopeRegistry.rootScope, ) val extras = buildCreationExtras { /* ... */ } val viewModel: MyViewModel = factory.create(extras) viewModel.extras === extras // true <--- `viewModel.extras` is the same as `extras` passed to `factory.create(extras)`
Example, docs and tests¶
- Add more tests to
kmp-viewmodel-compose(android & jvm),kmp-viewmodel-koin(common), andkmp-viewmodel-koin-compose(common & jvm).
0.6.2 - Feb 5, 2024¶
Update dependencies¶
Added kmp-viewmodel-koin and kmp-viewmodel-koin-compose artifacts¶
- For more information check out the docs/0.x/viewmodel-koin-compose
- The Koin dependencies are used in
kmp-viewmodel-koin-compose:io.insert-koin:koin-core:3.5.3.io.insert-koin:koin-compose:1.1.2.
-
New The
kmp-viewmodel-koinartifact provides the integration ofkmp-viewmodel,kmp-viewmodel-composeandKoin, helps us to retrieveViewModelfrom the Koin DI container without manually dependency injection.class MyRepository class MyViewModel( val myRepository: MyRepository, val savedStateHandle: SavedStateHandle, val id: Int, ) : ViewModel() { // ... } val myModule: Module = module { factoryOf(::MyRepository) factoryOf(::MyViewModel) } @Composable fun MyScreen( id: Int, viewModel: MyViewModel = koinKmpViewModel( key = "MyViewModel-$id", parameters = { parametersOf(id) } ) ) { // ... }
Added type-safe API for SavedStateHandle¶
- For more information check out the docs/0.x/viewmodel-savedstate-safe
-
New The
kmp-viewmodel-savedstateartifact provides the type-safe API that allows you to accessSavedStateHandlein a type-safe way.private val searchTermKey: NonNullSavedStateHandleKey<String> = NonNullSavedStateHandleKey.string( key = "search_term", defaultValue = "" ) // Use `SavedStateHandle.safe` extension function to access `SavedStateHandle` in a type-safe way. savedStateHandle.safe { it[searchTermKey] = searchTerm } savedStateHandle.safe { it.getStateFlow(searchTermKey) } // Or use `SavedStateHandle.safe` extension property to access `SavedStateHandle` in a type-safe way. savedStateHandle.safe[searchTermKey] = searchTerm savedStateHandle.safe.getStateFlow(searchTermKey)
kmp-viewmodel-compose artifact¶
-
New Add
rememberViewModelFactorys to remember theViewModelFactorys in@Composablefunctions. They acceptbuilder: @DisallowComposableCalls CreationExtras.() -> VMs.class MyViewModel(savedStateHandle: SavedStateHandle): ViewModel() @Composable fun MyScreen() { val factory: ViewModelFactory<MyViewModel> = rememberViewModelFactory { MyViewModel(savedStateHandle = createSavedStateHandle()) } val viewModel: MyViewModel = kmpViewModel(factory = factory) // ... }
-
New Add a new
kmpViewModeloverload that acceptsfactory: @DisallowComposableCalls CreationExtras.() -> VM(Previously, it only acceptsfactory: ViewModelFactory<VM>).class MyViewModel(savedStateHandle: SavedStateHandle): ViewModel() @Composable fun MyScreen( viewModel: MyViewModel = kmpViewModel { MyViewModel(savedStateHandle = createSavedStateHandle()) } ) { // ... }
The above
kmpViewModelusesrememberViewModelFactoryinternally. UserememberViewModelFactory { ... }andkmpViewModel(factory = factory)is the same as usingkmpViewModel { ... }.
0.6.1 - Dec 10, 2023¶
viewmodel¶
- On non-Android targets:
ViewModel.viewModelScopedoes not useDispatchers.Defaultas a fallback. That means theCoroutineDispatcherofViewModel.viewModelScopeisDispatchers.Main.immediateorDispatchers.Main.
Example, docs¶
- Refactor example code.
- Add NOTE about the
kotlinx-coroutinesdependency when targetingDesktop(aka.jvm).
0.6.0 - Dec 8, 2023¶
Update dependencies¶
Removed¶
- Remove now-unsupported targets:
iosArm32,watchosX86.
viewmodel¶
-
MutableCreationExtrashas been renamed toMutableCreationExtrasBuilder, and it does not inherit fromCreationExtrasanymore. Because of this, a new methodMutableCreationExtrasBuilder.asCreationExtras()has been introduced can be used to convert a builder back toCreationExtrasas needed.NOTE:
buildCreationExtrasandCreationExtras.editmethods are still the same as before.// Old version (0.5.0) val creationExtras: CreationExtras = MutableCreationExtras().apply { // ... } // New version (0.6.0): `MutableCreationExtras` does not inherit from `CreationExtras` anymore. val creationExtras: CreationExtras = MutableCreationExtrasBuilder().apply { // ... }.asCreationExtras() // <--- asCreationExtras: convert a builder back to `CreationExtras` as needed.More details: with Kotlin 1.9.20, an expect with default arguments are no longer permitted when an actual is a typealias (see KT-57614), we cannot use
actual typealias MutableCreationExtras = androidx.lifecycle.viewmodel.MutableCreationExtras. So we have to use wrapper class instead.
- Update the docs of
ViewModel.viewModelScopeto clarify that the scope is thread-safe on both Android and non-Android targets.
- On non-Android targets
ViewModel.clear()method has been refactored to improve the performance.- Any
Exceptionthrown fromCloseable.close()will be re-thrown asRuntimeException.
0.5.0 - Sep 27, 2023¶
Update dependencies¶
- Kotlin
1.9.0. - AndroidX Lifecycle
2.6.1. - KotlinX Coroutines
1.7.3. - Android Gradle Plugin
8.1.0.
viewmodel¶
- Add
ViewModelStoreandViewModelStoreOwner. - Add
ViewModelFactoryandVIEW_MODEL_KEY. - Add
CreationExtrasandCreationExtrasKey. - Add
buildCreationExtrasandCreationExtras.edit. - Add
ViewModel.isCleared()method to check if theViewModelis cleared, only available on non-Android targets. - Add
MainThread(moved fromviewmodel-savedstatemodule).
viewmodel-savedstate¶
- Remove
MainThread(moved toviewmodelmodule). - Add
SavedStateHandleFactoryinterface. - Add
SAVED_STATE_HANDLE_FACTORY_KEYandCreationExtras.createSavedStateHandle().
viewmodel-compose¶
- A new module allows to access
ViewModels in Jetpack Compose Multiplatform.kmpViewModelto retrieveViewModels in @Composable functions.LocalSavedStateHandleFactoryandSavedStateHandleFactoryProviderto get/provideSavedStateHandleFactoryin @Composable functions. It allows integration with any navigation library.LocalViewModelStoreOwnerandViewModelStoreOwnerProviderto get/provideViewModelStoreOwnerin @Composable functions. It allows integration with any navigation library.defaultPlatformCreationExtrasanddefaultPlatformViewModelStoreOwnerto get the defaultCreationExtrasandViewModelStoreOwner, which depends on the platform.
- Dependencies: Compose Multiplatform 1.5.0.
- Docs: 0.x Viewmodel-Compose docs.
Example, docs and tests¶
- Refactor example code.
- Add Compose Multiplatform Koin sample
which shares
ViewModels and integrates withNavigationin Compose Multiplatform.
- Add Compose Multiplatform KmpViewModel KMM Unsplash Sample, a KMP template of the Unsplash App using Compose multiplatform for Android, Desktop, iOS. Share everything including data, domain, presentation, and UI.
- Add more docs: 0.x docs.
- Add more tests.
0.4.0 - Apr 7, 2023¶
Changed¶
Update dependencies¶
- Kotlin
1.8.10. - Target
Java 11. - Touchlab Stately
1.2.5. - AndroidX Lifecycle
2.6.0. - Android Gradle Plugin
7.4.2.
Flow wrappers¶
- Add
NonNullStateFlowWrapperandNullableFlowWrapperto common source set.
- Move all
Flowwrappers to common source set. Previously, they were only available forDarwin targets(iOS,macOS,tvOS,watchOS).
-
Add
Flow.wrap()extension methods to wrapFlows sources:Flow<T: Any>.wrap(): NonNullFlowWrapper<T>.Flow<T>.wrap(): NullableFlowWrapper<T>.StateFlow<T: Any>.wrap(): NonNullStateFlowWrapper<T>.StateFlow<T>.wrap(): NullableStateFlowWrapper<T>.
In common code, you can use these methods to wrap
Flowsources and use them in Swift code easily.// Kotlin code data class State(...) class SharedViewModel : ViewModel() { private val _state = MutableStateFlow(State(...)) val stateFlow: NonNullStateFlowWrapper<State> = _state.wrap() }// Swift code @MainActor class IosViewModel: ObservableObject { private let vm: SharedViewModel @Published private(set) var state: State init(viewModel: SharedViewModel) { vm = viewModel state = vm.stateFlow.value // <--- Use `value` property with type safety (do not need to cast). vm.stateFlow.subscribe( // <--- Use `subscribe(scope:onValue:)` method directly. scope: vm.viewModelScope, onValue: { [weak self] in self?.state = $0 } ) } deinit { vm.clear() } }
Example, docs and tests¶
- Refactor example code.
- Add more docs: 0.x docs.
- Add more tests.
0.3.0 - Mar 18, 2023¶
Added¶
- Add
NonNullFlowWrapperandNullableFlowWrapper, that are wrappers forFlows that provides a more convenient API for subscribing to theFlows onDarwin targets(iOS,macOS,tvOS,watchOS)// Kotlin code val flow: StateFlow<Int>// Swift code NonNullFlowWrapper<KotlinInt>(flow: flow).subscribe( scope: scope, onValue: { print("Received ", $0) } )
Changed¶
- Add more example, refactor example code.
- Add more docs: 0.x docs.
- Add more tests.
- Gradle
8.0.2. - Dokka
1.8.10.
0.2.0 - Mar 5, 2023¶
Added¶
-
Add
kmp-viewmodel-savedstateartifact. This artifact brings:- Android Parcelable interface.
- The
@Parcelizeannotation from kotlin-parcelize compiler plugin. - SavedStateHandle class.
to Kotlin Multiplatform, so they can be used in common code. This is typically used for state/data preservation over Android configuration changes and system-initiated process death , when writing common code targeting Android.
Changed¶
- Add more example, refactor example code.
- Add more docs: 0.x docs.
0.1.0 - Feb 18, 2023¶
Changed¶
- Make
ViewModel.viewModelScopepublic.
Added¶
- Add an
ViewModel.addCloseableAPI and a new constructor overloadconstructor(vararg closeables: Closeable), that allow you to add one or moreCloseableobjects to theViewModelthat will be closed when theViewModelis cleared without requiring any manual work inonCleared().
0.0.1 - Feb 11, 2023¶
- Initial release.