kotlinkotlin-coroutinesandroid-livedataandroid-mvvmmediatorlivedata

How to manipulate a specific set of data from Live or Flow Data Kotlin MVVM?


I have three Room entities. An entity Person includes id and name, entity Holiday includes id, personId, onHoliday and Sickness entity includes id, personId and onSickness.

Also, I have a POJO Entity called ScreenPOJO that includes viewTypeId and personName.

My goal is to get the person's name (from Person) and viewTypeId that I can observe in Activity.

The viewTypeId is an Integer that depends on the onHoliday - true/false and onSickness true/false. So let's say when the onHoliday is false and onSickness is false the viewTypeId = 1, when onHoliday is true then viewTypeId = 2 and when onSickness is true then viewTypeId = 2.

In this example, it is achievable by creating a query and returning the result by using the POJO Entity.

Sometimes the query can be too complex and for that reason, I would like to somehow merge all three Live/Flow data together using the person_id. I read that I can use MediatorLiveData, however, I do not have experience yet to put all the data together and return only the result that I need (person name and viewTypeId).

Person Entity:

@Entity(tableName = "person_table")
data class Person(
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id") val id: Int,

    @SerializedName("name")
    @ColumnInfo(name = "name") val name: String,
    )

Sickness Entity:

@Entity(tableName = "sickness_table")
data class Sickness(
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id") val id: Int,

    @SerializedName("person_id")
    @ColumnInfo(name = "person_id") val personId: Int,

    @SerializedName("on_sickness")
    @ColumnInfo(name = "on_sickness") val onSickness: Boolean
    )

Holiday Entity:

@Entity(tableName = "holiday_table")
data class Holiday(
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id") val id: Int,

    @SerializedName("person_id")
    @ColumnInfo(name = "person_id") val personId: Int,

    @SerializedName("on_holiday")
    @ColumnInfo(name = "on_holiday") val onHoliday: Boolean
    )

ScreenPOJO Entity:

data class ScreenPOJO(
    val viewTypeId: Int,
    val personName: String
)

ViewModel, the data are originally as Flow :

val allPersons: LiveData<List<Person>> = repository.allPersons.asLiveData()

val allHolidays: LiveData<List<Holiday>> = repository.allHolidays.asLiveData()

val allSickness: LiveData<List<Sickness>> = repository.allSickness.asLiveData()

ViewModel, example of insert Person:

private val _insertPersonStatus = MutableLiveData<ViewModelStatus>()
val insertPersonStatus: LiveData<ViewModelStatus> = _insertPersonStatus


fun insertPerson(person: Person) = viewModelScope.launch {
    try {

        val insertedRowId = repository.insertPerson(person)

        if (insertedRowId > -1) {
            _insertPersonStatus.postValue(ViewModelStatus.SUCCESS(insertedRowId,ViewModelStatus.SUCCESS.Type.VM_INSERT))
  

        } else {
            _insertPersonStatus.value = ViewModelStatus.FAIL("Fail",ViewModelStatus.FAIL.Type.VM_INSERT)
  
        }

    } catch (ex: Exception) {
        _insertPersonStatus.value = ViewModelStatus.EXCEPTION(ex.localizedMessage.toString(),ViewModelStatus.EXCEPTION.Type.VM_INSERT)
     

    } 
}

Solution

  • I think it is better to modify the data layer in a way, which will allow you to write less code in the business-logic and view layers, therefore the code will be simpler and cleaner which is always a good sign.

    Therefore, instead of merging LiveData you should think about redesigning Person data class.

    Since I don't see much sense in storing holidays and sickness separately from persons, I assume you should somehow store holiday and sickness related data in the person object. I suggest something like this (I will omit room annotations for simplicity):

    data class Person(
        val id: Long,
        val name: String,
        val onHoliday: Boolean,
        val onSickness: Boolean   
    )
    

    In case when the person has multiple holidays or sickness days, you can do something like this (which is kinda common practice nowadays):

    data class Person(
        val id: Long,
        val name: String,
        val holidays: List<Holiday>,
        val sicknessDays: List<Sickness>   
    )
    

    I am not sure that it is easy to insert a collection in Room, but there are workarounds here. Maybe you will also need @Embedded annotation to keep the POJO's inside of the POJO