diff --git a/state-keeper/api/state-keeper.klib.api b/state-keeper/api/state-keeper.klib.api index f2a7053..2b70f2b 100644 --- a/state-keeper/api/state-keeper.klib.api +++ b/state-keeper/api/state-keeper.klib.api @@ -41,9 +41,9 @@ final class com.arkivanov.essenty.statekeeper/SerializableContainer { // com.ark final fun <#A: kotlin/Any> (com.arkivanov.essenty.statekeeper/SerializableContainer).com.arkivanov.essenty.statekeeper/consumeRequired(kotlinx.serialization/DeserializationStrategy<#A>): #A // com.arkivanov.essenty.statekeeper/consumeRequired|consumeRequired@com.arkivanov.essenty.statekeeper.SerializableContainer(kotlinx.serialization.DeserializationStrategy<0:0>){0§}[0] final fun <#A: kotlin/Any> com.arkivanov.essenty.statekeeper/SerializableContainer(#A?, kotlinx.serialization/SerializationStrategy<#A>): com.arkivanov.essenty.statekeeper/SerializableContainer // com.arkivanov.essenty.statekeeper/SerializableContainer|SerializableContainer(0:0?;kotlinx.serialization.SerializationStrategy<0:0>){0§}[0] final fun <#A: kotlin/Any> com.arkivanov.essenty.statekeeper/polymorphicSerializer(kotlin.reflect/KClass<#A>, kotlinx.serialization.modules/SerializersModule): kotlinx.serialization/KSerializer<#A> // com.arkivanov.essenty.statekeeper/polymorphicSerializer|polymorphicSerializer(kotlin.reflect.KClass<0:0>;kotlinx.serialization.modules.SerializersModule){0§}[0] -final fun <#A: kotlin/Any?, #B: kotlin/Any> (com.arkivanov.essenty.statekeeper/StateKeeperOwner).com.arkivanov.essenty.statekeeper/saveable(kotlinx.serialization/KSerializer<#B>, kotlin/Function1<#A, #B>, kotlin/String? = ..., kotlin/Function1<#B?, #A>): kotlin.properties/PropertyDelegateProvider> // com.arkivanov.essenty.statekeeper/saveable|saveable@com.arkivanov.essenty.statekeeper.StateKeeperOwner(kotlinx.serialization.KSerializer<0:1>;kotlin.Function1<0:0,0:1>;kotlin.String?;kotlin.Function1<0:1?,0:0>){0§;1§}[0] -final fun <#A: kotlin/Any?, #B: kotlin/Any?> (com.arkivanov.essenty.statekeeper/StateKeeper).com.arkivanov.essenty.statekeeper/saveable(kotlinx.serialization/KSerializer<#B>, kotlin/Function1<#A, #B>, kotlin/String? = ..., kotlin/Function1<#B?, #A>): kotlin.properties/PropertyDelegateProvider> // com.arkivanov.essenty.statekeeper/saveable|saveable@com.arkivanov.essenty.statekeeper.StateKeeper(kotlinx.serialization.KSerializer<0:1>;kotlin.Function1<0:0,0:1>;kotlin.String?;kotlin.Function1<0:1?,0:0>){0§;1§}[0] -final fun <#A: kotlin/Any?> (com.arkivanov.essenty.statekeeper/StateKeeper).com.arkivanov.essenty.statekeeper/saveable(kotlinx.serialization/KSerializer<#A>, kotlin/String? = ..., kotlin/Function0<#A>): kotlin.properties/PropertyDelegateProvider> // com.arkivanov.essenty.statekeeper/saveable|saveable@com.arkivanov.essenty.statekeeper.StateKeeper(kotlinx.serialization.KSerializer<0:0>;kotlin.String?;kotlin.Function0<0:0>){0§}[0] -final fun <#A: kotlin/Any?> (com.arkivanov.essenty.statekeeper/StateKeeperOwner).com.arkivanov.essenty.statekeeper/saveable(kotlinx.serialization/KSerializer<#A>, kotlin/String? = ..., kotlin/Function0<#A>): kotlin.properties/PropertyDelegateProvider> // com.arkivanov.essenty.statekeeper/saveable|saveable@com.arkivanov.essenty.statekeeper.StateKeeperOwner(kotlinx.serialization.KSerializer<0:0>;kotlin.String?;kotlin.Function0<0:0>){0§}[0] final fun com.arkivanov.essenty.statekeeper/StateKeeperDispatcher(com.arkivanov.essenty.statekeeper/SerializableContainer? = ...): com.arkivanov.essenty.statekeeper/StateKeeperDispatcher // com.arkivanov.essenty.statekeeper/StateKeeperDispatcher|StateKeeperDispatcher(com.arkivanov.essenty.statekeeper.SerializableContainer?){}[0] +final inline fun <#A: kotlin/Any> (com.arkivanov.essenty.statekeeper/StateKeeper).com.arkivanov.essenty.statekeeper/saveable(kotlinx.serialization/KSerializer<#A>, kotlin/String? = ..., crossinline kotlin/Function0<#A>): kotlin.properties/PropertyDelegateProvider> // com.arkivanov.essenty.statekeeper/saveable|saveable@com.arkivanov.essenty.statekeeper.StateKeeper(kotlinx.serialization.KSerializer<0:0>;kotlin.String?;kotlin.Function0<0:0>){0§}[0] +final inline fun <#A: kotlin/Any> (com.arkivanov.essenty.statekeeper/StateKeeperOwner).com.arkivanov.essenty.statekeeper/saveable(kotlinx.serialization/KSerializer<#A>, kotlin/String? = ..., crossinline kotlin/Function0<#A>): kotlin.properties/PropertyDelegateProvider> // com.arkivanov.essenty.statekeeper/saveable|saveable@com.arkivanov.essenty.statekeeper.StateKeeperOwner(kotlinx.serialization.KSerializer<0:0>;kotlin.String?;kotlin.Function0<0:0>){0§}[0] +final inline fun <#A: kotlin/Any?, #B: kotlin/Any> (com.arkivanov.essenty.statekeeper/StateKeeper).com.arkivanov.essenty.statekeeper/saveable(kotlinx.serialization/KSerializer<#B>, crossinline kotlin/Function1<#A, #B>, kotlin/String? = ..., crossinline kotlin/Function1<#B?, #A>): kotlin.properties/PropertyDelegateProvider> // com.arkivanov.essenty.statekeeper/saveable|saveable@com.arkivanov.essenty.statekeeper.StateKeeper(kotlinx.serialization.KSerializer<0:1>;kotlin.Function1<0:0,0:1>;kotlin.String?;kotlin.Function1<0:1?,0:0>){0§;1§}[0] +final inline fun <#A: kotlin/Any?, #B: kotlin/Any> (com.arkivanov.essenty.statekeeper/StateKeeperOwner).com.arkivanov.essenty.statekeeper/saveable(kotlinx.serialization/KSerializer<#B>, crossinline kotlin/Function1<#A, #B>, kotlin/String? = ..., crossinline kotlin/Function1<#B?, #A>): kotlin.properties/PropertyDelegateProvider> // com.arkivanov.essenty.statekeeper/saveable|saveable@com.arkivanov.essenty.statekeeper.StateKeeperOwner(kotlinx.serialization.KSerializer<0:1>;kotlin.Function1<0:0,0:1>;kotlin.String?;kotlin.Function1<0:1?,0:0>){0§;1§}[0] final inline fun <#A: reified kotlin/Any> com.arkivanov.essenty.statekeeper/polymorphicSerializer(kotlinx.serialization.modules/SerializersModule): kotlinx.serialization/KSerializer<#A> // com.arkivanov.essenty.statekeeper/polymorphicSerializer|polymorphicSerializer(kotlinx.serialization.modules.SerializersModule){0§}[0] diff --git a/state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/StateKeeperExt.kt b/state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/StateKeeperExt.kt index e93ab63..89bd3ad 100644 --- a/state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/StateKeeperExt.kt +++ b/state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/StateKeeperExt.kt @@ -1,8 +1,6 @@ package com.arkivanov.essenty.statekeeper import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializable -import kotlin.concurrent.Volatile import kotlin.properties.PropertyDelegateProvider import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadWriteProperty @@ -23,17 +21,16 @@ import kotlin.reflect.KProperty * @return [PropertyDelegateProvider] of type [T], typically used to define a delegated property. */ @ExperimentalStateKeeperApi -fun StateKeeper.saveable( +inline fun StateKeeper.saveable( serializer: KSerializer, - state: (T) -> S, + crossinline state: (T) -> S, key: String? = null, - init: (savedState: S?) -> T, + crossinline init: (savedState: S?) -> T, ): PropertyDelegateProvider> = PropertyDelegateProvider { _, property -> val stateKey = key ?: "SAVEABLE_HOLDER_${property.name}" - val holderSerializer = Holder.serializer(serializer) - val result = init(consume(key = stateKey, strategy = holderSerializer)?.value) - register(key = stateKey, strategy = holderSerializer) { Holder(state(result)) } + val result = init(consume(key = stateKey, strategy = serializer)) + register(key = stateKey, strategy = serializer) { state(result) } ReadOnlyProperty { _, _ -> result } } @@ -52,11 +49,11 @@ fun StateKeeper.saveable( * @return [PropertyDelegateProvider] of type [T], typically used to define a delegated property. */ @ExperimentalStateKeeperApi -fun StateKeeperOwner.saveable( +inline fun StateKeeperOwner.saveable( serializer: KSerializer, - state: (T) -> S, + crossinline state: (T) -> S, key: String? = null, - init: (savedState: S?) -> T, + crossinline init: (savedState: S?) -> T, ): PropertyDelegateProvider> = stateKeeper.saveable( serializer = serializer, @@ -77,28 +74,25 @@ fun StateKeeperOwner.saveable( * @return [PropertyDelegateProvider] of type [T], typically used to define a delegated property. */ @ExperimentalStateKeeperApi -fun StateKeeper.saveable( +inline fun StateKeeper.saveable( serializer: KSerializer, key: String? = null, - init: () -> T, + crossinline init: () -> T, ): PropertyDelegateProvider> = PropertyDelegateProvider { _, property -> val stateKey = key ?: "SAVEABLE_${property.name}" - val holderSerializer = Holder.serializer(serializer) - val holder = consume(key = stateKey, strategy = holderSerializer) ?: Holder(init()) - register(key = stateKey, strategy = holderSerializer) { Holder(holder.value) } - holder - } + var saveable = consume(key = stateKey, strategy = serializer) ?: init() + register(key = stateKey, strategy = serializer) { saveable } -@Serializable -private class Holder(@Volatile var value: T) : ReadWriteProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): T = - this.value + object : ReadWriteProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): T = + saveable - override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { - this.value = value + override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { + saveable = value + } + } } -} /** * Helper function for creating a mutable @@ -112,10 +106,10 @@ private class Holder(@Volatile var value: T) : ReadWriteProperty { * @return [PropertyDelegateProvider] of type [T], typically used to define a delegated property. */ @ExperimentalStateKeeperApi -fun StateKeeperOwner.saveable( +inline fun StateKeeperOwner.saveable( serializer: KSerializer, key: String? = null, - init: () -> T, + crossinline init: () -> T, ): PropertyDelegateProvider> = stateKeeper.saveable( serializer = serializer, diff --git a/state-keeper/src/commonTest/kotlin/com/arkivanov/essenty/statekeeper/StateKeeperExtTest.kt b/state-keeper/src/commonTest/kotlin/com/arkivanov/essenty/statekeeper/StateKeeperExtTest.kt index a35d87c..429df48 100644 --- a/state-keeper/src/commonTest/kotlin/com/arkivanov/essenty/statekeeper/StateKeeperExtTest.kt +++ b/state-keeper/src/commonTest/kotlin/com/arkivanov/essenty/statekeeper/StateKeeperExtTest.kt @@ -1,10 +1,8 @@ package com.arkivanov.essenty.statekeeper -import kotlinx.serialization.builtins.nullable import kotlinx.serialization.builtins.serializer import kotlin.test.Test import kotlin.test.assertEquals -import kotlin.test.assertNull @OptIn(ExperimentalStateKeeperApi::class) class StateKeeperExtTest { @@ -23,20 +21,6 @@ class StateKeeperExtTest { assertEquals(1, newComponent.holder.state) } - @Test - fun saveable_holder_saves_and_restores_nullable_state() { - val oldStateKeeper = StateKeeperDispatcher() - val oldComponent = ComponentWithStateHolder(oldStateKeeper) - - oldComponent.nullableHolder.state = 1 - - val savedState = oldStateKeeper.save().serializeAndDeserialize() - val newStateKeeper = StateKeeperDispatcher(savedState = savedState) - val newComponent = ComponentWithStateHolder(newStateKeeper) - - assertEquals(1, newComponent.nullableHolder.state) - } - @Test fun saveable_property_saves_and_restores_state() { val oldStateKeeper = StateKeeperDispatcher() @@ -51,50 +35,15 @@ class StateKeeperExtTest { assertEquals(1, newComponent.state) } - @Test - fun saveable_property_saves_and_restores_nullable_state_1() { - val oldStateKeeper = StateKeeperDispatcher() - val oldComponent = ComponentWithState(oldStateKeeper) - - oldComponent.nullableState1 = null - - val savedState = oldStateKeeper.save().serializeAndDeserialize() - val newStateKeeper = StateKeeperDispatcher(savedState = savedState) - val newComponent = ComponentWithState(newStateKeeper) - - assertNull(newComponent.nullableState1) - } - - @Test - fun saveable_property_saves_and_restores_nullable_state_2() { - val oldStateKeeper = StateKeeperDispatcher() - val oldComponent = ComponentWithState(oldStateKeeper) - - oldComponent.nullableState2 = 1 - - val savedState = oldStateKeeper.save().serializeAndDeserialize() - val newStateKeeper = StateKeeperDispatcher(savedState = savedState) - val newComponent = ComponentWithState(newStateKeeper) - - assertEquals(1, newComponent.nullableState2) - } - private class ComponentWithStateHolder(stateKeeper: StateKeeper) { val holder by stateKeeper.saveable(serializer = Int.serializer(), state = Holder::state) { Holder(state = it ?: 0) } - - val nullableHolder by stateKeeper.saveable(serializer = Int.serializer().nullable, state = NullableHolder::state) { - NullableHolder(state = it) - } } private class ComponentWithState(stateKeeper: StateKeeper) { - var state: Int by stateKeeper.saveable(serializer = Int.serializer()) { 0 } - var nullableState1: Int? by stateKeeper.saveable(serializer = Int.serializer().nullable) { 0 } - var nullableState2: Int? by stateKeeper.saveable(serializer = Int.serializer().nullable) { null } + var state by stateKeeper.saveable(serializer = Int.serializer()) { 0 } } private class Holder(var state: Int) - private class NullableHolder(var state: Int?) }