New
Pluggable Screen/PopResult persistence with CircuitSaver
Circuit's saveable back stacks no longer mandate how Screens and PopResults are persisted. A new CircuitSaver abstraction in circuit-runtime-screen converts them to and from saveable representations. Everything that saves navigation state (rememberSaveableBackStack, rememberSaveableNavStack, and answering results) now accepts one.
This is the first step toward removing the Parcelable supertypes from Screen and PopResult on Android in a future release. In 0.35, both types still need to be Parcelable even when using a serializing saver or CircuitSaver.NoOp. The default saver preserves the current Parcelable behavior. To prepare for the later removal, implement ParcelableScreen or ParcelablePopResult on types that should remain Parcelable, or adopt a serializing saver.
Configure a saver by passing it explicitly, providing LocalCircuitSaver at your app root via ProvideCircuitSaver, or with Circuit.Builder.setCircuitSaver (reaches only content inside CircuitCompositionLocals).
New artifacts: circuit-serialization and circuit-serialization-reflect
For kotlinx-serialization users, the new circuit-serialization artifact persists @Serializable screens and pop results to SavedState via androidx.savedstate:
SerializableCircuitSaver(configuration): KMP-friendly; register screens and results for polymorphic serialization againstCircuitSaveablein theSavedStateConfiguration'sserializersModule.ReflectiveSerializableCircuitSaver(configuration)(in the JVM/Android-onlycircuit-serialization-reflectartifact): resolves serializers reflectively with no registration needed. It embeds the R8/ProGuard rules it needs, so minified apps need no additional Circuit-specific rules.
Both savers can restore navigation state saved by Circuit 0.34's default saver. They restore leniently: records that no longer decode, such as after an app update removed a screen, are dropped rather than crashing. Pass an onRestoreError callback to observe drops. The bottom-navigation sample demonstrates the kotlinx setup end to end.
Also new:
ParcelableScreen/ParcelablePopResult: common interfaces that addParcelableon Android. Migrate common-code values to these to keep using the Parcelable strategy onceScreenandPopResultdrop theirParcelablesupertypes.CircuitSaver.NoOp: disables navigation state persistence entirely.
circuit-retained is migrating to upstream retained APIs
Compose recently introduced a retain API, which is essentially a first-party solution for what circuit-retained has long offered. While it's not fully ready to replace all the things circuit-retained covered, most of the core functionality is there. As such, we are beginning to wind down circuit-retained in favor of the first-party solution where it makes sense.
This will a multi-phase migration over multiple releases, starting with initial interop in this release to allow making circuit-retained run on top of the first-party retain APIs under the hood. This allows for testing use without migrating anything in your codebase.
- Setting
CircuitRetainedSettings.useFirstParty = true(before the first composition) backslifecycleRetainedStateRegistry()with a root-levelretaincall instead of a Circuit-managedViewModel, delegating configuration-change survival to theRetainedValuesStoreinstalled in the composition. - All
rememberRetained/rememberRetainedSaveablesemantics are unchanged. - First-party
retain {}can be used directly in presenters and UIs alongsiderememberRetained. In navigated content, retained values follow their record's lifetime.
See the circuit-retained README for more details.
Deprecated
SaveableBackStack.Record.argsandSaveableBackStack.push(screen, args). Pass data through theScreenitself instead;argswill be removed in a future release. This matches what the newSaveableNavStackalready does.- The
Savervals onSaveableBackStack,SaveableNavStack, andAnsweringResultHandlercompanions. Use theSaver(CircuitSaver)functions instead.
Fixed
- Fixed saveable stacks restoring empty when every record is dropped. Saveable nav stacks now discard incomplete forward history and fall back to their initial value if the active screen or its back history cannot be restored.
- Fixed stored back-stack snapshots being partially restored or associated with a different root when one of their records cannot be restored.
- Fixed
awaitResultsuspending indefinitely when a pending pop result cannot be saved or restored. - SubCircuit: Fix
subcircuit-codegenMetro mode (subcircuit.codegen.mode=metro). It previously required Dagger's@AssistedFactoryeven in Metro mode, generated@javax.inject.Injectinstead of Metro's@Inject, and referenceddev.zacsweers.metro.annotations.ContributesIntoSet(wrong package). Metro mode now acceptsdev.zacsweers.metro.AssistedFactoryand emitsdev.zacsweers.metro.Inject+dev.zacsweers.metro.ContributesIntoSet, so generated factories compile against Metro.
Changed
- SubCircuit:
@SubCircuitInjectis now handled by the maincircuit-codegenprocessor.circuitx-subcircuit-codegenis now a relocation pointer tocircuit-codegen, but prefer depending oncircuit-codegendirectly. - Update Android compileSdk to
37. - Update Compose Runtime to
1.11.4. - Update Dagger to
2.60.1.
Contributors
Special thanks to the following contributors for contributing to this release!
What's Changed
- Update metro to v1.2.0 by @slack-oss-bot in #2742
- Update dependency fastlane to v2.236.1 by @slack-oss-bot in #2740
- Update coil to v3.5.0 by @slack-oss-bot in #2741
- Update metro to v1.2.1 by @slack-oss-bot in #2744
- docs: Add Circuit recipes by @stagg in #2743
- Update roborazzi to v1.64.0 by @slack-oss-bot in #2746
- Add val to Presenter code example by @ryanbrookepayne in #2747
- [docs] Reorganize the docsite layout by @stagg in #2749
- Update dependency mkdocs-redirects to v1.2.3 by @slack-oss-bot in #2750
- Update compose.runtime to v1.11.3 by @slack-oss-bot in #2755
- Fix subcircuit-codegen Metro mode by @wiyarmir in #2753
- Update Gradle to v9.6.0 by @slack-oss-bot in #2756
- Update androidx.compose to v1.11.3 by @slack-oss-bot in #2754
- Update actions/cache action to v6 by @slack-oss-bot in #2762
- Update dependency click to v8.4.2 by @slack-oss-bot in #2763
- Update dagger to v2.60 by @slack-oss-bot in #2765
- Update ktor monorepo to v3.5.1 by @slack-oss-bot in #2764
- Update actions/checkout action to v7 by @slack-oss-bot in #2757
- Update Gradle to v9.6.1 by @slack-oss-bot in #2766
- Update dependency pymdown-extensions to v11 by @slack-oss-bot in #2760
- Update dependency dev.zacsweers.metro:runtime to v1.3.0 by @slack-oss-bot in #2770
- Update androidx.compose to v1.11.4 by @slack-oss-bot in #2771
- Update dependency androidx.compose.runtime:runtime to v1.11.4 by @slack-oss-bot in #2773
- Update dependency androidx.test.uiautomator:uiautomator to v2.4.0 by @slack-oss-bot in #2774
- Update benchmark to v1.5.0-alpha07 by @slack-oss-bot in #2772
- Update dependency pymdown-extensions to v11.0.1 by @slack-oss-bot in #2776
- Update roborazzi to v1.65.0 by @slack-oss-bot in #2777
- Update roborazzi to v1.66.0 by @slack-oss-bot in #2779
- Update dependency fastlane to v2.237.0 by @slack-oss-bot in #2778
- Update dagger to v2.60.1 by @slack-oss-bot in #2781
- Update roborazzi to v1.67.0 by @slack-oss-bot in #2782
- First party retained migration: phase 1 by @ZacSweers in #2780
- Phase 1 rework of Screen/PopResult serialization by @ZacSweers in #2784
- Update dependency org.jetbrains.androidx.savedstate:savedstate to v1.4.0 by @slack-oss-bot in #2787
- Update dependency org.jetbrains.kotlinx:kotlinx-serialization-core to v1.11.0 by @slack-oss-bot in #2788
- Update ksp monorepo to v2.3.10 by @slack-oss-bot in #2786
- Update roborazzi to v1.68.0 by @slack-oss-bot in #2790
- Update dependency com.slack.lint.compose:compose-lint-checks to v1.5.3 by @slack-oss-bot in #2789
- Update dependency dev.zacsweers.metro:runtime to v1.3.1 by @slack-oss-bot in #2791
- Update dependency com.facebook:ktfmt to v0.64 by @slack-oss-bot in #2761
- Merge subcircuit codegen into the main Circuit processor by @stagg in #2785
- Retained migration part 2 by @ZacSweers in #2792
- Migrate to kempt for formatting by @ZacSweers in #2795
- Update ZacSweers/kempt action to v0.3.0 by @slack-oss-bot in #2798
- Seal CircuitSaveable + harden CircuitSaver API by @ZacSweers in #2793
- Update dependency ruby to v4.0.6 by @slack-oss-bot in #2797
- Update dependency dev.zacsweers.metro:runtime to v1.3.2 by @slack-oss-bot in #2796
- Changelog and doc prep by @ZacSweers in #2799
- Update kotlin monorepo to v2.4.10 by @slack-oss-bot in #2800
- Migrate Renovate config by @slack-oss-bot in #2804
- Update renovatebot/github-action action to v46 by @slack-oss-bot in #2803
- Update agp to v9.3.0 by @slack-oss-bot in #2801
- Update mavenPublish version to 0.37.0 by @stagg in #2805
- Raise compileSdk to 37 by @ZacSweers in #2794
- Update renovatebot/github-action action to v40.3.6 by @slack-oss-bot in #2802
- Update jb.lifecycle to v2.11.0-rc01 by @slack-oss-bot in #2769
- Improve retained changelog notes by @ZacSweers in #2806
New Contributors
Full Changelog: 0.34.0...0.35.1