Skip to content

Commit

Permalink
Added StateKeeperOwner.saveable and InstanceKeeperOwner.retainedInsta…
Browse files Browse the repository at this point in the history
…nce to docs
  • Loading branch information
arkivanov committed Jul 8, 2024
1 parent 436c518 commit 0593ef5
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 24 deletions.
29 changes: 28 additions & 1 deletion docs/component/instance-retaining.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class SomeComponent(
componentContext: ComponentContext
) : ComponentContext by componentContext {

private val someLogic = instanceKeeper.getOrCreate(::SomeLogic)
private val someLogic = instanceKeeper.getOrCreate { SomeLogic() }

/*
* Instances of this class will be retained (not destroyed on configuration changes).
Expand All @@ -31,6 +31,33 @@ class SomeComponent(
}
```

## Usage example (experimental since version 3.2.0-alpha02)

```kotlin
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.essenty.instancekeeper.InstanceKeeper
import com.arkivanov.essenty.instancekeeper.retainedInstance

class SomeComponent(
componentContext: ComponentContext
) : ComponentContext by componentContext {

private val someLogic = retainedInstance { SomeLogic() }

/*
* Instances of this class will be retained (not destroyed on configuration changes).
* This is equivalent to AndroidX ViewModel.
* ⚠️ Pay attention to not leak any dependencies,
* e.g. don't make this class `inner`, and don't pass dependencies like Activity Context into it.
*/
private class SomeLogic : InstanceKeeper.Instance {
override fun onDestroy() {
// Clean-up
}
}
}
```

## Retained components

Although discouraged, it is still possible to have all components retained over configuration changes on Android. On the one hand, this makes `InstanceKeeper` no longer required. But on the other hand, this prevents from supplying dependencies that capture the hosting `Activity` or `Fragment`.
Expand Down
69 changes: 46 additions & 23 deletions docs/component/state-preservation.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ Decompose relies on [kotlinx-serialization](https://github.com/Kotlin/kotlinx.se
import com.arkivanov.decompose.ComponentContext
import kotlinx.serialization.Serializable

class SomeComponent(
componentContext: ComponentContext
) : ComponentContext by componentContext {
class SomeComponent(componentContext: ComponentContext) : ComponentContext by componentContext {

// Either restore the previously saved state or create a new (initial) one
private var state: State = stateKeeper.consume(key = "SAVED_STATE", strategy = State.serializer()) ?: State()
Expand All @@ -34,42 +32,67 @@ class SomeComponent(
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.essenty.instancekeeper.InstanceKeeper
import com.arkivanov.essenty.instancekeeper.getOrCreate
import com.arkivanov.essenty.statekeeper.SerializableContainer
import com.arkivanov.sample.shared.SomeStatefulEntity.State
import kotlinx.serialization.Serializable

class SomeComponent(
componentContext: ComponentContext
) : ComponentContext by componentContext {

class SomeComponent(componentContext: ComponentContext) : ComponentContext by componentContext {
private val statefulEntity =
instanceKeeper.getOrCreate {
SomeStatefulEntity(savedState = stateKeeper.consume(key = "SAVED_STATE", strategy = SerializableContainer.serializer()))
SomeStatefulEntity(savedState = stateKeeper.consume(key = "SAVED_STATE", strategy = State.serializer()))
}

init {
stateKeeper.register(
key = "SAVED_STATE",
strategy = SerializableContainer.serializer(),
supplier = statefulEntity::saveState,
)
stateKeeper.register(key = "SAVED_STATE", strategy = State.serializer(), supplier = statefulEntity::state)
}
}

class SomeStatefulEntity(
savedState: SerializableContainer?,
) : InstanceKeeper.Instance {

var state: State = savedState?.consume(strategy = State.serializer()) ?: State()
class SomeStatefulEntity(savedState: State?) : InstanceKeeper.Instance {
var state: State = savedState ?: State()
private set

fun saveState(): SerializableContainer =
SerializableContainer(value = state, strategy = State.serializer())
@Serializable
data class State(val someValue: Int = 0)
}
```

## Usage examples (experimental since version 3.2.0-alpha02)

```kotlin title="Saving state in a component"
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.essenty.statekeeper.saveable
import kotlinx.serialization.Serializable

class SomeComponent(componentContext: ComponentContext) : ComponentContext by componentContext {
private var state: State by saveable(serializer = State.serializer(), init = ::State)

@Serializable // Comes from kotlinx-serialization
private class State(val someValue: Int = 0)
}
```

```kotlin title="Saving state of a retained instance"
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.essenty.instancekeeper.InstanceKeeper
import com.arkivanov.essenty.instancekeeper.retainedInstance
import com.arkivanov.essenty.statekeeper.saveable
import com.arkivanov.sample.shared.SomeStatefulEntity.State
import kotlinx.serialization.Serializable

class SomeComponent(componentContext: ComponentContext) : ComponentContext by componentContext {
private val statefulEntity by saveable(serializer = State.serializer(), state = { it.state }) { savedState ->
retainedInstance {
SomeStatefulEntity(savedState = savedState)
}
}
}

override fun onDestroy() {}
class SomeStatefulEntity(savedState: State?) : InstanceKeeper.Instance {
var state: State = savedState ?: State()
private set

@Serializable
data class State(val someValue: Int = 0)
}
}
```

## Saving state on non-Android targets
Expand Down

0 comments on commit 0593ef5

Please sign in to comment.