android10 / Android-CleanArchitecture-Kotlin

This is a movies sample app in Kotlin, which is part of a serie of blog posts I have written about architecting android application using different approaches.
https://fernandocejas.com/2018/05/07/architecting-android-reloaded/
4.69k stars 933 forks source link

Clean architecture of input validation #44

Open stanbar opened 6 years ago

stanbar commented 6 years ago

Hello. The example here covers only fetching immutable data. But for me, the most challenging is creating and updating existing data. How to handle validation of mutable data, when there is high coupling between View and ViewModel. Should I keep track of each field separately ?

    ...
    val id: MutableLiveData<Int> = MutableLiveData()
    val title: MutableLiveData<String> = MutableLiveData()
    val titleError: MutableLiveData<String> = MutableLiveData()
    val year: MutableLiveData<Int> = MutableLiveData()
    val yearError: MutableLiveData<String> = MutableLiveData()
    ...

and then how to validate ?

    fun createMovie() {
        var valid = true

        val title = title.value
        if (title == null || title.isBlank()) {
            titleError.value = "Title can not be blank"
            valid = false
        }

        val year = year.value
        if (year == null || year < 0) {
            yearError.value = "Year must be positive number"
            valid = false
        }
        ...

        if(valid){
            val newMovie = MovieDetails(
                    id = UUID.randomUUID().toString().hashCode(),
                    title = title!!,
                    year = year!!,
                    ...)

            createMovieDetails.execute({ it.either(::handleFailure, ::handleMovieDetails) }, Params(newMovie))

        }
    }

And then observe on each error ?

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        with(movieDetailsViewModel) {
            observe(titleError, ::handleTitleError)
            observe(yearError, ::handleYearError)
        }
    }

    private fun handleTitleError(erorrMessage: String?) {
        editTextTitle.error = erorrMessage
    }
    private fun handleYearError(errorMessage: String?) {
        Toast.makeText(context,errorMessage,Toast.LENGTH_SHORT).show()
    }

I'm not sure of any of those lines And how to update title in ViewModel ? Via TextWatcher ? It becomes quite tricky, when it comes to update TextView from ViewModel's Observable and updating ViewModel from TextView's TextWatcher (recursive updates).

An example of creating new and updating existing movies would be great 👍

AppGrade-D commented 6 years ago

Hello, there is a common problem in Android development :) In Clean Architecture we does'n have a reference to View in ViewModel, so in my opinion we have to notify ViewModel from View about any text changes. Try to create some InputModel class that has all inputs state with error flag and expose LiveData from ViewModel that is observed by View . After each input change in View, notify ViewModel by calling it's public model (ex. fun nameChange(text: String)), and ViewModel based on actuall value publish new event on LiveData that is consumed by View.

In this scenarion you always have actuall value stored in LiveData and do not have strong reference to View from ViewModel, all communication is done by LiveData.

This is only my proposed solution, please do not get it as the only right choice :)