{"id":13659597,"url":"https://github.com/skydoves/compose-stable-marker","last_synced_at":"2025-04-04T07:08:54.950Z","repository":{"id":183624141,"uuid":"670467725","full_name":"skydoves/compose-stable-marker","owner":"skydoves","description":"✒️ Compose stable markers for KMP to tell stable/immutable guarantees to the compose compiler.","archived":false,"fork":false,"pushed_at":"2025-03-20T05:34:27.000Z","size":377,"stargazers_count":245,"open_issues_count":0,"forks_count":2,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-03-28T06:09:54.796Z","etag":null,"topics":["android","compose","jetpack-compose","kotlin","skydoves"],"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/skydoves.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"skydoves"}},"created_at":"2023-07-25T05:59:02.000Z","updated_at":"2025-03-20T05:34:30.000Z","dependencies_parsed_at":"2023-07-25T08:00:44.305Z","dependency_job_id":"643e94ce-e605-449f-8c64-b5aea3d72230","html_url":"https://github.com/skydoves/compose-stable-marker","commit_stats":null,"previous_names":["skydoves/compose-stable-marker"],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skydoves%2Fcompose-stable-marker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skydoves%2Fcompose-stable-marker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skydoves%2Fcompose-stable-marker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skydoves%2Fcompose-stable-marker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/skydoves","download_url":"https://codeload.github.com/skydoves/compose-stable-marker/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247135144,"owners_count":20889421,"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":["android","compose","jetpack-compose","kotlin","skydoves"],"created_at":"2024-08-02T05:01:10.423Z","updated_at":"2025-04-04T07:08:54.922Z","avatar_url":"https://github.com/skydoves.png","language":"Kotlin","readme":"\u003ch1 align=\"center\"\u003eCompose Stable Marker\u003c/h1\u003e\u003c/br\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://opensource.org/licenses/Apache-2.0\"\u003e\u003cimg alt=\"License\" src=\"https://img.shields.io/badge/License-Apache%202.0-blue.svg\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://android-arsenal.com/api?level=21\"\u003e\u003cimg alt=\"API\" src=\"https://img.shields.io/badge/API-21%2B-brightgreen.svg?style=flat\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/skydoves/compose-stable-marker/actions/workflows/android.yml\"\u003e\u003cimg alt=\"Build Status\" \n  src=\"https://github.com/skydoves/compose-stable-marker/actions/workflows/android.yml/badge.svg\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/skydoves\"\u003e\u003cimg alt=\"Profile\" src=\"https://skydoves.github.io/badges/skydoves.svg\"/\u003e\u003c/a\u003e\n\u003c/p\u003e\u003cbr\u003e\n\n\u003cp align=\"center\"\u003e\n✒️ Compose stable markers were originated Compose runtime, which improves Compose performance by telling stable and skippable guarantees to the compose compiler from non-compose dependent modules. This library supports Kotlin Multiplatform.\n\u003c/p\u003e\u003cbr\u003e\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/skydoves/compose-stable-marker/assets/24237865/9ead142c-3a35-4027-932a-b1d4e1cd13c5\" /\u003e\n\u003c/p\u003e\n\n## Agenda\n\nThis library contains a few extracted Compose stable markers, such as [Stable](https://developer.android.com/reference/kotlin/androidx/compose/runtime/Stable), [Immutable](https://developer.android.com/reference/kotlin/androidx/compose/runtime/Immutable), and [StableMarker](https://developer.android.com/reference/kotlin/androidx/compose/runtime/StableMarker), which are used to communicate some guarantees to the compose compiler and mark class as producing immutable instances. You can utilize this library when you want to mark your properties/classes/functions with **Stable** or **Immutable**, which are from [compose-runtime](https://developer.android.com/jetpack/androidx/releases/compose-runtime) in your pure Kotlin module or non-compose dependent modules. So if you don't want to depend on the `compose-runtime` library, but you still want to improve your Compose performance by marking your models as stable in multiple module structure, you can use this library. If you want to learn more about **Skippable**, **Stable**, and **Immutable**, check out [6 Jetpack Compose Guidelines to Optimize Your App Performance](https://medium.com/proandroiddev/6-jetpack-compose-guidelines-to-optimize-your-app-performance-be18533721f9) and [Composable metrics by Chris Banes](https://chrisbanes.me/posts/composable-metrics/#skippable).\n\n## Download\n[![Maven Central](https://img.shields.io/maven-central/v/com.github.skydoves/compose-stable-marker.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22com.github.skydoves%22%20AND%20a:%compose-stable-marker%22)\n\n### Gradle\n\nAdd the `compileOnly` dependency below to your **module**'s `build.gradle.kts` file:\n\n```gradle\ndependencies {\n    compileOnly(\"com.github.skydoves:compose-stable-marker:1.0.6\")\n}\n```\n\nFor Kotlin Multiplatform, add the `compileOnly` dependency below to your **module**'s `build.gradle.kts` file:\n\n```gradle\nsourceSets {\n    val commonMain by getting {\n        dependencies {\n            compileOnly(\"com.github.skydoves:compose-stable-marker:$version\")\n        }\n    }\n}\n```\n\n## Stable\n\nThese hold data that is mutable but notify Composition upon mutating. This renders them stable since Composition is always informed of any changes to the state. This annotation implies are used for optimizations by the compose compiler if the assumptions below are met:\n\n1.  The result of `equals` will always return the same result for the same two instances.\n 2. When a public property of the type changes, composition will be notified.\n 3. All public property types are stable.\n\nYou can utilize the [Stable](https://developer.android.com/reference/kotlin/androidx/compose/runtime/Stable) annotation like a normal annotation in your pure Kotlin module or non-compose dependent modules. \u003cbr\u003e\u003cbr\u003e\n\n### Without Stable Annotation (Unskippable, and Unstable)\n\nLet's assume that you have a normal class without the `Stable` annotation:\n\n```kotlin\n// data module (pure Kotlin module)\npublic data class UnstableUser(\n  public val name: String,\n  public val devices: List\u003cString\u003e,\n  public val createdAt: Instant,\n)\n\n// feature module\n@Composable\nprivate fun UnstableUserFun(unstableUser: UnstableUser) {\n  Text(text = unstableUser.toString())\n}\n```\n\nFollowing the [Compose Compiler Metrics](https://github.com/JetBrains/kotlin/blob/master/plugins/compose/design/compiler-metrics.md), you'll get the result below:\n\n```\nrestartable scheme(\"[androidx.compose.ui.UiComposable]\") fun UnstableUserFun(\n  unstable unstableUser: UnstableUser\n)\n```\n\nAs you can see in the above metrics, the `UnstableUserFun` Composable function is not **Skippable**, and the `UnstableUser` class is marked as **unstable**. \n\n### With Stable Annotation (Skippable, and Stable)\n\nLet's assume that you have a normal class with the `Stable` annotation:\n\n```kotlin\n// data module (pure Kotlin module)\n@Immutable\npublic data class StableUser(\n  public val name: String,\n  public val devices: List\u003cString\u003e,\n  public val createdAt: Instant,\n)\n\n// feature module\n@Composable\nprivate fun StableUserFun(stableUser: StableUser) {\n  Text(text = stableUser.toString())\n}\n```\n\nFollowing the [Compose Compiler Metrics](https://github.com/androidx/androidx/blob/androidx-main/compose/compiler/design/compiler-metrics.md), you'll get the result below:\n\n```\nrestartable skippable scheme(\"[androidx.compose.ui.UiComposable]\") fun StableUserFun(\n  stable stableUser: StableUser\n)\n```\n\nAs you can see in the above metrics, the `StableUserFun` Composable function is **Skippable**, and the `StableUser` class is marked as **stable**. So your function will be completely skipped calling a function if the parameters haven't changed since the last call since all of your properties were marked as **Stable**.\n\n## Immutable\n\nWith the same approach of the `Stable` annotation, you can use this annotation like a normal `compose-runtime`'s one. As the name suggests, these hold data that is immutable. Since the data never changes, Compose can treat this as stable data. Composition enables optimizations based on the assumption that values read from the type will not change, using this annotation. \u003cbr\u003e\u003cbr\u003e\n\nLet's assume that you have a normal class with the `Immutable` annotation:\n\n```kotlin\n// data module (pure Kotlin module)\n@Immutable\npublic data class ImmutableUser constructor(\n  public val name: String,\n  public val devices: List\u003cString\u003e,\n  public val createdAt: Instant,\n)\n\n// feature module\n@Composable\nprivate fun ImmutableUserComposable(immutableUser: ImmutableUser) {\n  Text(text = immutableUser.toString())\n}\n```\n\nFollowing the [Compose Compiler Metrics](https://github.com/androidx/androidx/blob/androidx-main/compose/compiler/design/compiler-metrics.md), you'll get the result below:\n\n```\nrestartable skippable scheme(\"[androidx.compose.ui.UiComposable]\") fun ImmutableUser(\n  stable immutableUser: ImmutableUser\n)\n```\n\n## StableMarker\n\nAn annotation marked as `StableMarker` indicates a stable type, which obeys the following assumptions:\n\n 1. The result of [equals] will always return the same result for the same two instances.\n 2. When a public property of the type changes, composition will be notified.\n 3. All public property types are stable.\n\n# License\n```xml\nDesigned and developed by 2023 skydoves (Jaewoong Eum)\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","funding_links":["https://github.com/sponsors/skydoves"],"categories":["Libraries"],"sub_categories":["🍎 Compose UI"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskydoves%2Fcompose-stable-marker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fskydoves%2Fcompose-stable-marker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskydoves%2Fcompose-stable-marker/lists"}