Compose HotSwan Blog

Compose HotSwan 2.2.0: Faster Saves and More of Your State Kept

Jaewoong Eum
Jaewoong Eum (skydoves)

October 3, 2026 · 7 min read

Compose HotSwan 2.2.0 is out, and most of it is about the save you make dozens of times a day on Android. A save that changes code inside a composable now spends far less time inside HotSwan, more of what was on your screen survives an edit, and far fewer saves end with a warning about a problem that was never there. When a save cannot apply, HotSwan now says so sooner and tells you what to do about it.

In this article, you'll explore how much faster an everyday save got, which kinds of state now survive an edit, the warnings and failures that were cleaned up, what changed on iOS and Compose Desktop, and the one step to take when you upgrade.

Faster saves on Android

HotSwan applies a save in one of two ways. A value edit, such as a color, a padding, or a piece of text, usually takes the fast path and skips compilation entirely. Everything else, such as editing code inside a composable, adding a call, or reworking a layout, takes the structural reload: your code is compiled and the new version is applied to the running app. That second kind of save is where 2.2.0 spent most of its effort.

When a save only changes code inside composables, without adding or removing calls, HotSwan now reruns just the composables that ran that code, from the second such save on. Adding or removing a call, or changing a regular function, still reruns the whole screen. HotSwan also finishes its own checks sooner on every save. Here is what that looks like on our sample apps on an Android emulator:

What you doSwitched off or before the fix2.2.0
HotSwan's part of a save that changes code inside a composable (median)944 ms385 ms
Time for the screen to finish updating once the new code arrives (median)605 ms189 ms
The whole save, Kotlin compile includedBaseline9 to 15% faster
A save on a screen with an infinite animationCould wait up to 2.6 s for the animationNo wait, later saves settled in 120 to 216 ms
A value edit inside a LazyColumn item that takes the structural reloadEvery visible item started over, settled in 204 to 291 msItems kept, settled in 104 to 122 ms

Each row compares the same app with the change switched off, or the build just before the fix, so the difference is the change itself rather than everything else since 2.1.0. The first two rows are the part HotSwan controls, and both got more than twice as fast. Most of a structural reload is the Kotlin compiler, which HotSwan does not control, so the whole save improves by a smaller share: 9 to 15% on the same app.

The last two rows matter as much as the timing. A screen with an infinite animation, such as a loading shimmer or a pulsing indicator, could keep a save waiting for the animation even though the screen had already updated. And a value edit inside a list item that had to take the structural reload restarted every visible item, including its animations and remembered values. Both now behave the way you would expect.

More of your state survives

Hot reload is only useful while the screen in front of you is the one you were working on. If an edit clears the form you just filled in or sends a list back to the top, you end up recreating that state by hand, which is the work hot reload is supposed to save. On Android, 2.2.0 keeps a lot more of it:

The editBefore2.2.0
Add or remove a call next to typed text held in rememberSaveableText clearedText kept
Add or remove a call next to a selected tab held in rememberSaveableTab reset to its defaultTab kept
Remove a call inside a LazyColumn itemList scrolled to the top, detail pane closedList and pane stay where they were
A later save that rebuilds a listList could jump back to where it was during an earlier saveNo jump to an old position
Change a rememberSaveable value's typeApp crashedThat state starts over, app keeps running

The first two rows cover state you keep in rememberSaveable, which HotSwan can now carry across an edit on Android. Compose Desktop still starts that state over. A screen with no rememberSaveable of its own, such as a screen whose only saved state is the scroll position of its list, also still loses that position when an edit rebuilds the screen, as it did in 2.1.0.

Fewer false alarms, clearer failures

A save ends in one of three colors in the IDE: green when it applied, amber when part of it did not, and red when it failed. Those colors are only worth looking at if they are right. In 2.1.0, many saves that had fully applied still ended amber, and a few real failures took minutes to show up. Here is the difference:

The situationBefore2.2.0
A note that marked fully applied saves as partial, across five real appsOn 182 of 266 savesNo longer marks them partial
The first save after launching your app, for a one file editResent 303 unchanged classes on our sample app and ended amberSends 1 class, no warning
Saving while Android has frozen the app (a sleeping screen, say)Sent the whole app's code, failed after 265 sStops in 14 s, nothing sent, says what to do
A structural save after upgrading HotSwan, before running the app againWould push 3,290 classes and run the app out of memoryRefused before anything is applied, says to run the app once

The first two rows were noise: the edit was on screen, but the result said otherwise, and the suggested fix (rebuild and reinstall) could not help. The third was a real problem that surfaced late. Android freezes apps it considers idle, for example behind a sleeping screen, and a save sent to a frozen app used to send the whole app's code and fail more than four minutes later. Now HotSwan checks first and asks you to unlock the device and bring the app to the foreground.

The last row is new in 2.2.0, and it is the one you are most likely to meet. After upgrading HotSwan, the app still on your device was built by the old version, and the two versions do not compile your code the same way. Without this check, a structural save into that app would send thousands of classes (3,290 on one app) and run it out of memory partway through. Now the save is refused before anything is applied, with a message that tells you to run the app once. A few other messages were corrected along the way: HotSwan no longer says the app restarted when it still has to, or that every remembered value was kept when some were rebuilt.

Fewer crashes

A hot reload that crashes your app costs more than the build it was meant to skip. These used to crash the app, and they no longer do:

  • Changing a saved state's type, such as mutableStateOf("abc") to mutableIntStateOf(7) inside rememberSaveable. The state now starts over with its new type.
  • Changing top level values (a val outside any function) in a file whose code runs every frame, such as an animated background. On one app this crashed 1 attempt in 4.
  • Adding a launch { } above a running coroutine in a ViewModel.
  • Two functions with the same name in one class, such as a suspend overload next to a regular one. They no longer mix up their code, which could fail even before your first save.
  • Release builds of apps with an Android KMP library, which crashed at launch in 2.1.0. Those builds no longer carry HotSwan.
  • Android Studio's "Profile with low overhead", which crashed any HotSwan app at launch in 2.1.0. A profileable build now launches normally.

Before this release, we saved 343 times across seven real apps on Android. The one crash that turned up is fixed in 2.2.0. On the iOS simulator, 36 saves across three apps ran without a crash.

iOS and Compose Desktop

On the iOS simulator, an edited LaunchedEffect or DisposableEffect now runs after an instant reload, the fast path that skips the native build. Before, a save that changed only the effect's block reported success and left the old block running, so the change you made never happened. The effects next to it keep running untouched. A few effects run once more the first time their composable takes the instant path, and hotswanIosEffectGroups=false in gradle.properties turns the restart off.

2.1.0 also had an iOS build problem: the simulator build failed when a LazyColumn's content captured a value and one of its items declared var x by remember { ... }. That is fixed. If you added hotswanIosMemoEveryCapture=false to your gradle.properties to work around it, remove it: it is no longer needed, and it turns off parts of the instant path.

Compose Desktop gets the same Android speedups. On our sample app, with the new switches off and on, a value edit inside a list item no longer starts the visible items over, and the screen settles in 70 to 86 ms instead of 123 to 126 ms.

Upgrading to 2.2.0

Update the HotSwan version in your version catalog:

libs.versions.toml
1[plugins]
2hotswan-compiler = { id = "com.github.skydoves.compose.hotswan.compiler", version = "2.2.0" }

Then update the IDE plugin to 2.2.0 from the JetBrains Marketplace, and run your app once before your first save. That last step matters this time. 2.2.0 compiles your code differently from 2.1.0, so the app on your device has to be built by the new version before it can take a structural save. If you skip it, a structural save on Android or Compose Desktop is refused with a message to run the app, and nothing on your device changes. Value edits and the iOS simulator are not checked.

If your app depends on an Android KMP library module, install the debug app with Run or installDebug. A plain assemble now builds that library without HotSwan, which keeps release builds clean, and warns you about it.

The full list of changes is in the release notes. If something does not reload the way you expected, a small repro on the Compose HotSwan issue tracker is the fastest way to get it fixed.

If you have been avoiding structural edits because they were slow or reset your screen, this is the release to try them again. Change the code inside a composable on the screen you are working on and see how much of your session is still there afterwards.

Hot reload earns your trust one save at a time. The faster a save comes back, and the more often its result is right, the less you find yourself checking the device and the more you simply keep working.

As always, happy coding!

Jaewoong (skydoves)

Recommended Reading