androidkotlinandroid-jetpack-composerepositoryviewmodel

How do I handle a function that is calling a repository and also being used in two separate ViewModels


I have two ViewModels that contain the same function. The function calls an interface/repository to retrieve data from a database, and the function then manipulates the data.

It is my understanding that the repository should only handle the call to the database and the ViewModels should never interact with each other. I also do not want this function residing in both ViewModels since that would have an unnecessary additional call to the database and it just seems to be redundant code to me.

Therefore, what is the best practice to have this function only written once and be accessible to both ViewModels?

Repository:

class PartsScreenImpl : PartsScreenRepository {
    private val log = logging("KMLogging Tag")
    private val db = Firebase.firestore
    private val storage = Firebase.storage

    override suspend fun getPartsList(): QuerySnapshot {
        val result = db.collection("parts").get()
        val doc = result.documents
        return result
    }
}

Function in question

private fun getPartsList(): List<Part>? {
    val result = runBlocking {
        val querySnapshot = async {
            return@async repository.getPartsList()
        }.await()
        return@runBlocking querySnapshot
    }

    val documents = result.documents

    var partsList: MutableList<Part>? = null

    for (document in documents) {
        val partNumber = document.get<String>("part_number")
        val partDesc = document.get<String>("part_desc")

        val partImage = getImageUrl(partNumber)

        val part = Part(partNumber, partDesc, partImage)

        if (partsList == null) {
            partsList = mutableListOf(part)
        } else {
            partsList.add(part)
        }
    }
    return partsList
}

Code that calls the function

fun getOrderSummary(order: List<OrderReportData>) : List<OrderPart> {
    val partsList = getPartsList()

    val orderSummary = mutableListOf<OrderPart>()

    for (part in partsList!!) {

        val orderCount = order.filter { it.orderPart.number == part.number }


        var orderPartQuantityList: List<Double> = emptyList()

        try {
            orderPartQuantityList = orderCount.map { it.orderPart.qty.toDouble() }

        } catch (e: NumberFormatException) {
            Log.e("mytags", "Invalid number format", e)
        }

        val partQuantityDouble = (orderPartQuantityList.sum())

        val partQuantity = if (partQuantityDouble.rem(1).equals(0.0)) {
            partQuantityDouble.toInt()
            } else {
                partQuantityDouble.toBigDecimal().setScale(1, RoundingMode.FLOOR).toDouble()
        }

        val orderPart = OrderPart(part.number, part.desc, partQuantity.toString())

        if (orderPart.qty.toFloat() > 0) {
            orderSummary += orderPart
        }
    }
    return orderSummary
}

Solution

  • I think what you're looking for is the Domain Layer

    From the first snippet:

    The domain layer is responsible for encapsulating complex business logic, or simple business logic that is reused by multiple ViewModels. This layer is optional because not all apps will have these requirements. You should only use it when needed-for example, to handle complexity or favor reusability.

    Edit: For example (I don't have an IDE currently and i'm not sure if you can return an optional) :

    class PartListUseCase(repository: PartsScreenRepository) {
    
        operator fun invoke() : List<Part>? {
            return getPartsList()
        }
    
        private fun getPartsList() : List<Part>? {
            val result = runBlocking {
                val querySnapshot = async {
                    return@async repository.getPartsList()
                }.await()
                return@runBlocking querySnapshot
            }
    
            val documents = result.documents
    
            var partsList: MutableList<Part>? = null
    
            for (document in documents) {
                val partNumber = document.get<String>("part_number")
                val partDesc = document.get<String>("part_desc")
    
                val partImage = getImageUrl(partNumber)
    
                val part = Part(partNumber, partDesc, partImage)
    
                if (partsList == null) {
                    partsList = mutableListOf(part)
                } else {
                    partsList.add(part)
                }
            }
            return partsList
        }
    
    }