Skip to content

Commit

Permalink
Revert "Made StateKeeper#saveable type nullable"
Browse files Browse the repository at this point in the history
  • Loading branch information
arkivanov authored Jan 22, 2025
1 parent 440c8ba commit c027aa6
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 83 deletions.
8 changes: 4 additions & 4 deletions state-keeper/api/state-keeper.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -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§<kotlin.Any>}[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§<kotlin.Any>}[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§<kotlin.Any>}[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<kotlin/Any?, kotlin.properties/ReadOnlyProperty<kotlin/Any?, #A>> // com.arkivanov.essenty.statekeeper/saveable|[email protected](kotlinx.serialization.KSerializer<0:1>;kotlin.Function1<0:0,0:1>;kotlin.String?;kotlin.Function1<0:1?,0:0>){0§<kotlin.Any?>;1§<kotlin.Any>}[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<kotlin/Any?, kotlin.properties/ReadOnlyProperty<kotlin/Any?, #A>> // com.arkivanov.essenty.statekeeper/saveable|[email protected](kotlinx.serialization.KSerializer<0:1>;kotlin.Function1<0:0,0:1>;kotlin.String?;kotlin.Function1<0:1?,0:0>){0§<kotlin.Any?>;1§<kotlin.Any?>}[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<kotlin/Any?, kotlin.properties/ReadWriteProperty<kotlin/Any?, #A>> // com.arkivanov.essenty.statekeeper/saveable|[email protected](kotlinx.serialization.KSerializer<0:0>;kotlin.String?;kotlin.Function0<0:0>){0§<kotlin.Any?>}[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<kotlin/Any?, kotlin.properties/ReadWriteProperty<kotlin/Any?, #A>> // com.arkivanov.essenty.statekeeper/saveable|[email protected](kotlinx.serialization.KSerializer<0:0>;kotlin.String?;kotlin.Function0<0:0>){0§<kotlin.Any?>}[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<kotlin/Any?, kotlin.properties/ReadWriteProperty<kotlin/Any?, #A>> // com.arkivanov.essenty.statekeeper/saveable|[email protected](kotlinx.serialization.KSerializer<0:0>;kotlin.String?;kotlin.Function0<0:0>){0§<kotlin.Any>}[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<kotlin/Any?, kotlin.properties/ReadWriteProperty<kotlin/Any?, #A>> // com.arkivanov.essenty.statekeeper/saveable|[email protected](kotlinx.serialization.KSerializer<0:0>;kotlin.String?;kotlin.Function0<0:0>){0§<kotlin.Any>}[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<kotlin/Any?, kotlin.properties/ReadOnlyProperty<kotlin/Any?, #A>> // com.arkivanov.essenty.statekeeper/saveable|[email protected](kotlinx.serialization.KSerializer<0:1>;kotlin.Function1<0:0,0:1>;kotlin.String?;kotlin.Function1<0:1?,0:0>){0§<kotlin.Any?>;1§<kotlin.Any>}[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<kotlin/Any?, kotlin.properties/ReadOnlyProperty<kotlin/Any?, #A>> // com.arkivanov.essenty.statekeeper/saveable|[email protected](kotlinx.serialization.KSerializer<0:1>;kotlin.Function1<0:0,0:1>;kotlin.String?;kotlin.Function1<0:1?,0:0>){0§<kotlin.Any?>;1§<kotlin.Any>}[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§<kotlin.Any>}[0]
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -23,17 +21,16 @@ import kotlin.reflect.KProperty
* @return [PropertyDelegateProvider] of type [T], typically used to define a delegated property.
*/
@ExperimentalStateKeeperApi
fun <T, S> StateKeeper.saveable(
inline fun <T, S : Any> StateKeeper.saveable(
serializer: KSerializer<S>,
state: (T) -> S,
crossinline state: (T) -> S,
key: String? = null,
init: (savedState: S?) -> T,
crossinline init: (savedState: S?) -> T,
): PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, T>> =
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 }
}

Expand All @@ -52,11 +49,11 @@ fun <T, S> StateKeeper.saveable(
* @return [PropertyDelegateProvider] of type [T], typically used to define a delegated property.
*/
@ExperimentalStateKeeperApi
fun <T, S : Any> StateKeeperOwner.saveable(
inline fun <T, S : Any> StateKeeperOwner.saveable(
serializer: KSerializer<S>,
state: (T) -> S,
crossinline state: (T) -> S,
key: String? = null,
init: (savedState: S?) -> T,
crossinline init: (savedState: S?) -> T,
): PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, T>> =
stateKeeper.saveable(
serializer = serializer,
Expand All @@ -77,28 +74,25 @@ fun <T, S : Any> StateKeeperOwner.saveable(
* @return [PropertyDelegateProvider] of type [T], typically used to define a delegated property.
*/
@ExperimentalStateKeeperApi
fun <T> StateKeeper.saveable(
inline fun <T : Any> StateKeeper.saveable(
serializer: KSerializer<T>,
key: String? = null,
init: () -> T,
crossinline init: () -> T,
): PropertyDelegateProvider<Any?, ReadWriteProperty<Any?, T>> =
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<T>(@Volatile var value: T) : ReadWriteProperty<Any?, T> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T =
this.value
object : ReadWriteProperty<Any?, T> {
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
Expand All @@ -112,10 +106,10 @@ private class Holder<T>(@Volatile var value: T) : ReadWriteProperty<Any?, T> {
* @return [PropertyDelegateProvider] of type [T], typically used to define a delegated property.
*/
@ExperimentalStateKeeperApi
fun <T> StateKeeperOwner.saveable(
inline fun <T : Any> StateKeeperOwner.saveable(
serializer: KSerializer<T>,
key: String? = null,
init: () -> T,
crossinline init: () -> T,
): PropertyDelegateProvider<Any?, ReadWriteProperty<Any?, T>> =
stateKeeper.saveable(
serializer = serializer,
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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()
Expand All @@ -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?)
}

0 comments on commit c027aa6

Please sign in to comment.