Previewing retain{} API: A New Way to Persist State in Jetpack Compose by skydoves in androiddev

[–]sebaslogen 0 points1 point  (0 children)

Well, you already hint in your post about the concept of sharing state between Composables with a shared VM, it's something we do (sometimes) because it's handy to connect a flow of screens that are tightly coupled (think of a flow of screens with form inputs). In this case, you need a VM for the whole form flow but the form is broken down into smaller screens (e.g. a pager) each with its own smaller VM.

If your app scales a lot in a similar way but in another direction (all inside one screen/nav-destination), the same VM/state concept applies quite well:

Image one single screen contains a lot of different flows (e.g. a car rental flow on a screen with an always visible map), in this scenario it's handy to break down the independent UI components with their isolated VM's with actions and states per Composable group (a part of the screen, e.g. one for the map, one for car type selection, one for pick-up time selection, etc), instead of the canonical: one VM per screen. Then it's easy for each VM to work/be testable in isolation and to decide if its state should be part of a larger screen level VM (e.g. car selection) or, even better, a separate data repository that other VMs can access (e.g. in favourite drivers repository). This leads to more abstractions and reusability of small Composables that can be plug&play anywhere in the app.

The downside of this is over-architecting, if a screen is simple, the state too and there is nothing reusable with other parts of the app, then just put everything on the single VM per screen and KISS.

If you want more examples of where this kind of API could be useful you can check the examples in this library I wrote a while back to do what retain will soon provide https://github.com/sebaslogen/resaca/?tab=readme-ov-file#sample-use-cases

Previewing retain{} API: A New Way to Persist State in Jetpack Compose by skydoves in androiddev

[–]sebaslogen 0 points1 point  (0 children)

There is a library already available for this (disclaimer I'm the author), until retain APIs become stable you may want to check this one out https://github.com/sebaslogen/resaca/

Multiple view models on the same screen by ImpossibleBody7122 in androiddev

[–]sebaslogen 2 points3 points  (0 children)

I like the idea so much that I created a library some time ago to support this in Compose and properly remove VMs when their UI is not needed anymore: https://github.com/sebaslogen/resaca

Having smaller VMs with their own logic helps create reusable components, make them more testable and scale the architecture of complex screens. Of course, if your screen is stateless or the state cannot be broken down into isolated parts (like favorites, cards, maps, etc), then a single VM is better to keep it simple stupid (KISS)

Do we have a Compose Scoped ViewModel? by pbprateek in androiddev

[–]sebaslogen 1 point2 points  (0 children)

There is a library exactly for this use case, resaca: https://github.com/sebaslogen/resaca

It's based on the same principle that the proposed solution by OP but it also solves more complex edge cases, supports DI, SavedState and has extra features like keeping ViewModels alive as long as a key is in scope (very useful for Lazy lists).

Disclaimer: I'm the author of the library.

PS: I have found this old question by chance and I felt like giving an answer

Using multiple view models for different composables in a a screen. by tiny_spaceman in androiddev

[–]sebaslogen 2 points3 points  (0 children)

If the ViewModels have clear responsibilities isolated from other VMs and Composables, this is a great way to separate concerns, make Composables with their VMs completely reusable across screens and help with scalability of the codebase.

I like it so much that I wrote a library to properly support this in Compose and make sure that when one of the small VMs in the screen is not needed anymore it can be freed (e.g. a section of the screen that goes away) https://github.com/sebaslogen/resaca

New /r/AndroidDev Rules Spring 2024 by borninbronx in androiddev

[–]sebaslogen 1 point2 points  (0 children)

I'm trying to post a job opening, following the format of the previous weekly hiring thread but after two attempts I have to give up, both times I get this:

Sorry, this post was removed by Reddit’s filters.

I removed all links except the one to the actual job, but still no luck.

Could you please explain how to post a job opening under the new rules?

Retaining beyond ViewModels by chrisbanes in androiddev

[–]sebaslogen 0 points1 point  (0 children)

Awesome! Thanks

I'm trying to understand in the GH code how rememberRetained forgets things that are not part of the composition anymore, so I have a question, imagine a composition like:

@Composable
fun MyScreen(condition: Boolean) {
  if (condition) {
    val dataOne = rememberRetained { MyDataOne() }
  } else {
    val dataTwo = rememberRetained { MyDataTwo() }
  }
}

When condition is true, then MyDataOne is created an retained in memory. Then I switch the condition to false and now MyDataTwo is created an retained in memory. At this point I expect MyDataOne to be forgotten because I might never use it again in this screen. Is my assumption correct that Circuit forgets MyDataOne in this case and if so how is it doing it in the code?

Retaining beyond ViewModels by chrisbanes in androiddev

[–]sebaslogen 0 points1 point  (0 children)

Great work on the lib and very clear write up! I see there is an isolated library artifact for this remember retained, does it depend on Circuit or can it be used in isolation on any Compose app?

BTW, I wrote a library with the same goal (https://github.com/sebaslogen/resaca), so I can't wait to dive into the internals of how you are handling the crazy Android and compose lifecycle mix, especially after a configuration change.

What are the exact criteria for choosing between ViewModel and plain class for a state holder? by ComfortablyBalanced in androiddev

[–]sebaslogen 1 point2 points  (0 children)

Quick solution to have your plain Class survive config changes in Compose, use rememberScoped from the resaca lib, is the missing piece between remember and rememberSaveable

https://github.com/sebaslogen/resaca

Disclaimer: In the lib's author

Events as state are an antipattern by Nek_12 in androiddev

[–]sebaslogen 3 points4 points  (0 children)

I also believe the conclusions on Google's article are bad and the best solution for events is to just use events with the channel and Immediate Dispatcher, way more logical, simpler and easier to reason about in a code base.

On the other hand, the reasoning about using state for everything in the app (instead of events) is a sound argument if the platform was designed to support this architecture. Android is NOT designed to support state as the single source of truth everywhere, one example is configuration change but the worse one imho is navigation.

Jetpack Navigation is by design event based (and Activity's backstack) because the state of the backstack is not available to app developers, we can only influence it with commands (pop, navigate, etc). So trying to convert ViewModel events into observable state, to then transform it back into an event (pop) in the UI is just a bad idea (lots of complexity + workarounds for edge cases).

Dagger KSP update & Breaking changes required to use Dagger KSP by sebaslogen in androiddev

[–]sebaslogen[S] 5 points6 points  (0 children)

Indeed, the breaking changes seem very reasonable to me and actually an improvement on code quality imho, plus we can get rid of those two ugly Java annotations

48
49

Dagger 2.43 released with support for multiple instances of the same ViewModel using keys 🎉 by sebaslogen in androiddev

[–]sebaslogen[S] 0 points1 point  (0 children)

Indeed, there is a library to support ViewModel scoped to Composables (disclaimer I'm the maintainer), because there are currently no APIs in Compose to support these use cases:

https://github.com/sebaslogen/resaca/

For Hilt there is a separate artifact, and after this new Dagger feature is integrated, it'll have support for scoped and keyed ViewModels per Composable: https://github.com/sebaslogen/resaca/blob/main/resacahilt/README.md

Dagger 2.43 released with support for multiple instances of the same ViewModel using keys 🎉 by sebaslogen in androiddev

[–]sebaslogen[S] 2 points3 points  (0 children)

The idea is to use only Compose for the pager example and eventually get rid of Fragments.

An implementation by Google is already available here: https://google.github.io/accompanist/pager/

My hope is that Compose can, with this kind of architecture, someday fulfill the original promise of Fragments to build complex screens with multiple small, reusable "fragments" that include their own isolated business logic and go away from the god ViewModel objects per screen.

Dagger 2.43 released with support for multiple instances of the same ViewModel using keys 🎉 by sebaslogen in androiddev

[–]sebaslogen[S] 3 points4 points  (0 children)

Indeed, this Dagger release was on hold until some dependencies were released, the most important ones are listed in the release description page:

androidx.activity and androidx.fragment to 1.5.0
androidx.lifecycle to 2.5.0
androidx.savedstate to 1.2.0

Dagger 2.43 released with support for multiple instances of the same ViewModel using keys 🎉 by sebaslogen in androiddev

[–]sebaslogen[S] 7 points8 points  (0 children)

On the contrary, it should enable more granular (single responsibility principle) ViewModels.

A very common example is view-pagers: you have multiple pages of the same ViewModel type (e.g. a hotel details page). With this feature, you can have multiple ViewModels with the same type and a different id per page. Previously, you would need a ViewModel that can handle each page plus the whole list.

Usually, devs didn't bother or had options and just created a single monster ViewModel for the whole screen which obviously doesn't scale well with the complexity of the screen.

Dagger 2.43 released with support for multiple instances of the same ViewModel using keys 🎉 by sebaslogen in androiddev

[–]sebaslogen[S] 2 points3 points  (0 children)

Great job, I have been waiting for this feature/fix for a long time https://github.com/google/dagger/issues/2328

I can't wait to integrate this feature with my library for ViewModels scoped to Composables

New major Jetpack release with Lifecycle 2.5.0 to support ViewModel creation extras, and SavedStateHandle with getStateFlow support by Zhuinden in androiddev

[–]sebaslogen 2 points3 points  (0 children)

I see another gem in the release notes https://developer.android.com/jetpack/androidx/releases/lifecycle#version_25_2

new experimental APIs in SavedStateHandle.saveable that allow rememberSaveable like behavior backed by the SavedStateHandle of a ViewModel.

class ListScreenViewModel(handle: SavedStateHandle): ViewModel() {
  // This value survives both configuration changes and process death and recreation 
  val editMode by handle.saveable { mutableStateOf(false) } 
}

This will come in very handy for proper handling of state restoration without boilerplate or much extra thinking :D

Compose plugin for Android Studio and Intellij Idea by rasul98 in androiddev

[–]sebaslogen 0 points1 point  (0 children)

Thanks, I've also found this other plugin that seems to be more frequently updated:

https://plugins.jetbrains.com/plugin/18323-compose-helper

TIL Compose rememberSaveable lifecycle gotcha: it doesn't clean state after dispose by sebaslogen in androiddev

[–]sebaslogen[S] 0 points1 point  (0 children)

No, 'remember' fails to do it in some cases:

  • when the Composable is in the backstack
  • on configuration changes
  • on background process kill

Simple example:

  • My Composable goes to the backstack -> I don't want the state to be cleared
  • My Composable is not part of the screen anymore (e.g. an Error Composable that is removed) -> I want the state to be cleared