0

I have the following functions:

fun validateEmail(email: String) = flowOf {
    when {
        email.isEmpty() -> throw EmailError.Empty
        email.contains("@") -> email
        else -> throw EmailError.NotValid
    }
}
fun validatePassword(password: String) = flowOf {
    when {
        password.isEmpty() -> throw PasswordError.Empty
        else -> password
    }
}

fun useCaseLogin(email: String, password: String) = flow {
    //response: MyResponse = make and http request
    emit(response)
}

Then i would like to know if is posible to run this flows in the following way:

fun login(email: String, password: String) = 
    validateEmail(email).then(validatePassword(password))
                        .then(useCaseLogin(email, password)) { response: MyResponse -> 
        if (response==200) {
           emit("Login success!!")
        } else {
           emit("Login failed")
        }
 }.catch { e: Throwable ->
  when (e) {
    is EmailError.NotValid -> {
        print(e.localizedMessage)
    }
    is EmailError.Empty -> {
        print(e.localizedMessage)
    }
    is PasswordError.Empty -> {
        print(e.localizedMessage)
    }
  }
}.asLiveData(viewModelScope.coroutineContext)

Is there a way to write something like this?

5
  • 1
    Why do the validation functions return Flows? Seems like added convolution with no benefit. Commented Jun 9, 2021 at 17:11
  • These look like they should just be suspend functions, not flows. Commented Jun 9, 2021 at 17:59
  • I don't think they should even be suspend functions, because they return instantly with trivial work. Commented Jun 9, 2021 at 19:59
  • @Tenfour04 yes is a good point. As the validation functions are processing trivial work, I agree, they shouldn't be a flow. How could I change it? Commented Jun 10, 2021 at 5:17
  • Following post might help. krossovochkin.com/posts/… Commented Dec 12, 2022 at 10:13

1 Answer 1

3

What you are looking for is the combine operator:

val emailFlow = validateEmail(email)
val pwdFlow = validatePassword(pwd)

emailFlow
    .combine(pwdFlow) { email, pass -> Pair(email, pass) }
    // In case your useCaseLogin is just a suspending function
    .map { (email, pass) ->
        val result = useCaseLogin(email, pass)
    }
    // In case your useCaseLogin returns a Flow with some result
    .flatMap { (email, pass) -> useCaseLogin(email, pass) }

While the comments are right and it's a little bit overkill to make the validation a Flow. You can build a nullable error Flow that you can use to map to the UI and display perhaps a little label below each field.

I would approach such thing as follows:

class YourViewModel() : ViewModel() {
     val emailFlow = MutableStateFlow("");
     val passwFlow = MutableStateFlow("");

     val emailError = emailFlow.map { validateEmail(it) }
     val passwError = emailFlow.map { validatePassword(it) }

     fun validateEmail(email: String) : Error? =
         when {
             email.isEmpty() -> throw EmailError.Empty
             email.contains("@") -> null
             else -> throw EmailError.NotValid
        }
// Similarly for password …
}

And on the OnChangeListener from your text inputs call viewModel.setEmailValue(value) so you get an updated emailError and passwError for each keystroke

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.