Skip to content
Kaushik Gopal edited this page Dec 12, 2024 · 8 revisions
  • if you have questions on how something would work the USF way, raise an issue
  • i'll try to add more questions

I've broken this FAQ into two sections:

  1. How to do X
  2. Why do we Y
  3. Misc

How to do X

How do I subscribe to the ViewState & Effects when when?

The hardest problems for an android developer eventually always boil down to the lifecycle.

How do I setup a hot flow or live update stream of ui updates without leaking memory?

This question very similar to the next one but the question boils down to - "if i subscribe to a USF pipeline that internally sends a hot flow, will i leak memory?"

How do I reuse my ViewModel functionality to share behavior?

You don't. You might be tempted to use multiple VMs for the same screen, this is not a good idea as there can be view-state update collisions from different VM.

How do I prevent my VM from becoming huge for complicated screens?

If you add a feature, or make a changes to an existing screen using the USF pattern, you're typically only working with one "strand" in the USF pipeline thread. So even if your class is big, the scope of changes you make is isolated. So personally, I don't have any qualms with the ViewModel going big, since most people would be dealing (typically) with different strands of the USFM thread.

That being said, you should leverage the UseCase pattern to reuse functionality that might otherwise make your eventToResult functions long.

How do i leverage UseCases?

Why do we Y

Why do we split UiState & Effect into two streams?

Google came out with this article stating explicitly that one-off event(s) are anti-patterns. I disagree.

  1. The alternative to achieving this one-time side effect behavior is tracking the "state" of these effects in the UI state directly via Boolean values. That feels like a very ham-fisted way of dealing with the UI.
  2. This recommendation predates many of the SideEffect apis that Compose has since introduced.

But let's dive in deeper: The tldr of that Google post is - when the producer outlives the consumer, these APIs don’t guarantee the delivery and processing of the events. Therefore, handle the event immediately in the VM and reduce it to state which is exposed using an observable data holder type (implying use a similar mechanism to how you manage the view state).

But that feels like a band-aid to the original problem which is guarantying the delivery and processing of events. We have constructs like Channels that have interesting properties allowing you to guarantee delivery for a subscriber, and queue/buffer up events before a subscriber shows up.

This reddit thread (strong language aside) captures a lot of how I think around this specific subject.

I also have a suite of unit tests covering many of these corner-case scenarios.

The other advantage we get by splitting is that a Result object doesn't need to forcefully emit a UiState or Effect. We might selectively need to emit one or both.

Why do I need the additional Result object?

It feels like an unnecessary intermediate abstraction. Why not try to transform directly into UiState & Effects ?

  1. similar to the previous point having a Result object allows us to multicast the Event → Result stream (bulk of the work) and then selectively emit just a UiState or Effect (or both). You cannot achieve this without that abstraction
  2. Event → Result also allows general reuse through UseCases

Anecdotally, I did try with my iOS team initially who felt it added complexity. Eventually we reverted back to having a Result object.

Why don't we use some kind of LCE (loading, content, error) pattern?

It's up to you. My personal preference is not to have this but rather capture that more directly in the ViewState class.

Why do we need LocalState?

requires separate page.

Misc

Is this architecture used in production apps?

  • point to examples
  • Sample Android app
  • Sample iOS app