Skip to content

Instantly share code, notes, and snippets.

View RohitSurwase's full-sized avatar
💭
Slightly Busy

Rohit Surwase RohitSurwase

💭
Slightly Busy
View GitHub Profile
@RohitSurwase
RohitSurwase / MainViewEvent.kt
Created March 25, 2020 13:57
ViewEvent example
sealed class MainViewEvent {
data class NewsItemClicked(val newsItem: NewsItem) : MainViewEvent()
object FabClicked : MainViewEvent()
object OnSwipeRefresh : MainViewEvent()
object FetchNews : MainViewEvent()
}
@RohitSurwase
RohitSurwase / AacMviViewModelDH.kt
Created February 13, 2021 08:27
AacMviViewModel for Activities with DataHolder
open class AacMviViewModelDH<STATE, EFFECT, EVENT>(application: Application, private val aacMviDH: AacMviDH<STATE, EFFECT>) :
AndroidViewModel(application), ViewModelContract<EVENT> {
fun viewStates(): LiveData<STATE> = aacMviDH.stateLiveData
fun viewEffects(): LiveData<EFFECT> = aacMviDH.effectLiveData
@CallSuper
override fun process(viewEvent: EVENT) {
Log.d(TAG, "processing viewEvent : $viewEvent")
@RohitSurwase
RohitSurwase / AacMviDH.kt
Created February 13, 2021 08:32
Parent DataHolder for easy implementation of DataHolder for Activities.
open class AacMviDH<STATE, EFFECT> {
private val _states: MutableLiveData<STATE> = MutableLiveData()
val stateLiveData: LiveData<STATE>
get() = _states
private var _state: STATE? = null
var state: STATE
get() = _state
?: throw UninitializedPropertyAccessException("\"state\" was queried before being initialized")
@RohitSurwase
RohitSurwase / gist:f715dc42beb6f43ca473bff815aea642
Created March 31, 2026 17:54
Evolving Android MVI: Kotlin Flows, Compose, and Lifecycle Safety 🚀
Prerequisites: This builds on our foundation: Best Architecture For Android : MVI + LiveData + ViewModel = ❤️. The core concepts of Unidirectional Data Flow, ViewState, ViewEffect, and ViewEvent remain intact.
The original architecture served us well, but the Android ecosystem has shifted. LiveData is tied to the Android SDK and struggles with one-shot events. SingleLiveEvent was a hack. Jetpack Compose completely changed the UI rendering contract.
Here is how we evolved our MVI base to use Kotlin Flows, support Jetpack Compose, and guarantee lifecycle safety.
1. State: LiveData ➡️ StateFlow
LiveData binds lifecycle-awareness into the ViewModel. StateFlow pushes that responsibility to the UI layer, keeping the ViewModel pure Kotlin. It always holds a value and replays it instantly to new collectors, making it the perfect replacement for ViewState.
// In AacMviViewModel.kt
private val _viewStates: MutableStateFlow<STATE> by lazy { MutableStateFlow(viewState) }
override val viewStates: StateFlow<STATE> get()
@RohitSurwase
RohitSurwase / gist:0a2c18c115a15c4de1c5145d476aa9c0
Created March 31, 2026 18:06
Scaling Android MVI: Taming Fat ViewModels with DelegateVM 🧩
Prerequisites: This builds on our modernised MVI foundation: Evolving Android MVI: Kotlin Flows, Compose, and Lifecycle Safety.
MVI keeps screen state predictable, but it creates a new problem: Fat ViewModels.
If your app has a complex "Feature Carousel" component used on the Home Screen, the Search Screen, and the Profile Screen, duplicating the loading, error handling, and state logic across three ViewModels violates DRY. You cannot use inheritance because a ViewModel can only extend one base class, and a carousel is just one piece of a screen.
We need Composition. We need to extract the MVI logic of that component into a reusable unit that any ViewModel can plug in.
Introducing AacMviDelegateVM
A DelegateVM is a plain class (not an AndroidViewModel) that encapsulates a full MVI loop (ViewState, ViewEffect, ViewEvent) for a specific, reusable UI component.
It shares code, not state. Each Host ViewModel instantiates its own unique instance of the Delegate. The Host ViewModel funnels the Delegate's state and
@RohitSurwase
RohitSurwase / gist:5e61638c8c29821cf8f0380331c27b81
Created March 31, 2026 18:23
Replacing LocalBroadcastManager in Android: SharedFlow + Sealed Classes = ❤️
---
Replacing LocalBroadcastManager in Android: SharedFlow + Sealed Classes = ❤️
A modern, lifecycle-safe, type-driven in-app event bus using Kotlin Coroutines and SharedFlow - demonstrated with a News Reader app.
TL;DR - If you are already familiar with Kotlin Coroutines, SharedFlow, and sealed classes, skip the basics and jump straight to the LocalBroadcast + BroadcastEvent section.
Preface
Every non-trivial Android app eventually runs into the same problem: how do you communicate between two completely unrelated parts of your app - a background sync service and a fragment, a repository and a ViewModel - without coupling them together?
For years, the go-to answer was LocalBroadcastManager. It was simple. It worked. And then Google deprecated it.
"LocalBroadcastManager is an application-wide event bus and embraces layer violations in your app; any component may listen to events from any other component… Use other means of communicating between components."
 - Android Developer Documentation