Releases: hoc081098/kmp-viewmodel
0.8.0
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.Serializable
interface,
along withNonNullSavedStateHandleKey.Companion.serializable
andNullableSavedStateHandleKey.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@Parcelize
annotation 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
kmp-viewmodel-compose
- JetBrains Compose Multiplatform
1.6.0
. - New: Add support for Kotlin/Wasm (
wasmJs
target) 🎉.
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-koject
artifact provides the integration ofkmp-viewmodel
,kmp-viewmodel-compose
andKoject
,
helps us to retrieveViewModel
from 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 sharesViewModel
s and integrates withNavigation
in Compose Multiplatform. It usesKoject
for DI.
0.7.0
Update dependencies
- AndroidX Lifecycle
2.7.0
. - Android target: update
Compile SDK
andTarget SDK
to34
. - KotlinX Coroutines
1.8.0
.
kmp-viewmodel
and kmp-viewmodel-savedstate
- New: Add support for Kotlin/Wasm (
wasmJs
target) 🎉. - The behavior of
ViewModel.addCloseable(Closeable)
on non-Android targets has been changed to be consistent with Android target.
ViewModel
'saddCloseable()
now immediately closes theCloseable
if theViewModel
has been cleared.
This behavior is the same across all targets ✅.
kmp-viewmodel-koin
-
Fixed:
koinViewModelFactory
:CreationExtras
passed toViewModelFactory.create
will 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
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-koin
artifact provides the integration ofkmp-viewmodel
,kmp-viewmodel-compose
andKoin
,
helps us to retrieveViewModel
from 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-savedstate
artifact provides the type-safe API
that allows you to accessSavedStateHandle
in 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
rememberViewModelFactory
s to remember theViewModelFactory
s in@Composable
functions.
They acceptbuilder: @DisallowComposableCalls CreationExtras.() -> VM
s.
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
kmpViewModel
overload 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
kmpViewModel
usesrememberViewModelFactory
internally.
UserememberViewModelFactory { ... }
andkmpViewModel(factory = factory)
is the same as usingkmpViewModel { ... }
.
0.6.1
viewmodel
- On non-Android targets:
ViewModel.viewModelScope
does not useDispatchers.Default
as a fallback.
That means theCoroutineDispatcher
ofViewModel.viewModelScope
isDispatchers.Main.immediate
orDispatchers.Main
.
Example, docs
- Refactor example code.
- Add NOTE about the
kotlinx-coroutines
dependency when targetingDesktop
(aka.jvm
).
0.6.0
Update dependencies
Removed
- Remove now-unsupported targets:
iosArm32
,watchosX86
.
viewmodel
-
MutableCreationExtras
has been renamed toMutableCreationExtrasBuilder
,
and it does not inherit fromCreationExtras
anymore.
Because of this, a new method
MutableCreationExtrasBuilder.asCreationExtras()
has been introduced can be used to convert
a builder back toCreationExtras
as needed.NOTE:
buildCreationExtras
andCreationExtras.edit
methods 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
useactual typealias MutableCreationExtras = androidx.lifecycle.viewmodel.MutableCreationExtras
.
So we have to use wrapper class instead. -
Update the docs of
ViewModel.viewModelScope
to 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
Exception
thrown fromCloseable.close()
will be re-thrown asRuntimeException
.
0.5.0
Update dependencies
- Kotlin
1.9.0
. - AndroidX Lifecycle
2.6.1
. - KotlinX Coroutines
1.7.3
. - Android Gradle Plugin
8.1.0
.
viewmodel
- Add
ViewModelStore
andViewModelStoreOwner
. - Add
ViewModelFactory
andVIEW_MODEL_KEY
. - Add
CreationExtras
andCreationExtrasKey
. - Add
buildCreationExtras
andCreationExtras.edit
. - Add
ViewModel.isCleared()
method to check if theViewModel
is cleared, only available on
non-Android targets. - Add
MainThread
(moved fromviewmodel-savedstate
module).
viewmodel-savedstate
- Remove
MainThread
(moved toviewmodel
module). - Add
SavedStateHandleFactory
interface. - Add
SAVED_STATE_HANDLE_FACTORY_KEY
andCreationExtras.createSavedStateHandle()
.
viewmodel-compose
-
A new module allows to access
ViewModel
s in Jetpack Compose Multiplatform.kmpViewModel
to retrieveViewModel
s in @composable functions.LocalSavedStateHandleFactory
andSavedStateHandleFactoryProvider
to
get/provideSavedStateHandleFactory
in @composable functions.
It allows integration with any navigation library.LocalViewModelStoreOwner
andViewModelStoreOwnerProvider
to
get/provideViewModelStoreOwner
in @composable functions.
It allows integration with any navigation library.defaultPlatformCreationExtras
anddefaultPlatformViewModelStoreOwner
to get the defaultCreationExtras
andViewModelStoreOwner
,
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 sample
which sharesViewModel
s and integrates withNavigation
in 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
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
NonNullStateFlowWrapper
andNullableFlowWrapper
to common source set. -
Move all
Flow
wrappers to common source set.
Previously, they were only available forDarwin targets
(iOS
,macOS
,tvOS
,watchOS
). -
Add
Flow.wrap()
extension methods to wrapFlow
s 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
Flow
sources 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
Added
- Add
NonNullFlowWrapper
andNullableFlowWrapper
, that are wrappers forFlow
s
that provides a more convenient API for subscribing to theFlow
s 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
Added
-
Add
kmp-viewmodel-savedstate
artifact. This artifact brings:- Android Parcelable interface.
- The
@Parcelize
annotation
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.