Is it okay for a ViewModel's LiveData to include functions for its respective views to invoke?

Viewed 303

Similar to iamjonfry's approach here...

Simple Example: Along with ui content (ie. title), my data class (MyUIState) has onClicked properties. When MyFragment with, say, a RecyclerView of buttons submits the myViewsData to a ListAdapter, the adapter sets the OnClickListener of each item to invoke the respective onClicked function in the data class (which originally came from the ViewModel)

// MyViewModel.kt - Note: It does not reference anything in Activity/Fragment/View layer

// Data Class representing State that will map to the View Layer
data class MyState(val myItems: List<Item>) {

    /* Other properties go here. Not necessary for sake of example */

    // Item will map to a button in the View Layer
    data class Item(
        val title: String,
        val onClicked: (() -> Unit)? // I want to know if this is okay to do
    )
}

//LiveData for Activity/Fragment/View to observe
val myStateLiveData: MutableLiveData<MyState> = MutableLiveData()

fun refreshState() {
    myStateLiveData.value = MyState(
        getRawData().map {
            MyState.Item(
                title = it.title,
                // Note the lambda being passed here...
                onClicked = { /* do stuff in here */ }
            )
        }
    )
}

Main Question: Is it a reasonable approach for a ViewModel's LiveData to include function types for its respective views to invoke (for example, to pass an onClick handler to a View)? If not, what's the proper standard?

1 Answers

Yes, this will leak your UI components. Observers of LiveData are automatically disconnected when they go out of scope, so they are not leaked. But if the LiveData itself stores an indirect reference to a lambda that captures anything in an Activity or Fragment, that Activity or Fragment will be leaked. There's no mechanism to automatically drop the reference in your code. You theoretically could use WeakReferences, but that results in convoluted code, and you're violating MVVM anyway by passing UI references to a ViewModel.

Related