I had the time over the summer to make some experi...
# feed
j
I had the time over the summer to make some experimentation with Compose and thought it might interest some other people. So here’s a short article summarising it up https://medium.com/entur/compose-for-app-architecture-a2ebeb41ba72
d
Isn't there a downside to not using ViewModels? They were also made to survive after rotations and they are kept on the navigation stack to be able to return to a certain state... with only a Presenter the state gets reset.
j
It should not, since the composable is not recomposed, the remember/LaunchedEffect functions make it possible. I did try with changing the screen orientation, changing from light to dark theme, putting the app in the background and reopening it. It worked every time, the app did not recreate the state from scratch and kept it’s previous state
🤔 1
d
I'm not so sure it's that simple... but 🤷🏼‍♂️ You also didn't cover testing (although that might not be the point of the article, it IS a concern for such new patterns), and compose previews (which I guess is the reason why you used a fun interface for the presenter... but it might be nice to show how and that that was your intention).
j
Yes those themes could be covered in a second article, and I’ll try elaborate on the point you mentioned. Thanks!
d
The whole SavedStateHandle api and navigation component's backstack state would be superfluous according to your assumptions... but I'm really not convinced that Presenters cover all their use-cases...
j
What I can say now regarding testing is that it’s quite simple. The presenter can be tested by faking the input flow of events with a simple
flowOf
and make sure the correct outputs are generated.
SavedStateHandle does persists data when the app is killed. That will not be done by the presenters in this article, true. But one can always create it’s own
produceSavebleState
using a
rememberSaveble
function to accomplish something similar
d
It's an interesting approach, and I had the same concern about Circuit when I first saw it, but I'm really in doubt if I wouldn't combine a ViewModel to this Presenter somehow to get all the features. Even though produceSaveableState could cover part of the use-cases, it still wouldn't cover nav's backstack state scoping.
j
True, for the navigation backstack, a custom solution is probably necessary
d
It might be nice to mention these downsides in the article, since right now it seems like this is the better than ViewModels and that all the use-cases are covered... which doesn't seem 100% true...
👍🏻 1
y
It looks like 'ViewModel' and Presenter styles can coexist.
Copy code
class Presenter : ViewModel() {
  var value1 by mutableStateOf(false)

  @Composable
  fun uiState(events: Flow<Event>): UiState {
    var value2 by remember { mutableStateOf(false) }
    LaunchedEffect(events) {
      events.collect { event ->
        when(event) {
          Event.Load -> {
            withContext(viewModelScope.
coroutineContext) {
              launch {
                value1 = true
                value2 = true
              }
            }
          }
        }
      }
    return UiState(value1, value2)
  }
}

@Composable
fun Screen() {
  val eventBus = remember { EventBus<Event> }
  val presenter = viewModel { Presenter() }
  val uiState = presenter.uiState(eventBus.events)
  Button(onclick = { eventBus.produceEvent(Event.Load) } {
    Text("Click here")
  }
}
In this case, value1 is stored in 'ViewModel' and value2 is stored in Composable's 'remember'.
This ensures that value1 will survive recompose such as screen rotation.
j
I tested screen rotations and theme changes. The data was persisted without the use of a view model
y
Yes, it is true that we can use rememberSaveable to save state without a ViewModel. But, it is easier to save in the ViewModel, because rememberSaveable requires you to define a Saver, which makes it difficult to use in custom classes. ViewModel is ideal for saving state that can withstand recomposition.
j
I did not need to use a rememberSaveable,
produceState
is build on top of
remember
and
LaunchedEffect
. That seems to be enough to survive screen orientation and theme changes.
I was completely wrong on this. It worked in my project because I had
android:configChanges="orientation|...
in the manifest file 🤦🏻 I did create a sample repo to study the subject without distraction of an actual app https://github.com/jeantuffier/CounterTest It’s quite clear there that the counter is reset after a screen orientation. But I did find https://github.com/takahirom/Rin that basically extracted the
rememberRetained
from Circuit to solve that particular issue.