Skip to content

Improve Composable stabilities#2478

Merged
skydoves merged 3 commits into
mainfrom
improve/compose-stability
Jul 8, 2025
Merged

Improve Composable stabilities#2478
skydoves merged 3 commits into
mainfrom
improve/compose-stability

Conversation

@skydoves

@skydoves skydoves commented Jul 2, 2025

Copy link
Copy Markdown
Member

Improve Composable stabilities by adding stability annotations to states, functions, and classes.

  • Added @Immutable when all public properties of a class are completely immutable typically for Kotlin data classes representing domain models where the state never changes after creation.
  • Added @Stable for mutable types or interfaces with multiple implementations, as long as they provide consistent outputs for the same inputs, commonly used for UI state holders to enable optimized recomposition.

Differences

Those below composable functions have been "Skippable" now from "Unskippable".

Unnamed: 0 package name
1 com.revenuecat.purchases.ui.revenuecatui.LoadedPaywall LoadedPaywall
2 com.revenuecat.purchases.ui.revenuecatui.TemplatePaywall TemplatePaywall
9 com.revenuecat.purchases.ui.revenuecatui.LoadingPaywall LoadingPaywall
13 com.revenuecat.purchases.ui.revenuecatui.PaywallDialog PaywallDialog
199 com.revenuecat.purchases.ui.revenuecatui.composables.ConsistentPackageContentView ConsistentPackageContentView
233 com.revenuecat.purchases.ui.revenuecatui.composables.OfferDetails OfferDetails
241 com.revenuecat.purchases.ui.revenuecatui.composables.PurchaseButton PurchaseButton
329 com.revenuecat.purchases.ui.revenuecatui.templates.Template1 Template1
330 com.revenuecat.purchases.ui.revenuecatui.templates.Template1MainContent Template1MainContent
346 com.revenuecat.purchases.ui.revenuecatui.templates.Template2 Template2
347 com.revenuecat.purchases.ui.revenuecatui.templates.Template2PortraitContent Template2PortraitContent
348 com.revenuecat.purchases.ui.revenuecatui.templates.Template2LandscapeContent Template2LandscapeContent
349 com.revenuecat.purchases.ui.revenuecatui.templates.IconImage IconImage
350 com.revenuecat.purchases.ui.revenuecatui.templates.Title Title
366 com.revenuecat.purchases.ui.revenuecatui.templates.Subtitle Subtitle
367 com.revenuecat.purchases.ui.revenuecatui.templates.AnimatedPackages AnimatedPackages
411 com.revenuecat.purchases.ui.revenuecatui.templates.Template3 Template3
412 com.revenuecat.purchases.ui.revenuecatui.templates.PortraitContent PortraitContent
413 com.revenuecat.purchases.ui.revenuecatui.templates.LandscapeContent LandscapeContent
414 com.revenuecat.purchases.ui.revenuecatui.templates.Icon Icon
415 com.revenuecat.purchases.ui.revenuecatui.templates.Features Features
436 com.revenuecat.purchases.ui.revenuecatui.templates.Template4 Template4
437 com.revenuecat.purchases.ui.revenuecatui.templates.Template4MainContent Template4MainContent
438 com.revenuecat.purchases.ui.revenuecatui.templates.Packages Packages
445 com.revenuecat.purchases.ui.revenuecatui.templates.Template5 Template5
446 com.revenuecat.purchases.ui.revenuecatui.templates.Template5PortraitContent Template5PortraitContent
447 com.revenuecat.purchases.ui.revenuecatui.templates.Template5LandscapeContent Template5LandscapeContent
455 com.revenuecat.purchases.ui.revenuecatui.templates.Template7 Template7

@skydoves skydoves requested a review from a team July 2, 2025 01:43
@skydoves skydoves self-assigned this Jul 2, 2025
@codecov

codecov Bot commented Jul 4, 2025

Copy link
Copy Markdown

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 78.33%. Comparing base (eb3d010) to head (6a8710b).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2478   +/-   ##
=======================================
  Coverage   78.33%   78.33%           
=======================================
  Files         286      286           
  Lines       10432    10432           
  Branches     1500     1500           
=======================================
  Hits         8172     8172           
  Misses       1614     1614           
  Partials      646      646           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@JayShortway JayShortway left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing! Thanks for doing this! 🙌 Just some questions.

Comment on lines +17 to 20
@Stable
@JvmSynthetic
@Composable
internal fun rememberButtonComponentState(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious: do you think adding @Stable could theoretically introduce bugs, e.g. when it's not actually stable?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, theoretically, but in practice, it's not a common situation. Even when parameters are marked as @Stable, Compose ultimately determines skippability using the following equality checks:

  • Unstable parameters are compared using instance equality (===)
  • Stable parameters are compared using structural/object equality (equals())

Additionally, Strong Skipping Mode is now enabled by default as of Kotlin 2.0.20. This mode enhances skipping behavior and was designed to be backward-compatible, minimizing the chance of introducing bugs even in existing projects.

So while misusing @Stable or @Immutable can theoretically lead to subtle issues, Compose’s internal safeguards and runtime checks reduce the likelihood of real world problems, especially for most common use cases.

https://developer.android.com/develop/ui/compose/performance/stability/strongskipping

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Let's keep an eye out whether we see these subtle issues.

Comment on lines +33 to 36
@Stable
@JvmSynthetic
@Composable
internal fun rememberUpdatedStackComponentState(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a bunch more of these *ComponentStates. Did you try adding it to those as well? We have:

  • TextComponentState
  • ImageComponentState
  • IconComponentState
  • CarouselComponentState
  • TabsComponentState
  • TimelineComponentState

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a great point. Thanks for bringing it up! I marked those components accordingly in 0a0870a, and I’ll continue refining and updating more in upcoming PRs.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, thank you!

@emerge-tools

emerge-tools Bot commented Jul 7, 2025

Copy link
Copy Markdown

📸 Snapshot Test

648 unchanged

Name Added Removed Modified Renamed Unchanged Errored Approval
TestPurchasesUIAndroidCompatibility
com.revenuecat.testpurchasesuiandroidcompatibility
0 0 0 0 397 0 N/A
TestPurchasesUIAndroidCompatibility Paparazzi
com.revenuecat.testpurchasesuiandroidcompatibility.paparazzi
0 0 0 0 251 0 N/A

🛸 Powered by Emerge Tools

@tonidero tonidero left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice perf gains!!

@skydoves skydoves added this pull request to the merge queue Jul 8, 2025
Merged via the queue into main with commit 1de5ad2 Jul 8, 2025
14 checks passed
@skydoves skydoves deleted the improve/compose-stability branch July 8, 2025 00:42
github-merge-queue Bot pushed a commit that referenced this pull request Jul 9, 2025
**This is an automatic release.**

## RevenueCat SDK
### ✨ New Features
* feat(purchases): Add setPostHogUserId() method to Purchases API
(#2495) via Hussain Mustafa (@hussain-mustafa990)
### 🐞 Bugfixes
* Improves button progress indicator size calculation. (#2485) via
JayShortway (@JayShortway)

### 🔄 Other Changes
* Revert "BC8 migration (#2477)" (#2501) via Toni Rico (@tonidero)
* Add codelab instructions on README file (#2489) via Jaewoong Eum
(@skydoves)
* Use collectAsStateWithLifecycle instead of collectAsState in Compose
(#2488) via Jaewoong Eum (@skydoves)
* Improve Composable stabilities (#2478) via Jaewoong Eum (@skydoves)
* [AUTOMATIC][Paywalls V2] Updates paywall-preview-resources submodule
(#2486) via RevenueCat Git Bot (@RCGitBot)
* BC8 migration (#2477) via Toni Rico (@tonidero)
* Fixes building sample apps with SNAPSHOT dependencies (#2483) via
JayShortway (@JayShortway)
* [AUTOMATIC][Paywalls V2] Updates paywall-preview-resources submodule
(#2484) via RevenueCat Git Bot (@RCGitBot)

Co-authored-by: revenuecat-ops <ops@revenuecat.com>
github-merge-queue Bot pushed a commit that referenced this pull request Aug 21, 2025
…or increasing UI performances (#2608)

### Motivation

This is a follow-up task from
[#2478](#2478)

Introduce runtime annotations library (which is completely [JVM-based
annotations](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/runtime/runtime-annotation/src/commonMain/kotlin/androidx/compose/runtime/annotation/compose-runtime-annotation-documentation.md))
and add stability annotations on the purchase core models.

~Unfortunately, the AndroidX runtime annotations library requires a
compile SDK version above 34. So for now, I’ve switched to using
[compose-stable-marker](https://github.com/skydoves/compose-stable-marker)
instead. It offers the exact same functionality, was created two years
earlier than the official library, and is already widely adopted by many
libraries and SDKs.~
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants