Skip to content

Commit

Permalink
Added StateKeeperOwner.withSavedState and InstanceKeeperOwner.retaine…
Browse files Browse the repository at this point in the history
…dInstance extensions
  • Loading branch information
arkivanov committed Jul 7, 2024
1 parent bc46211 commit 8c6fa0a
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 0 deletions.
4 changes: 4 additions & 0 deletions instance-keeper/api/instance-keeper.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,8 @@ final fun com.arkivanov.essenty.instancekeeper/InstanceKeeperDispatcher(): com.a
final inline fun <#A: com.arkivanov.essenty.instancekeeper/InstanceKeeper.Instance> (com.arkivanov.essenty.instancekeeper/InstanceKeeper).com.arkivanov.essenty.instancekeeper/getOrCreate(kotlin/Any, kotlin/Function0<#A>): #A // com.arkivanov.essenty.instancekeeper/getOrCreate|[email protected](kotlin.Any;kotlin.Function0<0:0>){0§<com.arkivanov.essenty.instancekeeper.InstanceKeeper.Instance>}[0]
final inline fun <#A: kotlin/Any?> (com.arkivanov.essenty.instancekeeper/InstanceKeeper).com.arkivanov.essenty.instancekeeper/getOrCreateSimple(kotlin/Any, kotlin/Function0<#A>): #A // com.arkivanov.essenty.instancekeeper/getOrCreateSimple|getOrCreateSimple@com.arkivanov.essenty.instancekeeper.InstanceKeeper(kotlin.Any;kotlin.Function0<0:0>){0§<kotlin.Any?>}[0]
final inline fun <#A: reified com.arkivanov.essenty.instancekeeper/InstanceKeeper.Instance> (com.arkivanov.essenty.instancekeeper/InstanceKeeper).com.arkivanov.essenty.instancekeeper/getOrCreate(kotlin/Function0<#A>): #A // com.arkivanov.essenty.instancekeeper/getOrCreate|[email protected](kotlin.Function0<0:0>){0§<com.arkivanov.essenty.instancekeeper.InstanceKeeper.Instance>}[0]
final inline fun <#A: reified com.arkivanov.essenty.instancekeeper/InstanceKeeper.Instance> (com.arkivanov.essenty.instancekeeper/InstanceKeeperOwner).com.arkivanov.essenty.instancekeeper/retainedInstance(kotlin/Any, kotlin/Function0<#A>): #A // com.arkivanov.essenty.instancekeeper/retainedInstance|retainedInstance@com.arkivanov.essenty.instancekeeper.InstanceKeeperOwner(kotlin.Any;kotlin.Function0<0:0>){0§<com.arkivanov.essenty.instancekeeper.InstanceKeeper.Instance>}[0]
final inline fun <#A: reified com.arkivanov.essenty.instancekeeper/InstanceKeeper.Instance> (com.arkivanov.essenty.instancekeeper/InstanceKeeperOwner).com.arkivanov.essenty.instancekeeper/retainedInstance(kotlin/Function0<#A>): #A // com.arkivanov.essenty.instancekeeper/retainedInstance|retainedInstance@com.arkivanov.essenty.instancekeeper.InstanceKeeperOwner(kotlin.Function0<0:0>){0§<com.arkivanov.essenty.instancekeeper.InstanceKeeper.Instance>}[0]
final inline fun <#A: reified kotlin/Any?> (com.arkivanov.essenty.instancekeeper/InstanceKeeper).com.arkivanov.essenty.instancekeeper/getOrCreateSimple(kotlin/Function0<#A>): #A // com.arkivanov.essenty.instancekeeper/getOrCreateSimple|getOrCreateSimple@com.arkivanov.essenty.instancekeeper.InstanceKeeper(kotlin.Function0<0:0>){0§<kotlin.Any?>}[0]
final inline fun <#A: reified kotlin/Any?> (com.arkivanov.essenty.instancekeeper/InstanceKeeperOwner).com.arkivanov.essenty.instancekeeper/retainedSimpleInstance(kotlin/Any, kotlin/Function0<#A>): #A // com.arkivanov.essenty.instancekeeper/retainedSimpleInstance|retainedSimpleInstance@com.arkivanov.essenty.instancekeeper.InstanceKeeperOwner(kotlin.Any;kotlin.Function0<0:0>){0§<kotlin.Any?>}[0]
final inline fun <#A: reified kotlin/Any?> (com.arkivanov.essenty.instancekeeper/InstanceKeeperOwner).com.arkivanov.essenty.instancekeeper/retainedSimpleInstance(kotlin/Function0<#A>): #A // com.arkivanov.essenty.instancekeeper/retainedSimpleInstance|retainedSimpleInstance@com.arkivanov.essenty.instancekeeper.InstanceKeeperOwner(kotlin.Function0<0:0>){0§<kotlin.Any?>}[0]
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@ inline fun <T : InstanceKeeper.Instance> InstanceKeeper.getOrCreate(key: Any, fa
inline fun <reified T : InstanceKeeper.Instance> InstanceKeeper.getOrCreate(factory: () -> T): T =
getOrCreate(key = typeOf<T>(), factory = factory)

/**
* A convenience function for [InstanceKeeper.getOrCreate].
*/
inline fun <reified T : InstanceKeeper.Instance> InstanceKeeperOwner.retainedInstance(key: Any, factory: () -> T): T =
instanceKeeper.getOrCreate(key = key, factory = factory)

/**
* A convenience function for [InstanceKeeper.getOrCreate].
*/
inline fun <reified T : InstanceKeeper.Instance> InstanceKeeperOwner.retainedInstance(factory: () -> T): T =
instanceKeeper.getOrCreate(factory = factory)

/**
* Returns a previously stored instance of type [T] with the given key,
* or creates and stores a new one if it doesn't exist.
Expand All @@ -43,3 +55,15 @@ inline fun <T> InstanceKeeper.getOrCreateSimple(key: Any, factory: () -> T): T =
*/
inline fun <reified T> InstanceKeeper.getOrCreateSimple(factory: () -> T): T =
getOrCreateSimple(key = typeOf<T>(), factory = factory)

/**
* A convenience function for [InstanceKeeper.getOrCreateSimple].
*/
inline fun <reified T> InstanceKeeperOwner.retainedSimpleInstance(key: Any, factory: () -> T): T =
instanceKeeper.getOrCreateSimple(key = key, factory = factory)

/**
* A convenience function for [InstanceKeeper.getOrCreateSimple].
*/
inline fun <reified T> InstanceKeeperOwner.retainedSimpleInstance(factory: () -> T): T =
instanceKeeper.getOrCreateSimple(factory = factory)
5 changes: 5 additions & 0 deletions state-keeper/api/android/state-keeper.api
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ public final class com/arkivanov/essenty/statekeeper/StateKeeperDispatcherKt {
public static synthetic fun StateKeeperDispatcher$default (Lcom/arkivanov/essenty/statekeeper/SerializableContainer;ILjava/lang/Object;)Lcom/arkivanov/essenty/statekeeper/StateKeeperDispatcher;
}

public final class com/arkivanov/essenty/statekeeper/StateKeeperExtKt {
public static final fun withSavedState (Lcom/arkivanov/essenty/statekeeper/StateKeeper;Ljava/lang/String;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun withSavedState (Lcom/arkivanov/essenty/statekeeper/StateKeeperOwner;Ljava/lang/String;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
}

public abstract interface class com/arkivanov/essenty/statekeeper/StateKeeperOwner {
public abstract fun getStateKeeper ()Lcom/arkivanov/essenty/statekeeper/StateKeeper;
}
Expand Down
5 changes: 5 additions & 0 deletions state-keeper/api/jvm/state-keeper.api
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ public final class com/arkivanov/essenty/statekeeper/StateKeeperDispatcherKt {
public static synthetic fun StateKeeperDispatcher$default (Lcom/arkivanov/essenty/statekeeper/SerializableContainer;ILjava/lang/Object;)Lcom/arkivanov/essenty/statekeeper/StateKeeperDispatcher;
}

public final class com/arkivanov/essenty/statekeeper/StateKeeperExtKt {
public static final fun withSavedState (Lcom/arkivanov/essenty/statekeeper/StateKeeper;Ljava/lang/String;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun withSavedState (Lcom/arkivanov/essenty/statekeeper/StateKeeperOwner;Ljava/lang/String;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
}

public abstract interface class com/arkivanov/essenty/statekeeper/StateKeeperOwner {
public abstract fun getStateKeeper ()Lcom/arkivanov/essenty/statekeeper/StateKeeper;
}
Expand Down
2 changes: 2 additions & 0 deletions state-keeper/api/state-keeper.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,6 @@ final fun <#A: kotlin/Any> (com.arkivanov.essenty.statekeeper/SerializableContai
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 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?, #B: kotlin/Any> (com.arkivanov.essenty.statekeeper/StateKeeper).com.arkivanov.essenty.statekeeper/withSavedState(kotlin/String, kotlinx.serialization/KSerializer<#B>, crossinline kotlin/Function1<#A, #B>, kotlin/Function1<#B?, #A>): #A // com.arkivanov.essenty.statekeeper/withSavedState|[email protected](kotlin.String;kotlinx.serialization.KSerializer<0:1>;kotlin.Function1<0:0,0:1>;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/withSavedState(kotlin/String, kotlinx.serialization/KSerializer<#B>, crossinline kotlin/Function1<#A, #B>, kotlin/Function1<#B?, #A>): #A // com.arkivanov.essenty.statekeeper/withSavedState|withSavedState@com.arkivanov.essenty.statekeeper.StateKeeperOwner(kotlin.String;kotlinx.serialization.KSerializer<0:1>;kotlin.Function1<0:0,0:1>;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
@@ -0,0 +1,42 @@
package com.arkivanov.essenty.statekeeper

import kotlinx.serialization.KSerializer

/**
* Helper function for creating objects with a saved state.
*
* @param key a key for saving and restoring the state.
* @param serializer a [KSerializer] for serializing and deserializing the state.
* @param saveableState a function that selects a state [S] from the resulting
* object [T] and returns it for saving.
* @param init a function that accepts the previously saved state [S] (if any) and
* returns an object of type [T].
* @return an object of type [T] returned by [init] function.
*/
inline fun <T, S : Any> StateKeeper.withSavedState(
key: String,
serializer: KSerializer<S>,
crossinline saveableState: (T) -> S,
init: (savedState: S?) -> T,
): T {
val result = init(consume(key = key, strategy = serializer))
register(key = key, strategy = serializer) { saveableState(result) }

return result
}

/**
* A convenience function for [StateKeeper.withSavedState].
*/
inline fun <T, S : Any> StateKeeperOwner.withSavedState(
key: String,
serializer: KSerializer<S>,
crossinline saveableState: (T) -> S,
init: (savedState: S?) -> T,
): T =
stateKeeper.withSavedState(
key = key,
serializer = serializer,
saveableState = saveableState,
init = init,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.arkivanov.essenty.statekeeper

import kotlinx.serialization.builtins.serializer
import kotlin.test.Test
import kotlin.test.assertEquals

class StateKeeperExtTest {

@Test
fun withSavedState_saves_and_restores_state() {
val oldDispatcher = StateKeeperDispatcher()

val holder =
oldDispatcher.withSavedState(
key = "SAVED_STATE",
serializer = Int.serializer(),
saveableState = { it.state },
) { Holder(state = it ?: 0) }

holder.state++

val savedState = oldDispatcher.save().serializeAndDeserialize()
val newDispatcher = StateKeeperDispatcher(savedState = savedState)

val newHolder =
newDispatcher.withSavedState(
key = "SAVED_STATE",
serializer = Int.serializer(),
saveableState = { it.state },
) { Holder(state = it ?: 0) }

assertEquals(1, newHolder.state)
}

class Holder(var state: Int)
}

0 comments on commit 8c6fa0a

Please sign in to comment.