Shared ViewModel with MVI architecture

125 Views Asked by At

I'm working on an Android application using Jetpack Compose and the MVVM/MVI architecture. I have a BaseViewModel class that all of my ViewModels extend. This class handles the associated State, Event, and Effect for each screen using generics. Here is the BaseViewModel class:

interface ViewEvent
interface ViewState 
interface ViewSideEffect

abstract class BaseViewModel<UiState: ViewState, Event: ViewEvent, Effect: ViewSideEffect>: ViewModel() {
    // Handling State
    private val initialState: UiState by lazy { setInitialState() }
    abstract fun setInitialState(): UiState 
    protected fun setState(reducer: UiState.() -> UiState) {
        val newState = viewState.value.reducer()
        _viewState.value = newState
    } 

    private val _viewState: MutableState<UiState> = mutableStateOf(initialState)
    val viewState: State<UiState> = _viewState

    // Handling Event 
    private val _event: MutableSharedFlow<Event> = MutableSharedFlow()

    abstract fun handleEvents(event: Event)

    init {
        subscribeToEvents()
    }

    private fun subscribeToEvents() {
        viewModelScope.launch {
            _event.collect { handleEvents(it) }
        }
    }

    fun setEvent(event: Event) {
        viewModelScope.launch {
            _event.emit(event)
        }
    }

    // Handling Effect 
    private val _effect: Channel<Effect> = Channel()

    val effect = _effect.receiveAsFlow()

    protected fun setEffect(builder: () -> Effect) { 
        val effectValue = builder()
        viewModelScope.launch {
            _effect.send(effectValue)
        }
    }
}

The challenge arises when I want to use a shared ViewModel across multiple screens. For instance, I have a main screen with four buttons, each navigating to a related screen where the user fills part of a form. I need to gather this data and send it to the server when the user taps the submit button on the main screen.

I attempted to create a CommonEffect, CommonEvent, and CommonState and passed these interfaces as generics to the CommonViewModel. However, this approach seems messy and leads to issues, especially with UI updates.

I'm looking for advice on whether using a shared ViewModel across these screens is a best practice. If so, how should I implement it effectively considering the challenges with generics and UI updates?

Thank you in advance.

0

There are 0 best solutions below