Sending Events From an MVVM View Model with Kotlin

Erik Browne
4 min readNov 25, 2017

--

In the Model-View-View Model (MVVM) architecture, the View Model generally presents some state to the View — the View Model has data, and the View displays it to the user. But sometimes the View Model needs to take some action, and the View is what actually executes the action. Examples are displaying a message (e.g. a dialog, toast, or snack bar) or navigating to a new View. How should a View Model communicate this to the View?

State of Mind

One way is to consider the actions to be just another state. For example, if a View Model wants to show a message, it sets a message property. The View then displays the message and calls a method on the View Model to tell it that the message was displayed. The View Model then resets the message property.

With this mechanism, the View Model’s state machine goes to the “show a message” state, then to the “message was shown” state. It makes the View Model’s state machine more complicated, and it takes extra effort on the View’s part to tell the View Model it needs to change state. If you really like state machines then feel free to use this, but it seems like there should be a better way

Message In a Bottle

Another way is to look at the actions as messages the View Model sends to the View — in this case, the object-oriented meaning of messages as methods called on an object. You define interfaces which the View (or an object owned by the View) implements, and which the View Model calls to send a message.

interface ViewMessages {
fun showMessage(message: String)
}

The View must pass the object which implements the interface to the View Model as a dependency. This can get tricky if the View Model can outlive the View. If the View Model keeps a reference to a View which has gone away, memory leaks (or worse) can ensue.

In the Event…

The actions can also be seen as events sent from the View Model. In this paradigm, the event is data in a View Model property that the View consumes. Once the View has consumed the data, the View Model automatically clears the property. For this you’ll need some kind of property which handles clearing after consumption. For example, Google created a SingleLiveEvent example extension to its Android Architecture Components (which it is considering adding to the components), a LiveData object which only lets its observer see the data once.

This works well for simple events, but what about more complex ones? To display a message you could use a string for the event data, but what if the View Model also wants to convey whether the message should be shown for a short time or a long time? Or what if the event is for an Android Activity to call startActivityForResult(), which requires an Intent and a request code? You’ll need to define data classes for each event to hold the multiple pieces of data the event requires.

Using a data class to represent an event isn’t that understandable. It’s like having just a function’s arguments, but not the name of the function.

You Got Your Message In My Event!

The pro for messages is that calling a method is easy to understand, but the con is that the View Model’s reference to the object must be managed. The pro for events is that the View Model doesn’t have a reference to manage, but the con is using data classes to convey that an action should be performed isn’t as intuitive as method calls. Can we combine them so we get all pros and no cons?

We can, using Kotlin’s function literals with receiver feature. This allows us to define a lambda which calls a method on a receiver object, and the lambda becomes the event data. The type of this lambda is T.() -> Unit, a function which takes no parameters and returns no value, and operates on an object of type T (the receiver). The type T is the interface which defines the method(s) the lambda can call.

Here is an example using an Android Activity as the View and Google’s SingleLiveEvent to hold the event. To make this easier we create LiveMessageEvent, a subclass of SingleLiveEvent that handles sending message events:

The setEventReceiver() method is called by the Activity to attach a receiver to the LiveMessageEvent. The Activity passes itself as the owner and the object which implements the event interface (which in our case will be the Activity) as receiver. The method adds an observer to the LiveData which calls the receiver when it receives an event.

We define our interfaces:

interface ViewMessages {
fun showMessage(message: String)
}
interface ViewNavigation {
fun startActivity(intent: Intent?)

fun startActivityForResult(intent: Intent?, requestCode: Int) }

The View Model defines properties to hold the events, one for each interface:

val messagesEvent = LiveMessageEvent<ViewMessages>()
val navigationEvent = LiveMessageEvent<ViewNavigation>()

The Activity implements the interfaces and adds event handlers to the properties:

viewModel.messagesEvent.setEventReceiver(this, this)
viewModel.navigationEvent.setEventReceiver(this, this)

To send an event, the View Model calls sendEvent() on the property:

messagesEvent.sendEvent { showMessage("This is a message") }
navigationEvent.sendEvent {
startActivityForResult(intent, REQUEST_CODE)
}

In Conclusion…

Using Kotlin function literals with receiver we can create events that are much more understandable. The reader can see exactly what the View Model is telling the View to do., and there is no opaque data class, just an easy-to-read method call.

--

--