{"id":13658989,"url":"https://github.com/arkivanov/Essenty","last_synced_at":"2025-04-24T11:33:19.609Z","repository":{"id":37476896,"uuid":"385374863","full_name":"arkivanov/Essenty","owner":"arkivanov","description":"The most essential libraries for Kotlin Multiplatform development","archived":false,"fork":false,"pushed_at":"2025-04-06T13:36:52.000Z","size":887,"stargazers_count":541,"open_issues_count":1,"forks_count":15,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-14T03:07:37.415Z","etag":null,"topics":["kotlin","kotlin-multiplatform","multiplatform"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/arkivanov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"arkivanov","custom":["https://www.buymeacoffee.com/arkivanov","https://btc.com/1DXjn9e6rmbVvac3TH8hG3LdLtoA1CUvsM","https://etherscan.io/address/0xf027f5738f45676a54c15cf7753a0f66553947b9"]}},"created_at":"2021-07-12T20:27:43.000Z","updated_at":"2025-04-12T23:23:24.000Z","dependencies_parsed_at":"2023-11-08T03:47:09.760Z","dependency_job_id":"0d26a1a8-2f3d-4e22-bdd9-c351071fcdf0","html_url":"https://github.com/arkivanov/Essenty","commit_stats":{"total_commits":238,"total_committers":6,"mean_commits":"39.666666666666664","dds":0.07563025210084029,"last_synced_commit":"576bd7002b7fdec442c3d120bbe93ad38a8d3d06"},"previous_names":[],"tags_count":57,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arkivanov%2FEssenty","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arkivanov%2FEssenty/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arkivanov%2FEssenty/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arkivanov%2FEssenty/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arkivanov","download_url":"https://codeload.github.com/arkivanov/Essenty/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250618937,"owners_count":21460187,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["kotlin","kotlin-multiplatform","multiplatform"],"created_at":"2024-08-02T05:01:04.340Z","updated_at":"2025-04-24T11:33:19.218Z","avatar_url":"https://github.com/arkivanov.png","language":"Kotlin","readme":"[![Maven Central](https://img.shields.io/maven-central/v/com.arkivanov.essenty/lifecycle?color=blue)](https://search.maven.org/search?q=g:com.arkivanov.essenty)\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0)\n[![Twitter URL](https://img.shields.io/badge/Twitter-@arkann1985-blue.svg?style=social\u0026logo=twitter)](https://twitter.com/arkann1985)\n\n# Essenty\n\nThe most essential libraries for Kotlin Multiplatform development.\n\nSupported targets:\n\n- `android`\n- `jvm`\n- `js`\n- `wasmJs`\n- `ios`\n- `watchos`\n- `tvos`\n- `macos`\n- `linuxX64`\n\n## Lifecycle\n\nWhen writing Kotlin Multiplatform (common) code we often need to handle lifecycle events of a screen. For example, to stop background operations when the screen is destroyed, or to reload some data when the screen is activated. Essenty provides the `Lifecycle` API to help with lifecycle handling in the common code. It is very similar to [Android Activity lifecycle](https://developer.android.com/guide/components/activities/activity-lifecycle).\n\n### Setup\n\nGroovy:\n```groovy\n// Add the dependency, typically under the commonMain source set\nimplementation \"com.arkivanov.essenty:lifecycle:\u003cessenty_version\u003e\"\n```\n\nKotlin:\n```kotlin\n// Add the dependency, typically under the commonMain source set\nimplementation(\"com.arkivanov.essenty:lifecycle:\u003cessenty_version\u003e\")\n```\n\n### Lifecycle state transitions\n\n\u003cimg src=\"docs/media/LifecycleStates.png\" width=\"512\"\u003e\n\n### Content\n\nThe main [Lifecycle](https://github.com/arkivanov/Essenty/blob/master/lifecycle/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/Lifecycle.kt) interface provides ability to observe the lifecycle state changes. There are also handy [extension functions](https://github.com/arkivanov/Essenty/blob/master/lifecycle/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/LifecycleExt.kt) for convenience.\n\nThe [LifecycleRegistry](https://github.com/arkivanov/Essenty/blob/master/lifecycle/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/LifecycleRegistry.kt) interface extends both the `Lifecycle` and the `Lifecycle.Callbacks` at the same time. It can be used to manually control the lifecycle, for example in tests. You can also find some useful [extension functions](https://github.com/arkivanov/Essenty/blob/master/lifecycle/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/LifecycleRegistryExt.kt).\n\nThe [LifecycleOwner](https://github.com/arkivanov/Essenty/blob/master/lifecycle/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/LifecycleOwner.kt) just holds the `Lifecyle`. It may be implemented by an arbitrary class, to provide convenient API.\n\n#### Android extensions\n\nFrom Android, the `Lifecycle` can be obtained by using special functions, can be found [here](https://github.com/arkivanov/Essenty/blob/master/lifecycle/src/androidMain/kotlin/com/arkivanov/essenty/lifecycle/AndroidExt.kt).\n\n#### iOS and tvOS extensions\n\nThere is [ApplicationLifecycle](https://github.com/arkivanov/Essenty/blob/master/lifecycle/src/itvosMain/kotlin/com/arkivanov/essenty/lifecycle/ApplicationLifecycle.kt) available for `ios` and `tvos` targets. It follows the `UIApplication` lifecycle notifications.\n\n\u003e ⚠️  Since this implementation subscribes to `UIApplication` global lifecycle events, the instance and all its registered callbacks (and whatever they capture) will stay in memory until the application is destroyed or until `ApplicationLifecycle#destroy` method is called. It's ok to use it in a global scope like `UIApplicationDelegate`, but it may cause memory leaks when used in a narrower scope like `UIViewController` if it gets destroyed earlier. Use the `destroy` method to destroy the lifecycle manually and prevent memory leaks.\n\n#### Reaktive extensions\n\nThere are some useful `Lifecycle` extensions for Reaktive.\n\n- Automatic management of `Disposable` and `DisposableScope` by `Lifecycle`, can be found [here](https://github.com/arkivanov/Essenty/blob/master/lifecycle-reaktive/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/reaktive/DisposableWithLifecycle.kt).\n\n#### Coroutines extensions\n\nThere are some useful `Lifecycle` extensions for Coroutines.\n\n- Automatic management of `CoroutineScope` by `Lifecycle`, can be found [here](https://github.com/arkivanov/Essenty/blob/master/lifecycle-coroutines/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/coroutines/CoroutineScopeWithLifecycle.kt)\n- `Flow.withLifecycle(Lifecycle): Flow` - can be found [here](https://github.com/arkivanov/Essenty/blob/master/lifecycle-coroutines/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/coroutines/FlowWithLifecycle.kt).\n- `Lifecycle.repeatOnLifecycle(block)` - can be found [here](https://github.com/arkivanov/Essenty/blob/master/lifecycle-coroutines/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/coroutines/RepeatOnLifecycle.kt).\n\n### Usage example\n\n#### Observing the Lifecyle\n\nThe lifecycle can be observed using its `subscribe`/`unsubscribe` methods:\n\n```kotlin\nimport com.arkivanov.essenty.lifecycle.Lifecycle\n\nclass SomeLogic(lifecycle: Lifecycle) {\n    init {\n        lifecycle.subscribe(\n            object : Lifecycle.Callbacks {\n                override fun onCreate() {\n                    // Handle lifecycle created\n                }\n\n                // onStart, onResume, onPause, onStop are also available\n\n                override fun onDestroy() {\n                    // Handle lifecycle destroyed\n                }\n            }\n        )\n    }\n}\n```\n\nOr using the extension functions:\n\n```kotlin\nimport com.arkivanov.essenty.lifecycle.Lifecycle\nimport com.arkivanov.essenty.lifecycle.doOnCreate\nimport com.arkivanov.essenty.lifecycle.doOnDestroy\nimport com.arkivanov.essenty.lifecycle.subscribe\n\nclass SomeLogic(lifecycle: Lifecycle) {\n    init {\n        lifecycle.subscribe(\n            onCreate = { /* Handle lifecycle created */ },\n            // onStart, onResume, onPause, onStop are also available\n            onDestroy = { /* Handle lifecycle destroyed */ }\n        )\n\n        lifecycle.doOnCreate {\n            // Handle lifecycle created\n        }\n\n        // doOnStart, doOnResume, doOnPause, doOnStop are also available\n\n        lifecycle.doOnDestroy {\n            // Handle lifecycle destroyed\n        }\n    }\n}\n```\n\n#### Using the LifecycleRegistry manually\n\nA default implementation of the `LifecycleRegisty` interface can be instantiated using the corresponding builder function:\n\n```kotlin\nimport com.arkivanov.essenty.lifecycle.LifecycleRegistry\nimport com.arkivanov.essenty.lifecycle.resume\nimport com.arkivanov.essenty.lifecycle.destroy\n\nval lifecycleRegistry = LifecycleRegistry()\nval someLogic = SomeLogic(lifecycleRegistry)\n\nlifecycleRegistry.resume()\n\n// At some point later\nlifecycleRegistry.destroy()\n```\n\n## StateKeeper\n\nWhen writing common code targeting Android, it might be required to preserve some data over process death or Android configuration changes. For this purpose, Essenty provides the `StateKeeper` API, which is inspired by the AndroidX [SavedStateHandle](https://developer.android.com/reference/androidx/lifecycle/SavedStateHandle).\n\n### Setup\n\nGroovy:\n```groovy\n// Add the dependency, typically under the commonMain source set\nimplementation \"com.arkivanov.essenty:state-keeper:\u003cessenty_version\u003e\"\n```\n\nKotlin:\n```kotlin\n// Add the dependency, typically under the commonMain source set\nimplementation(\"com.arkivanov.essenty:state-keeper:\u003cessenty_version\u003e\")\n```\n\n### Content\n\nThe main [StateKeeper](https://github.com/arkivanov/Essenty/blob/master/state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/StateKeeper.kt) interface provides ability to register/unregister state suppliers, and also to consume any previously saved state. You can also find some handy [extension functions](https://github.com/arkivanov/Essenty/blob/master/state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/StateKeeperExt.kt). You can also find some handy [extension functions](https://github.com/arkivanov/Essenty/blob/master/state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/StateKeeperExt.kt).\n\nThe [StateKeeperDispatcher](https://github.com/arkivanov/Essenty/blob/master/state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/StateKeeperDispatcher.kt) interface extends `StateKeeper` and  allows state saving, by calling all registered state providers.\n\nThe [StateKeeperOwner](https://github.com/arkivanov/Essenty/blob/master/state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/StateKeeperOwner.kt) interface is just a holder of `StateKeeper`. It may be implemented by an arbitrary class, to provide convenient API.\n\n#### Android extensions\n\nFrom Android side, `StateKeeper` can be obtained by using special functions, can be found [here](https://github.com/arkivanov/Essenty/blob/master/state-keeper/src/androidMain/kotlin/com/arkivanov/essenty/statekeeper/AndroidExt.kt).\n\nThere are also some handy [extension functions](https://github.com/arkivanov/Essenty/blob/master/state-keeper/src/androidMain/kotlin/com/arkivanov/essenty/statekeeper/BundleExt.kt) for serializing/deserializing `KSerializable` objects to/from [Bundle](https://developer.android.com/reference/android/os/Bundle):\n\n- `fun \u003cT : Any\u003e Bundle.putSerializable(key: String?, value: T?, strategy: SerializationStrategy\u003cT\u003e)`\n- `fun \u003cT : Any\u003e Bundle.getSerializable(key: String?, strategy: DeserializationStrategy\u003cT\u003e): T?`\n- `fun Bundle.putSerializableContainer(key: String?, value: SerializableContainer?)`\n- `fun Bundle.getSerializableContainer(key: String?): SerializableContainer?`\n\nSimilar extensions are also available for [PersistableBundle](https://developer.android.com/reference/android/os/PersistableBundle).\n\n### Usage example\n\n#### Using StateKeeper\n\n\u003e ⚠️  Make sure you [setup](https://github.com/Kotlin/kotlinx.serialization#setup) `kotlinx-serialization` properly. \n\n```kotlin\nimport com.arkivanov.essenty.statekeeper.StateKeeper\nimport kotlinx.serialization.Serializable\n\nclass SomeLogic(stateKeeper: StateKeeper) {\n    // Use the saved State if any, otherwise create a new State\n    private var state: State = stateKeeper.consume(key = \"SAVED_STATE\", strategy = State.serializer()) ?: State()\n\n    init {\n        // Register the State supplier\n        stateKeeper.register(key = \"SAVED_STATE\", strategy = State.serializer()) { state }\n    }\n\n    @Serializable\n    private class State(\n        val someValue: Int = 0\n    )\n}\n```\n\n#### Saveable properties (experimental since version `2.2.0-alpha01`)\n\n```kotlin\nimport com.arkivanov.essenty.statekeeper.StateKeeper\nimport com.arkivanov.essenty.statekeeper.saveable\nimport kotlinx.serialization.Serializable\n\nclass SomeLogic(stateKeeper: StateKeeper) {\n    private var state: State by stateKeeper.saveable(serializer = State.serializer(), init = ::State)\n\n    @Serializable\n    private class State(val someValue: Int = 0)\n}\n```\n\n#### Saveable state holders (experimental since version `2.2.0-alpha01`)\n\n```kotlin\nimport com.arkivanov.essenty.statekeeper.StateKeeper\nimport com.arkivanov.essenty.statekeeper.saveable\nimport kotlinx.serialization.Serializable\n\nclass SomeLogic(stateKeeper: StateKeeper) {\n    private val viewModel by stateKeeper.saveable(serializer = State.serializer(), state = ViewModel::state) { savedState -\u003e\n        ViewModel(state = savedState ?: State())\n    }\n\n    private class ViewModel(var state: State)\n\n    @Serializable\n    private class State(val someValue: Int = 0)\n}\n```\n\n##### Polymorphic serialization (experimental)\n\nSometimes it might be necessary to serialize an interface or an abstract class that you don't own but have implemented. For this purpose Essenty provides `polymorphicSerializer` function that can be used to create custom polymorphic serializers for unowned base types.\n\nFor example a third-party library may have the following interface.\n\n```kotlin\ninterface Filter {\n    // Omitted code\n}\n```\n\nThen we can have multiple implementations of `Filter`.\n\n```kotlin\n@Serializable\nclass TextFilter(val text: String) : Filter { /* Omitted code */ }\n\n@Serializable\nclass RatingFilter(val stars: Int) : Filter { /* Omitted code */ }\n```\n\nNow we can create a polymorphic serializer for `Filter` as follows. It can be used to save and restore `Filter` directly via StateKeeper, or to have `Filter` as part of another `Serializable` class.\n\n```kotlin\nimport com.arkivanov.essenty.statekeeper.polymorphicSerializer\nimport com.slack.circuit.runtime.screen.Screen\nimport kotlinx.serialization.KSerializer\nimport kotlinx.serialization.Serializable\nimport kotlinx.serialization.modules.SerializersModule\nimport kotlinx.serialization.modules.polymorphic\n\nobject FilterSerializer : KSerializer\u003cFilter\u003e by polymorphicSerializer(\n    SerializersModule {\n        polymorphic(Filter::class) {\n            subclass(TextFilter::class, TextFilter.serializer())\n            subclass(RatingFilter::class, RatingFilter.serializer())\n        }\n    }\n)\n```\n\n#### Using the StateKeeperDispatcher manually\n\nOn Android, the `StateKeeper` obtained via one of the extensions described above automatically saves and restores the state. On other platforms (if needed) the state can be saved and restored manually. A default implementation of `StateKeeperDisptacher` interface can be instantiated using the corresponding builder function. The state can be encoded as a JSON string and saved using the corresponding platform-specific API.\n\n```kotlin\nimport com.arkivanov.essenty.statekeeper.SerializableContainer\nimport com.arkivanov.essenty.statekeeper.StateKeeper\nimport com.arkivanov.essenty.statekeeper.StateKeeperDispatcher\n\nval stateKeeperDispatcher = StateKeeperDispatcher(/*Previously saved state, or null*/)\nval someLogic = SomeLogic(stateKeeperDispatcher)\n\n// At some point later when it's time to save the state\nval savedState: SerializableContainer = stateKeeperDispatcher.save()\n\n// The returned SerializableContainer can now be saved using the corresponding platform-specific API\n```\n\n## InstanceKeeper\n\nWhen writing common code targetting Android, it might be required to retain objects over Android configuration changes. This use case is covered by the `InstanceKeeper` API, which is similar to the AndroidX [ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel).\n\n### Setup\n\nGroovy:\n```groovy\n// Add the dependency, typically under the commonMain source set\nimplementation \"com.arkivanov.essenty:instance-keeper:\u003cessenty_version\u003e\"\n```\n\nKotlin:\n```kotlin\n// Add the dependency, typically under the commonMain source set\nimplementation(\"com.arkivanov.essenty:instance-keeper:\u003cessenty_version\u003e\")\n```\n\n### Content\n\nThe main [InstanceKeeper](https://github.com/arkivanov/Essenty/blob/master/instance-keeper/src/commonMain/kotlin/com/arkivanov/essenty/instancekeeper/InstanceKeeper.kt) interface is responsible for storing object instances, represented by the [InstanceKeeper.Instance] interface. Instances of the `InstanceKeeper.Instance` interface survive Android Configuration changes, the `InstanceKeeper.Instance.onDestroy()` method is called when `InstanceKeeper` goes out of scope (e.g. the screen is finished). You can also find some handy [extension functions](https://github.com/arkivanov/Essenty/blob/master/instance-keeper/src/commonMain/kotlin/com/arkivanov/essenty/instancekeeper/InstanceKeeperExt.kt).\n\nThe [InstanceKeeperDispatcher](https://github.com/arkivanov/Essenty/blob/master/instance-keeper/src/commonMain/kotlin/com/arkivanov/essenty/instancekeeper/InstanceKeeperDispatcher.kt) interface extends `InstanceKeeper` and adds ability to destroy all registered instances.\n\nThe [InstanceKeeperOwner](https://github.com/arkivanov/Essenty/blob/master/instance-keeper/src/commonMain/kotlin/com/arkivanov/essenty/instancekeeper/InstanceKeeperOwner.kt) interface is just a holder of `InstanceKeeper`. It may be implemented by an arbitrary class, to provide convenient API.\n\n#### Android extensions\n\nFrom Android side, `InstanceKeeper` can be obtained by using special functions, can be found [here](https://github.com/arkivanov/Essenty/blob/master/instance-keeper/src/androidMain/kotlin/com/arkivanov/essenty/instancekeeper/AndroidExt.kt).\n\n### Usage example\n\n#### Using the InstanceKeeper\n\n```kotlin\nimport com.arkivanov.essenty.instancekeeper.InstanceKeeper\nimport com.arkivanov.essenty.instancekeeper.getOrCreate\n\nclass SomeLogic(instanceKeeper: InstanceKeeper) {\n    // Get the existing instance or create a new one\n    private val viewModel = instanceKeeper.getOrCreate { ViewModel() }\n}\n\n/*\n * Survives Android configuration changes.\n * ⚠️ Pay attention to not leak any dependencies.\n */\nclass ViewModel : InstanceKeeper.Instance {\n    override fun onDestroy() {\n        // Called when the screen is finished\n    }\n}\n```\n\n##### Alternative way (experimental since version 2.2.0-alpha01, stable since 2.2.0)\n\n```kotlin\nclass SomeLogic(instanceKeeperOwner: InstanceKeeperOwner) : InstanceKeeperOwner by instanceKeeperOwner {\n    // Get the existing instance or create a new one\n    private val viewModel = retainedInstance { ViewModel() }\n}\n```\n\n#### Using the InstanceKeeperDispatcher manually\n\nA default implementation of the `InstanceKeeperDispatcher` interface can be instantiated using the corresponding builder function:\n\n```kotlin\nimport com.arkivanov.essenty.instancekeeper.InstanceKeeper\nimport com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher\n\n// Create a new instance of InstanceKeeperDispatcher, or reuse an existing one\nval instanceKeeperDispatcher = InstanceKeeperDispatcher()\nval someLogic = SomeLogic(instanceKeeperDispatcher)\n\n// At some point later\ninstanceKeeperDispatcher.destroy()\n```\n\n## BackHandler\n\nThe `BackHandler` API provides ability to handle back button clicks (e.g. the Android device's back button), in common code. This API is similar to AndroidX [OnBackPressedDispatcher](https://developer.android.com/reference/androidx/activity/OnBackPressedDispatcher).\n\n### Setup\n\nGroovy:\n```groovy\n// Add the dependency, typically under the commonMain source set\nimplementation \"com.arkivanov.essenty:back-handler:\u003cessenty_version\u003e\"\n```\n\nKotlin:\n```kotlin\n// Add the dependency, typically under the commonMain source set\nimplementation(\"com.arkivanov.essenty:back-handler:\u003cessenty_version\u003e\")\n```\n\n### Content\n\nThe [BackHandler](https://github.com/arkivanov/Essenty/blob/master/back-handler/src/commonMain/kotlin/com/arkivanov/essenty/backhandler/BackHandler.kt) interface provides ability to register and unregister back button callbacks. When the device's back button is pressed, all registered callbacks are called in reverse order, the first enabled callback is called and the iteration finishes.\n\n\u003e Starting from `v1.2.x`, when the device's back button is pressed, all registered callbacks are sorted in ascending order first by priority and then by index, the last enabled callback is called.\n\n[BackCallback](https://github.com/arkivanov/Essenty/blob/master/back-handler/src/commonMain/kotlin/com/arkivanov/essenty/backhandler/BackCallback.kt) allows handling back events, including predictive back gestures.\n\nThe [BackDispatcher](https://github.com/arkivanov/Essenty/blob/master/back-handler/src/commonMain/kotlin/com/arkivanov/essenty/backhandler/BackDispatcher.kt) interface extends `BackHandler` and is responsible for triggering the registered callbacks. The `BackDispatcher.back()` method triggers all registered callbacks in reverse order, and returns `true` if an enabled callback was called, and `false` if no enabled callback was found.\n\n#### Android extensions\n\nFrom Android side, `BackHandler` can be obtained by using special functions, can be found [here](https://github.com/arkivanov/Essenty/blob/master/back-handler/src/androidMain/kotlin/com/arkivanov/essenty/backhandler/AndroidBackHandler.kt).\n\n### Predictive Back Gesture\n\nBoth `BackHandler` and `BackDispatcher` bring the new [Android Predictive Back Gesture](https://developer.android.com/guide/navigation/custom-back/predictive-back-gesture) to Kotlin Multiplatform. \n\n#### Predictive Back Gesture on Android\n\nOn Android, the predictive back gesture only works starting with Android T. On Android T, it works only between Activities, if enabled in the system settings. Starting with Android U, the predictive back gesture also works between application's screens inside an Activity. In the latter case, back gesture events can be handled using `BackCallback`.\n\n#### Predictive Back Gesture on other platforms\n\nOn all other platforms, predictive back gestures can be dispatched manually via `BackDispatcher`. This can be done e.g. by adding an overlay on top of the UI and handling touch events manually.\n\n### Usage example\n\n#### Using the BackHandler\n\n```kotlin\nimport com.arkivanov.essenty.backhandler.BackHandler\n\nclass SomeLogic(backHandler: BackHandler) {\n    private val callback = BackCallback {\n        // Called when the back button is pressed\n    }\n\n    init {\n        backHandler.register(callback)\n\n        // Disable the callback when needed\n        callback.isEnabled = false\n    }\n}\n```\n\n#### Using the BackDispatcher manually\n\nA default implementation of the `BackDispatcher` interface can be instantiated using the corresponding builder function:\n\n```kotlin\nimport com.arkivanov.essenty.backhandler.BackDispatcher\n\nval backDispatcher = BackDispatcher()\nval someLogic = SomeLogic(backDispatcher)\n\nif (!backDispatcher.back()) {\n    // The back pressed event was not handled\n}\n```\n\n## Author\n\nTwitter: [@arkann1985](https://twitter.com/arkann1985)\n\nIf you like this project you can always \u003ca href=\"https://www.buymeacoffee.com/arkivanov\" target=\"_blank\"\u003e\u003cimg src=\"https://cdn.buymeacoffee.com/buttons/v2/default-blue.png\" alt=\"Buy Me A Coffee\" height=32\u003e\u003c/a\u003e ;-)\n","funding_links":["https://github.com/sponsors/arkivanov","https://www.buymeacoffee.com/arkivanov","https://btc.com/1DXjn9e6rmbVvac3TH8hG3LdLtoA1CUvsM","https://etherscan.io/address/0xf027f5738f45676a54c15cf7753a0f66553947b9"],"categories":["Kotlin","Libraries"],"sub_categories":["🗃 Serializer"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farkivanov%2FEssenty","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farkivanov%2FEssenty","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farkivanov%2FEssenty/lists"}