Reputation: 1242
I am new to RX Java. While implementing Rx java with Retrofit i found i am getting throw-able object in my doOnError(){}
But what i want my doOnError() of RX Java should return ErrorBase() -> that is my custom class. instead of throwable.
it will help me to handle error at central. i will pass my throw-able object to my ErrorBase class where i have handled custom messages.
Below is doOnError()
. Where i want to return ErrorBase object
apiInterface.getLoginDetails(auth)
.doOnNext {
//LoginResponse
}
doOnError{
return ErrorBase(throwable)
}
Code of other classes.
Api Interface class
interface ApiInterface {
@POST("login")
fun getLoginDetails(@Header(Constants.AUTHORIZATION) auth: String): Observable<LoginResponseModel>
}
LoginRepository
class LoginRepository @Inject constructor(private val apiInterface: ApiInterface,
val utils: Utils) {
fun getLoginDetails(auth: String): Observable<LoginResponseModel> {
return apiInterface.getLoginDetails(auth)
.doOnNext {
}
.doOnError {
//Right now having throw-able object
}
}
}
ErrorBase
class ErrorBase(private val throwable: Throwable) {
private var message: String?
private var statusCode: Int
init {
statusCode = getStatusCode()
message = getMessage()
}
private fun getStatusCode(): Int {
if (throwable is HttpException) {
val exception = throwable
return exception.code()
}
return -1
}
private fun getMessage() =
when (throwable) {
is IOException -> "Something Went Wrong"
is UnknownHostException -> "No internet connectivity"
is SocketTimeoutException -> "Slow Internet connectivity"
else -> throwable.message
}
}
LoginvViewModel
class LoginViewModel @Inject constructor(
private val loginRepository: LoginRepository) : ViewModel() {
private val TAG = this.javaClass.name
private var loginResult: MutableLiveData<LoginResponseModel> = MutableLiveData()
private var loginError: MutableLiveData<String> = MutableLiveData()
private var loginLoader: MutableLiveData<Boolean> = MutableLiveData()
private lateinit var disposableObserver: DisposableObserver<LoginResponseModel>
fun loginResult(): LiveData<LoginResponseModel> {
return loginResult
}
fun loginError(): LiveData<String> {
return loginError
}
fun loginLoader(): LiveData<Boolean> {
return loginLoader
}
private fun getLoginData(auth: String) {
loginLoader.postValue(true)
initLoginObserver()
loginRepository.getLoginDetails(auth)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.debounce(400, MILLISECONDS)
.subscribe(disposableObserver)
}
private fun initLoginObserver() {
disposableObserver = object : DisposableObserver<LoginResponseModel>() {
override fun onComplete() {
}
override fun onNext(loginDetails: LoginResponseModel) {
loginResult.postValue(loginDetails)
loginLoader.postValue(false)
}
override fun onError(e: Throwable) {
loginError.postValue(e.message)
loginLoader.postValue(false)
}
}
}
fun disposeElements() {
if (null != disposableObserver && !disposableObserver.isDisposed) disposableObserver.dispose()
}
fun loginClicked() {
getLoginData("auth")
}}
Upvotes: 4
Views: 3316
Reputation: 10106
Firstly, doOnError
isn't aimed to transform/return some data, but helps to handle side-effects like logging.
Second thing, ErrorBase
doesn't fit well together with LoginResponseModel
cause they don't have any common parent.
Thus, I suggest you following solution:
Create one base class for your response:
sealed class LoginResponse {
class Result( ..your data here.. ) : LoginResponse()
class Error( ... ) : LoginResponse()
}
Make function return LoginResponse
and do following changes:
fun getLoginDetails(auth: String): Observable<LoginResponse> {
return apiInterface.getLoginDetails(auth)
.map { data -> LoginResponse.Result(data) }
.onErrorReturn { throwable -> LoginResponse.Error(throwable) }
}
Now both results have one common parent and you can use getLoginDetails
in the following way:
fun doRequest() {
loginRepository.getLoginDetails(auth)
.subscribe { result ->
when (result) {
is LoginResponse.Result -> //do something with result
is LoginResponse.Error -> //do something with error
}
}
}
Some explanation.
onErrorReturn
does exactly what you need - returns your custom value in case if error occurs
If you don't add LoginResponse
you have to make Observable<Any>
which is loosely typed and doesn't really well describes your interface.
Making LoginResponse
sealed allows to check only 2 cases whether emitted data is Result
or Error
. Otherwise Kotlin compiler forces you to add additional else
branch
Update In case if you need to do same thing in multiple places you can go with this:
sealed class Response<T> {
data class Result<T>(val result: T) : Response<T>()
data class Error<T>(val throwable: Throwable) : Response<T>()
}
fun getLoginDetails(auth: String): Observable<Response<LoginResponseModel>> {
return apiInterface.getLoginDetails(auth)
.map<Response<LoginResponseModel>> { data -> Response.Result(data) }
.onErrorReturn { throwable -> LoginResponse.Error(throwable) }
}
..and somewhere in your code..
fun handleResponse(response: Response<LoginData>) {
when (response) {
is Response.Result -> response.result
is Response.Error -> response.throwable
}
}
Upvotes: 4