Skip to content

Instantly share code, notes, and snippets.

@PrashamTrivedi
Last active February 21, 2018 14:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PrashamTrivedi/9e7d4d89eedd39ffe8f06b44f4e817a6 to your computer and use it in GitHub Desktop.
Save PrashamTrivedi/9e7d4d89eedd39ffe8f06b44f4e817a6 to your computer and use it in GitHub Desktop.
Code for blogpost. Koroutines, MVP+VM
sealed class ApiResponse<T> {
class Loading<Unit> : ApiResponse<Unit>()
class Nothing<Unit> : ApiResponse<Unit>()
class NoInternet<Unit> : ApiResponse<Unit>()
class ConnectionError<Unit>(val throwable: Throwable, val error: String) : ApiResponse<Unit>()
data class Success<T>(val response: Response<T>,
val data: T? = response.body(),
val headers: Headers? = response.headers()) : ApiResponse<T>()
data class SuccessData<T>(val data: T?) : ApiResponse<T>()
open class WebserviceError<Unit>(open val errorMessage: String,
val throwable: Throwable = RuntimeException(errorMessage),
val errorCode: Int = 0) : ApiResponse<Unit>()
class ServerBusyError<Unit>(override val errorMessage: String) : WebserviceError<Unit>(
errorMessage = errorMessage,
errorCode = 503)
class UnauthorizedError<Unit>(override val errorMessage: String) : WebserviceError<Unit>(
errorMessage = errorMessage,
errorCode = 401)
}
fun <T> Throwable.parseWsError(): ApiResponse<T> {
val defaultMessage = "Something has gone wrong"
val offlineMessage = "You are currently offline."
return when {
this is HttpException -> this.parseWsError(defaultMessage)
this is UnknownHostException -> NoInternet()
this is HttpException -> ConnectionError(this, offlineMessage)
this is SocketTimeoutException -> ConnectionError(this, offlineMessage)
this is SocketException -> ConnectionError(this, offlineMessage)
else -> WebserviceError(errorMessage = defaultMessage)
}
}
private fun <T> HttpException.parseWsError(defaultMessage: String): WebserviceError<T> {
val message = response().errorBody()?.string() ?: defaultMessage
var errorMessage = defaultMessage
try {
if (!message.isEmptyString()) {
val mJsonObject = JSONObject(message)
errorMessage = mJsonObject.optString("Message")
}
} catch (e: Exception) {
e.printStackTrace()
}
val code = response().code()
return when (code) {
401 -> UnauthorizedError(errorMessage = errorMessage)
503 -> ServerBusyError(errorMessage = errorMessage)
else -> WebserviceError(errorMessage = errorMessage, errorCode = code)
}
}
class LoginActivity : AppCompatActivity() {
@Inject lateinit var loginPresenter: LoginPresenter
lateinit var binding: ActivityLoginBinding
val loginViewModel: LoginViewmodel by lazy {
LoginViewmodel()
}
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_login)
binding.login = loginViewModel
btnSignIn.setOnClickListener {
if (loginViewModel.isValid) {
loginPresenter.doLogin(loginViewModel.loginModel)
}
}
observe(loginPresenter.loginResponse) {
when (it) {
is Loading -> showLoading()
is SuccessData<LoginResponse> -> handleData(it.data)
is WebserviceError -> handleError(it.throwable)
}
}
}
private fun handleError(throwable: Throwable) {
hideLoading()
}
private fun handleData(data: LoginResponse?) {
hideLoading()
d("Login resp: $data")
}
private fun showLoading() {
btnSignIn.setVisible(false)
progressBar.setVisible(true)
}
private fun hideLoading() {
btnSignIn.setVisible(true)
progressBar.setVisible(false)
}
}
interface LoginApi {
//Here all classes LoginRequest, LoginResponse etc are just placeholder for your actual classes
@POST("{Your own url}")
fun loginUser(@Body loginRequest: LoginRequest): Call<LoginResponse>
}
typealias LiveDataApi<T> = LiveData<ApiResponse<T>>
typealias MutableLiveDataApi<T> = MutableLiveData<ApiResponse<T>>
typealias Presenter = ViewModel
class LoginPresenter(val loginApi: LoginApi) : Presenter() {
private val loginData = MutableLiveDataApi<LoginResponse>().apply { value = Nothing() }
val loginResponse: LiveDataApi<LoginResponse> = loginData
private val loginActor = actor<LoginRequest>(context = UI, capacity = Channel.CONFLATED) {
for (loginRequest in this) {
loginData.value = Loading()
try {
val loginResponse = loginApi.loginUser(loginRequest).await()
loginData.value = SuccessData(data = loginResponse)
} catch (e: Exception) {
loginData.value = e.parseWsError()
}
}
}
fun doLogin(loginRequest: LoginRequest) = loginActor.offer(loginRequest)
}
class LoginViewmodel : BaseObservable() {
var userName: String = ""
@Bindable get
var password: String = ""
@Bindable get
var userNameError: String = ""
@Bindable get
var passwordError: String = ""
@Bindable get
val isValid: Boolean
get() {
userNameError = ""
notifyPropertyChanged(BR.userNameError)
passwordError = ""
notifyPropertyChanged(BR.passwordError)
return when {
userName.isEmptyString() -> {
userNameError = "Please enter username"
notifyPropertyChanged(BR.userNameError)
false
}
password.isEmptyString() -> {
passwordError = "Please enter password"
notifyPropertyChanged(BR.passwordError)
false
}
else -> true
}
}
val loginModel: LoginRequest
get() = LoginRequest(userName = userName, password = password)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment