Skip to content

Commit

Permalink
Merge branch 'master' into compose-experimental
Browse files Browse the repository at this point in the history
  • Loading branch information
arkivanov committed Sep 8, 2023
2 parents 628d90b + e5afc98 commit 9be224a
Show file tree
Hide file tree
Showing 23 changed files with 254 additions and 94 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

---

Decompose is a Kotlin Multiplatform library for breaking down your code into tree-structured lifecycle-aware business logic components (aka BLoC), with routing functionality and pluggable UI (Jetpack Compose, Android Views, SwiftUI, JS React, etc.).
Decompose is a Kotlin Multiplatform library for breaking down your code into tree-structured lifecycle-aware business logic components (aka BLoC), with routing functionality and pluggable UI (Jetpack/Multiplatform Compose, Android Views, SwiftUI, JS React, etc.).

Please see the [project website](https://arkivanov.github.io/Decompose/) for documentation and APIs.

Expand Down Expand Up @@ -99,6 +99,10 @@ Please refer to the [Quick start](https://arkivanov.github.io/Decompose/getting-

Check out the [Samples](https://arkivanov.github.io/Decompose/samples/) section of the docs for a full description of each sample.

## Template

Check out the [template repository](https://github.com/arkivanov/decompose-multiplatform-template) which may be used to kick-start a project for you.

## Articles

- [Decompose — experiments with Kotlin Multiplatform lifecycle-aware components and navigation](https://proandroiddev.com/decompose-experiments-with-kotlin-multiplatform-lifecycle-aware-components-and-navigation-a04ef3c7f6a3?source=friends_link&sk=f7d289cc329b6c8a765fc049e36c313f)
Expand Down
3 changes: 3 additions & 0 deletions decompose/api/android/decompose.api
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ public abstract interface annotation class com/arkivanov/decompose/FaultyDecompo
public abstract interface annotation class com/arkivanov/decompose/InternalDecomposeApi : java/lang/annotation/Annotation {
}

public final class com/arkivanov/decompose/UtilsKt {
}

public final class com/arkivanov/decompose/errorhandler/ErrorHandlersKt {
public static final fun getOnDecomposeError ()Lkotlin/jvm/functions/Function1;
public static final fun setOnDecomposeError (Lkotlin/jvm/functions/Function1;)V
Expand Down
3 changes: 3 additions & 0 deletions decompose/api/jvm/decompose.api
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ public abstract interface annotation class com/arkivanov/decompose/FaultyDecompo
public abstract interface annotation class com/arkivanov/decompose/InternalDecomposeApi : java/lang/annotation/Annotation {
}

public final class com/arkivanov/decompose/UtilsKt {
}

public final class com/arkivanov/decompose/errorhandler/ErrorHandlersKt {
public static final fun getOnDecomposeError ()Lkotlin/jvm/functions/Function1;
public static final fun setOnDecomposeError (Lkotlin/jvm/functions/Function1;)V
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
package com.arkivanov.decompose

import com.arkivanov.essenty.lifecycle.Lifecycle
import kotlin.reflect.KClass

@InternalDecomposeApi
fun Any.hashString(): String =
"${this::class.uniqueName ?: this::class.simpleName}_${hashCode().toString(radix = 36)}"

internal expect val KClass<*>.uniqueName: String?

internal expect fun Any.ensureNeverFrozen()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ interface ChildNavState<out C : Any> {
*/
enum class Status {
/**
* The child component is instantiated and active. Its maximum lifecycle state is `RESUMED`,
* depending on the parent's lifecycle state. An active component can handle back button presses.
* The state of the component is saved when it switches from `ACTIVE` to any other status.
* The child component is destroyed but still managed, e.g. it's state may be saved and restored later.
*/
DESTROYED,

Expand All @@ -33,8 +31,9 @@ interface ChildNavState<out C : Any> {
INACTIVE,

/**
* The child is created and may be resumed, depending on the lifecycle of the hosting component.
* Back button handling is enabled.
* The child component is instantiated and active. Its maximum lifecycle state is `RESUMED`,
* depending on the parent's lifecycle state. An active component can handle back button presses.
* The state of the component is saved when it switches from `ACTIVE` to any other status.
*/
ACTIVE,
}
Expand Down
6 changes: 6 additions & 0 deletions decompose/src/jsMain/kotlin/com/arkivanov/decompose/Utils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.arkivanov.decompose

import kotlin.reflect.KClass

internal actual val KClass<*>.uniqueName: String?
get() = js.name
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.arkivanov.decompose

import kotlin.reflect.KClass

internal actual val KClass<*>.uniqueName: String?
get() = qualifiedName
2 changes: 1 addition & 1 deletion deps.versions.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[versions]

decompose = "2.0.1-compose-experimental"
decompose = "2.0.2-compose-experimental"
kotlin = "1.8.20"
essenty = "1.1.0"
parcelizeDarwin = "0.1.4"
Expand Down
44 changes: 42 additions & 2 deletions docs/component/state-preservation.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ Sometimes it might be necessary to preserve state or data in a component when it

The `decompose` module adds Essenty's `state-keeper` module as `api` dependency, so you don't need to explicitly add it to your project. Please familiarise yourself with Essenty library, especially with the `StateKeeper`.

## Usage example
## Usage examples

```kotlin
```kotlin title="Saving state in a component"
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.essenty.parcelable.Parcelable
import com.arkivanov.essenty.parcelable.Parcelize
Expand All @@ -27,6 +27,46 @@ class SomeComponent(
}
```

```kotlin title="Saving state of a retained instance"
import com.arkivanov.essenty.instancekeeper.InstanceKeeper
import com.arkivanov.essenty.instancekeeper.getOrCreate
import com.arkivanov.essenty.parcelable.Parcelable
import com.arkivanov.essenty.parcelable.ParcelableContainer
import com.arkivanov.essenty.parcelable.Parcelize
import com.arkivanov.essenty.parcelable.consume
import com.arkivanov.essenty.statekeeper.consume

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

private val statefulEntity =
instanceKeeper.getOrCreate {
SomeStatefulEntity(savedState = stateKeeper.consume(key = "SAVED_STATE"))
}

init {
stateKeeper.register(key = "SAVED_STATE", supplier = statefulEntity::saveState)
}
}

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

var state: State = savedState?.consume() ?: State()
private set

fun saveState(): ParcelableContainer =
ParcelableContainer(state)

override fun onDestroy() {}

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

## Darwin (Apple) targets support

Decompose provides an experimental support of state preservation for all Darwin (Apple) targets. It works via `Essenty` library and [parcelize-darwin](https://github.com/arkivanov/parcelize-darwin) compiler plugin (from the same author). Please read the documentation of both before using state preservation on Darwin targets.
Expand Down
Loading

0 comments on commit 9be224a

Please sign in to comment.