Skip to content

Update circuit to v0.39.0 - #448

Open
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/circuit
Open

renovate[bot] wants to merge 1 commit into
mainfrom
renovate/circuit

Conversation

@renovate

@renovate renovate Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

Package Change Age Confidence
com.slack.circuit:circuit-test 0.33.10.39.0 age confidence
com.slack.circuit:circuitx-android 0.33.10.39.0 age confidence
com.slack.circuit:circuit-codegen 0.33.10.39.0 age confidence
com.slack.circuit:circuit-codegen-annotations 0.33.10.39.0 age confidence
com.slack.circuit:circuit-foundation 0.33.10.39.0 age confidence

Release Notes

slackhq/circuit (com.slack.circuit:circuit-test)

v0.39.0

Compare Source

2026-09-15

New
  • circuit-retained: Add RetainedStateRegistry.forgetValue(key), the single-key counterpart to forgetUnclaimedValues(). Custom RetainedStateRegistry implementations will need to implement it.
Changed
  • Update to Kotlin 2.4.20.
  • Update Compose Multiplatform to 1.12.0.
  • Update androidx Compose (including Compose Runtime) to 1.12.1.
  • Update AGP to 9.4.0.
  • [code gen] Update to KotlinPoet 2.4.0.
Fixed
  • circuit-foundation: NavigableCircuitContent now clears saved and retained state for records truncated out of the navstack while not composed.
  • circuit-retained: RetainedStateHolder.removeState now retires removed values instead of dropping them silently.

v0.38.0

Compare Source

2026-08-26

Screen and PopResult are no longer Parcelable

Screen and PopResult no longer extend Parcelable on Android. Apps upgrading to this release should choose the persistence strategy that fits each type

  • Already using kotlinx-serialization

    • Keep the existing SerializableCircuitSaver or ReflectiveSerializableCircuitSaver setup. Remove @Parcelize, and use Screen or PopResult directly instead of the Parcelable marker interfaces.
  • Keep Android Parcelable persistence

    • Retain @Parcelize and change the type to implement ParcelableScreen or ParcelablePopResult. The registry-backed default saver accepts these types on Android.
  • Migrate gradually

    • Put the serializing saver first and retain the registry-backed saver as a fallback for Parcelable types:

      val circuit =
        Circuit.Builder()
          .setCircuitSaver { fallbackSaver ->
            serializableSaver + fallbackSaver
          }
          .build()
  • Do not persist navigation state

    • Configure CircuitSaver.NoOp. Restored stacks start again from their initial value.

See Saving navigation state for complete setup and migration examples.

Saving an unsupported value now fails instead of silently dropping it. To intentionally drop values that no earlier saver supports, append CircuitSaver.Dropping { value -> ... } to the saver chain. Use CircuitSaver.NoOp as the final saver when dropped values do not need to be reported.

New
  • rememberDefaultCircuitSaver() creates a saver backed by the current Compose SaveableStateRegistry. CircuitCompositionLocals(circuit) uses it automatically when no static or inherited saver is available.
  • Combine CircuitSaver instances with +. Savers are tried from left to right, and the first saver that supports a value owns the save or restore operation.
  • Circuit.Builder.setCircuitSaver { fallbackSaver -> ... } combines an app saver with the saver inherited from the composition or created from its registry.
  • Custom savers can override canSave and canRestore to participate in a composite.
Removed
  • Removed DefaultCircuitSaver. CircuitCompositionLocals(circuit) now creates a registry-backed saver when needed. Call rememberDefaultCircuitSaver() when a saver must be passed explicitly.
  • Removed the deprecated companion Saver properties from SaveableBackStack and SaveableNavStack. Use the Saver(CircuitSaver) functions.
Fixed
  • Fixed R8 failures in apps using @CircuitSerializable or @CircuitInject without Hilt on the runtime classpath.

v0.37.1

Compare Source

2026-08-22

New
  • circuit-retained: Add experimental RetainedValuesStoreProvider and RetainedValuesStoreOwner APIs for preserving AndroidX retain values across composition recreation on non-Android platforms. This is temporary cover until androidx's implementation offers a non-android solution. See the first-party retain documentation.
  • circuit-retained: Add CircuitRetainedSettings.enforceRetainObserverCompatibility, an opt-in check that retained values implementing RememberObserver also implement RetainObserver.
    • Circuit's internal navigation observer now passes this check.
Changed
  • circuit-retained: Compose's first-party retain backing is now enabled by default on Android. Set CircuitRetainedSettings.useFirstParty = false before the first composition to use the previous ViewModel-based backing. Other platforms remain opt-in.

v0.37.0

Compare Source

2026-08-22

Ignore this release, it was accidentally cut with some stale branch changes!

v0.36.1

Compare Source

2026-08-11

Fixed
  • Fixed SerializableCircuitSaver round trips for registered types with nested Screen or PopResult properties.

v0.36.0

Compare Source

2026-08-05

New
  • circuit-codegen can now generate kotlinx-serialization registrations for Screen and PopResult types. Annotate each type with @CircuitSerializable(scope). The annotation supplies the default kotlinx serializer, and circuit-codegen contributes a registration through the selected DI framework. Pass the injected Set<CircuitSerializerRegistration> to SerializableCircuitSaver.

    A Metro setup looks like this:

    @Parcelize
    @CircuitSerializable(AppScope::class)
    data class DetailScreen(val itemId: Long) : Screen
    
    @Provides
    fun provideCircuitSaver(
      registrations: Set<CircuitSerializerRegistration>,
    ): CircuitSaver = SerializableCircuitSaver(registrations)
    • Metro, Hilt, kotlin-inject-anvil, and Anvil can provide registrations declared in different Gradle modules to the same application graph.
    • Apps without one of these DI frameworks can continue to register types manually or use the reflective saver on JVM and Android.
    • Android screens and results must still implement Parcelable. A future release will remove this requirement.

    See the code generation guide for setup and generated code examples.

  • rememberAnsweringNavigator and answeringNavigationAvailable are now part of circuit-runtime, so presenters can use them through circuit-runtime-presenter.

    • NavigableCircuitContent continues to provide result delivery. Without it, rememberAnsweringNavigator returns the supplied fallback navigator.
  • AnsweringResultHandler has moved to circuit-runtime.

    • rememberAnsweringResultHandler remains in circuit-foundation to provide saveable-state persistence without adding Compose runtime-saveable to circuit-runtime.
Fixed
  • When CircuitRetainedSettings.useFirstParty is enabled, retain {} values removed from conditional UI now retire normally instead of surviving for the navigation record's entire lifetime.
Deprecated
  • The Foundation answeringNavigationAvailable function and rememberAnsweringNavigator overloads that take a fallback Navigator are deprecated. Use the runtime versions.
    • IntelliJ IDEA and Android Studio versions before the 2026.2 platform may fail to update imports for these same-named replacements or leave a literal T in a reified call. Update the import manually if this occurs (KTIJ-12093).
  • The experimental Foundation AnsweringResultHandler type is deprecated in favor of its runtime replacement. Its Saver APIs have been removed. Use rememberAnsweringResultHandler for a saveable handler.
Changed
  • circuit-retained: Added the key-based retain(key = ...) and retainSaveable(...) APIs. Non-saveable rememberRetained(key = ...) calls are now deprecated in favor of retain(key = ...). Unkeyed and saveable rememberRetained variants and rememberRetainedSaveable remain supported but will eventually be deprecated.
Contributors

Special thanks to the following contributors for contributing to this release!

v0.35.1

Compare Source

2026-07-15

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 against CircuitSaveable in the SavedStateConfiguration's
    serializersModule.
  • ReflectiveSerializableCircuitSaver(configuration) (in the JVM/Android-only
    circuit-serialization-reflect artifact): 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 add Parcelable on Android.
    Migrate common-code values to these to keep using the Parcelable strategy once Screen and
    PopResult drop their Parcelable supertypes.
  • 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) backs lifecycleRetainedStateRegistry() with a root-level retain call instead of a Circuit-managed ViewModel, delegating configuration-change survival to the RetainedValuesStore installed in the composition.
  • All rememberRetained/rememberRetainedSaveable semantics are unchanged.
  • First-party retain {} can be used directly in presenters and UIs alongside rememberRetained. In navigated content, retained values follow their record's lifetime.

See the circuit-retained README for more details.

Deprecated
  • SaveableBackStack.Record.args and SaveableBackStack.push(screen, args). Pass data through the Screen itself instead; args will be removed in a future release. This matches what the new SaveableNavStack already does.
  • The Saver vals on SaveableBackStack, SaveableNavStack, and AnsweringResultHandler companions. Use the Saver(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 awaitResult suspending indefinitely when a pending pop result cannot be saved or restored.
  • SubCircuit: Fix subcircuit-codegen Metro mode (subcircuit.codegen.mode=metro). It previously
    required Dagger's @AssistedFactory even in Metro mode, generated @javax.inject.Inject instead of
    Metro's @Inject, and referenced dev.zacsweers.metro.annotations.ContributesIntoSet (wrong package).
    Metro mode now accepts dev.zacsweers.metro.AssistedFactory and emits dev.zacsweers.metro.Inject +
    dev.zacsweers.metro.ContributesIntoSet, so generated factories compile against Metro.
Changed
  • SubCircuit: @SubCircuitInject is now handled by the main circuit-codegen processor.
    circuitx-subcircuit-codegen is now a relocation pointer to circuit-codegen, but prefer
    depending on circuit-codegen directly.
  • 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!

v0.35.0

Compare Source

2026-07-15

Ignore this release, we goofed and it didn't include regenerated baseline profiles! Use 0.35.1.

v0.34.0

Compare Source

2026-05-09

New
SubCircuit

SubCircuit is a lightweight version of Circuit for rendering nested presenter/UI pairs that delegate events to an outer component rather than handling navigation themselves. Use it for nested, reusable UI components that don't need direct navigation access, and need to delegate cross-cutting concerns (navigation, dialogs) to a parent. See the SubCircuit docs for more info!

Artifacts

  • circuitx-subcircuit — core types (SubScreen, SubPresenter, SubUi, SubCircuitContent).
  • circuitx-subcircuit-codegen — KSP @​SubCircuitInject to wire presenter and UI factories into the DI graph. Supports Anvil (default) and Metro.
  • circuitx-subcircuit-test — a .test {} extension for SubPresenters, built on Molecule and Turbine.
Fixes
  • SaveableNavStack - isRecordReachable() off-by-one where depth of 0 didn't check the current record
  • SaveableBackStack — Update isRecordReachable() to match SaveableNavStack behaviour
  • [codegen] Propagate qualifier annotations from @CircuitInject declarations to generated factories.
Changes
  • Breaking: Moved NavDecoration from backstack to circuit-foundation and added a Navigator parameter to DecoratedContent. AnimatedNavDecorator now has an updateNavigator method to receive the new Navigator parameter. This gives decorations direct access to the correct Navigator for handling back gestures.
  • Breaking: Add GestureNavigationEventListener and an optional listener parameter to GestureNavigationDecorationFactory, for observing the back gesture lifecycle (e.g. analytics). It's observational only, the Navigator still drives the pop.
  • SaveableNavStack & SaveableBackStack - Made the Savers public apis
  • [codegen] Function-based @CircuitInject declarations now treat any non-circuit-provided parameter as an injected dependency across all modes. The generated factory accepts it as a provider (Provider<T> for Dagger/Anvil/Hilt, () -> T for kotlin-inject and Metro) and invokes it once at create() time, hoisted above the composable presenterOf { } / ui { } block so it isn't re-invoked on every recomposition. Parameters already declared as Provider<T> or Lazy<T> are passed through as-is rather than re-wrapped. In metro and kotlin-inject modes, () -> T is also passed through; in Dagger/Anvil/Hilt modes it is treated as a regular dependency.
  • [codegen] @CircuitInject-annotated classes must now be injectable — annotate the class or a constructor with @Inject. Previously, classes without @Inject silently generated a direct constructor call that could fail to compile.
  • [codegen/metro] Generate function providers when Metro mode is enabled. Note this requires enableFunctionProviders to be set enabled, which will be the default in Metro 1.0.0.
Misc:
  • Kotlin 2.4.0
  • Compose 1.11.0
  • Migrate to AGP 9
  • Remove deprecated X64 Apple targets.
  • [samples] Added a composite presenter Inbox sample
  • [samples] Add iOS support to STAR sample
Contributors

Special thanks to the following contributors for contributing to this release!


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Enabled.

Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about these updates again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate
renovate Bot enabled auto-merge (squash) June 10, 2026 01:40
@renovate
renovate Bot force-pushed the renovate/circuit branch from 7074d3b to 762ce87 Compare July 16, 2026 05:31
@renovate renovate Bot changed the title Update circuit to v0.34.0 Update circuit to v0.35.1 Jul 16, 2026
@renovate
renovate Bot force-pushed the renovate/circuit branch from 762ce87 to 90d5dcd Compare August 5, 2026 06:12
@renovate renovate Bot changed the title Update circuit to v0.35.1 Update circuit to v0.36.0 Aug 5, 2026
@renovate
renovate Bot force-pushed the renovate/circuit branch from 90d5dcd to 601ae1d Compare August 12, 2026 03:36
@renovate renovate Bot changed the title Update circuit to v0.36.0 Update circuit to v0.36.1 Aug 12, 2026
@renovate
renovate Bot force-pushed the renovate/circuit branch from 601ae1d to df36abb Compare August 22, 2026 22:45
@renovate renovate Bot changed the title Update circuit to v0.36.1 Update circuit to v0.37.0 Aug 22, 2026
@renovate
renovate Bot force-pushed the renovate/circuit branch from df36abb to 43579a0 Compare August 23, 2026 01:08
@renovate renovate Bot changed the title Update circuit to v0.37.0 Update circuit to v0.37.1 Aug 23, 2026
@renovate
renovate Bot force-pushed the renovate/circuit branch from 43579a0 to 4986efc Compare August 26, 2026 20:48
@renovate renovate Bot changed the title Update circuit to v0.37.1 Update circuit to v0.38.0 Aug 26, 2026
@renovate
renovate Bot force-pushed the renovate/circuit branch from 4986efc to c60d62c Compare September 16, 2026 01:11
@renovate renovate Bot changed the title Update circuit to v0.38.0 Update circuit to v0.39.0 Sep 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants