I want to implement Clean Architecture in my recent app. But I faced some trouble of error handling in useCase. I can't handle exception thrown from Usecase class although other library based exceptions were successfully handled. It means, when any exception is thrown by Firebase, it is successfully handled and show the exception text in Toast. But when I throw any exception from Usecase class like
Please fill both field
or
Please give email
or
Please give password
then app crashed. So how can I handled the exception of use case class and show the text in Toast?
Here is code I tried.
FirebaseAuthRepository.kt
interface FirebaseAuthRepository {
suspend fun register(email: String, password: String): Flow<User>
}
FirebaseAuthRepositoryImpl.kt
class FirebaseAuthRepositoryImpl @Inject constructor(
private val firebaseAuthDataSource: FirebaseAuthDataSource,
private val firebaseAuthMapper: FirebaseAuthMapper
) : FirebaseAuthRepository {
override suspend fun register(email: String, password: String): Flow<User> {
return flow {
emit(firebaseAuthDataSource.register(email, password))
}.map {
firebaseAuthMapper.toUser(it)
}
}
}
FirebaseAuthDataSource.kt
class FirebaseAuthDataSource @Inject constructor(private val firebaseAuth: FirebaseAuth) {
suspend fun register(email: String, password: String): FirebaseUser? {
val authResult: AuthResult = firebaseAuth.createUserWithEmailAndPassword(email, password).await()
return authResult.user
}
}
RegisterUseCase.kt
class RegisterUseCase @Inject constructor(
private val firebaseAuthRepository: FirebaseAuthRepository
) {
suspend fun execute(email: String, password: String): Flow<User> {
if (email.trim().isEmpty() && password.trim().isEmpty()) {
throw Exception("Please fill both field")
} else {
if (email.trim().isEmpty()) throw Exception("Please give email")
if (password.trim().isEmpty()) throw Exception("Please give password")
}
return firebaseAuthRepository.register(email, password)
}
}
LoginViewModel.kt
@HiltViewModel
class LoginViewModel @Inject constructor(
private val registerUseCase: RegisterUseCase
) : BaseViewModel() {
val register: MutableStateFlow<Resource<User>> = MutableStateFlow(Loading(true))
fun register(email: String, password: String) {
viewModelScope.launch {
loginUseCase.execute(email, password)
.flowOn(Dispatchers.IO)
.onStart { }
.onCompletion { register.value = Loading(false) }
.catch { register.value = Error(it) }
.collectLatest { register.value = Success(it) }
}
}
}
Resource.kt
sealed class Resource<out T> {
data class Loading(val isLoading: Boolean) : Resource<Nothing>()
data class Success<T>(val item: T) : Resource<T>()
data class Error(val throwable: Throwable) : Resource<Nothing>()
}
RegistrationFragment.kt
@AndroidEntryPoint
class RegistrationFragment : BaseFragment<FragmentRegisterBinding>() {
private val viewModel: RegistrationViewModel by viewModels()
override fun getViewBinding(
inflater: LayoutInflater,
container: ViewGroup?
): FragmentRegisterBinding {
return FragmentRegisterBinding.inflate(inflater, container, false)
}
override fun configureViews() {
binding?.registerButton?.setOnClickListener {
viewModel.register(
binding?.mailEditText?.value() ?: "",
binding?.passwordEditText?.value() ?: ""
)
}
}
override fun bindWithViewModel() {
lifecycleScope.launch {
viewModel.registration.collectLatest {
when(it) {
is Loading -> {
if (it.isLoading) {
} else {
}
}
is Success -> {
findNavController().navigate(R.id.home_fragment)
}
is Error -> {
requireContext().toast(it.throwable.message ?: "")
}
}
}
}
}
}