I'm copying the Google's code from their repository of samples implementing Android Architecture Components, slowly adapting to the needs of the app I have in mind, using the code as a base. I have reached already a part where for me is displaying an error and I can't understand why. This is the code block:
data class ApiSuccessResponse<T>(val responseBody: T) : ApiResponse<T>() {
constructor(body: T) : this (responseBody = body)
}
The error message underlying is
Conflicting overloads: public constructor ApiSuccessResponse(body: T) defined in com.example.rxe.api.ApiSuccessResponse, public constructor ApiSuccessResponse(responseBody: T) defined in com.example.rxe.api.ApiSuccessResponse
Here's where I call ApiSuccessResponse, just like in the sample code:
sealed class ApiResponse<T> {
companion object {
fun <T> create(response: Response<T>): ApiResponse<T> {
return if (response.isSuccessful) {
val responseBody = response.body()
if (responseBody == null || response.code() == 204) {
ApiEmptyResponse()
} else {
ApiSuccessResponse(body = responseBody)
}
} else {
val error = response.errorBody()?.string()
val message = if (error.isNullOrEmpty()) {
response.message()
} else {
error
}
ApiErrorResponse(errorMessage = message ?: "Unknown error")
}
}
}
}
Something might have changed since the sample has been written. But if I rename the variable to body instead of responseBody, the same error will underline the call for the class ApiSuccessResponse.
You don't seem to understand how constructors work in Kotlin. This is the code you tried to copy:
data class ApiSuccessResponse<T>(
val body: T,
val links: Map<String, String>
) : ApiResponse<T>() {
constructor(body: T, linkHeader: String?) : this(
body = body,
links = linkHeader?.extractLinks() ?: emptyMap()
)
//.... rest of body
}
It has two constructors:
Primary constructor: ApiSuccessResponse(body: T, links: Map<String, String>)
Secondary constructor: ApiSuccessResponse(body: T, linkHeader: String?) (which extracts map of links from String and passes it as links into primary).
What you have is:
Primary constructor: ApiSuccessResponse(resposebody: T)
Secondary constructor: ApiSuccessResponse(body: T) (which tries to call primary constructor 1:1, but it just clashes due to identical signature)
If you don't need secondary constructor You should just delete it entirely.
Related
I have two different response from the same endpoint. One being the actual success result data model and one being an error response model. Both json structure like this:
SuccessResponse:
{
"result":{
"id":1,
"name_en":"Stack Over Flow",
"summary":"Stack Overflow is the largest, most trusted online community for developers to learn, share their programming knowledge, and build their careers."
}
}
ErrorResponse:
{
"message": "Login Failed"
}
I can handle the success response but I can't show the error message what I get from the server. I have tried many ways but I can't do this.
Here my I share my some aspect what I did
MainViewModel.kt
var job: Job? = null
val myDataResponse: MutableLiveData<HandleResource<DataResponse>> =MutableLiveData()
fun myData() {
job = CoroutineScope(Dispatchers.IO).launch {
val myDataList = mainRepository.myData()
withContext(Dispatchers.Main) {
myDataResponse.postValue(handleMyDataResponse(myDataList))
}
}
}
private fun handleMyDataResponse(myDataResponse: Response<DataResponse>): HandleResource<DataResponse>? {
if (myDataResponse.isSuccessful) {
myDataResponse.body()?.let { myDataData ->
return HandleResource.Success(myDataData)
}
}
return HandleResource.Error(myDataResponse.message())
}
I need a solution while server give me error message I want to show same error message on my front side. How can I achieve this?
private fun handleMyDataResponse(myDataResponse: Response<DataResponse>): HandleResource<DataResponse>? {
myDataResponse.body()?.let { myDataData ->
if (myDataResponse.code() == 200) {
return HandleResource.Success(myDataData )
} else {
val rawResponse = myDataData.string()
return HandleResource.Error(getErrorMessage(rawResponse))
}
}
}
fun getErrorMessage(raw: String): String{
val object = JSONObject(raw);
return object.getString("message");
}
The body of the response (be it success or failure) is response.body(). And if you want to get it as a String, then call response.body().string(). Since you want to read message object from the response you need to convert it into Json.
If you are a following MVVM pattern then I suggest to create a sealed class for the API calls.
To handle api success and failure or network issue. Resource class is going to be generic because it will handle all kind of api response
sealed class Resource<out T> {
data class Success<out T>(val value: T): Resource<T>()
data class Failure(
val isNetworkErro: Boolean?,
val errorCode: Int?,
val errorBody: ResponseBody?
): Resource<Nothing>()
}
on the base repository while calling the API, you can return the resource whether it is success or failure.
abstract class BaseRepository {
suspend fun <T> safeApiCall(
apiCall: suspend () -> T
): Resource<T>{
return withContext(Dispatchers.IO){
try {
Resource.Success(apiCall.invoke())
} catch (throwable: Throwable){
when (throwable){
is HttpException -> {
Resource.Failure(false,throwable.code(), throwable.response()?.errorBody())
}
else ->{
Resource.Failure(true, null, null)
}
}
}
}
}
}
If you follow this pattern you'll be able to handle all the failure and success response, I hope this will help.
I have an API which sends me errors with a custom JSON under 500 error
so, here's my Api Interface:
#POST("${API_PREFIX}method")
fun callForIt(#Body request: MyRequest) : Single<MyResponse>
And here's how I call it:
api.callForIt(request)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
// Here I get perfect MyResponse object
},
{
// And here I get only throwable, but need to get info from the json
})
So, what I want is to describe a custom data class like
data class ErrorResponse(
val type: String,
val fatal: Boolean,
val msg: String
// etc
)
because the server is sending me valuable info in JSON and I need to obtain it, so is there any way to read it from onFailure()?
{ t: Throwable ->
if (t is HttpException) {
val errorMsg = t.response()!!.errorBody()!!.string()
val errorResponse = Gson().fromJson(errorMsg, ErrorResponse::class)
// handle the error with errorResponse...
}
else {
// handle the error with code >=500...
}
}
I m using two constructor with different input types one as string and other as generic. The problem is while using Kotlin its only using the string constructor and ignores the generic
class DataResponse<T> {
var isSuccess: Boolean = false
private set
var errorMessage: String? = null
var data: T? = null
constructor(success: Boolean, data: T) {
this.isSuccess = success
this.data = data
}
constructor(success: Boolean, errorMessage: String) {
this.isSuccess = success
this.errorMessage = errorMessage
}
}
usage
if (apiResponse.code() == 200) {
Observable.just(DataResponse<List<ResultDTO>>(true,
apiResponse.body()?.resultList)) ---> **(error on this line forcing to convert it to string)**
} else {
Observable.just(DataResponse(false, "Something went wrong"))
}
You can give named parameters in kotlin. That is if more than two constructors or functions with same name exists, we can explicitly specify the parameter as named one. Here I suggest to explicity mention the parameter data.
if (apiResponse.code() == 200) {
Observable.just(DataResponse<List<ResultDTO>>(true,data=
apiResponse.body()?.resultList))
} else {
Observable.just(DataResponse(false, "Something went wrong"))
}
Currently your DataResponse class represents two different things. One is an error message and the other one is actual data in case of success. isSuccess is redundant too because it is always true when data is non-null and always false if errorMessage is non-null.
I would change the design in the following way:
sealed class DataResponse
class SuccessResponse<T>(val data: T?)
class ErrorResponse(val errorMessage: String)
Now you have two separate classes, both having the same supertype DataResponse. This way you will always know what you are dealing with.
Usage:
when(dataResponse) {
is SuccessResponse -> TODO("deal with data")
is ErrorResponse -> TODO("deal with error")
}
I'm fetching response from some API, after getting the response I converting it to List of my required Object e.g:
fun <T> getAsList(input: String): ArrayList<T> {
val objType = object : TypeToken<ArrayList<T>>() {}.type
val result = Gson().fromJson(input, objType) as ArrayList<T>
println(result[0]) // <-- no warning here !! It's work
println("result: " + result.toString()) // Also it's work here
return result
}
Then I pass this list to somewhere e.g:
updateFromDownload(getAsList<T>(resultValue))
And by override this method I can get the result, e.g:
override fun updateFromDownload(result: List<Response>?) {
val listTest = ArrayList<Response>()
listTest.add(result!![0]) // <-- This work
println(listTest)
println("resss:" + result[0]) // <-- This not work !!!
for (response in result){
// The loop crash too, not work
}
As demonstrated above, adding to listTest work fine, but If I tried to get the element individually like I did inside getAsList() It's crash due to:
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to ......Response
Can I access the result directly without fill it to new list?
Edit- Whole cycle for code:
class ConnectToURL<T>(callback: DownloadCallback<T>) : AsyncTask<String, Int, ConnectToURL.Result>() {
private var mCallback: DownloadCallback<T>? = null
init {
setCallback(callback)
}
override fun onPostExecute(result: Result?) {
mCallback?.apply {
result?.mException?.also { exception ->
//val gson = Gson().fromJson(result.mResultValue!!, type)
//updateFromDownload(gson)
return
}
result?.mResultValue?.also { resultValue ->
updateFromDownload(getAsList<T>(resultValue))
return
}
finishDownloading()
}
}
}
And I Invoke ConnectToURL from:
class BuilderClass<T> private constructor(
callback: DownloadCallback<T>,
private val url: String
) {
init {
ConnectToURL(callback).execute(url)
}
/// some code . . .. .
fun build() = BuilderClass(callback, url)
}
}
Then I override the updateFromDownload function as it's part from DownloadCallback
The generic type T is erased at compile time, so the type information is not present at runtime.
object : TypeToken<ArrayList<T>>() {}.type
Thats the reason Gson does not convert to the Response class.
You could use inline plus reified to avoid type erasure.
inline fun <reified T> getAsList(input: String): ArrayList<T>
I'm trying to create generic architecture to consume complex json structure as follows:
Json Format
{
"type": "success",
"code": "s-groups-0006",
"description": "Index List successfully",
"result": {
"asOnDate": 1505457095278,
"indexList": [
{
"change": "22.35",
"changePercent": "0.27",
"isDefault": true,
"isEditable": false
}
]
}
}
Dagger Format
#Singleton
fun provideGson(): Gson =
GsonBuilder()
.setLenient()
// .registerTypeAdapter(BaseResponse::class.java, RestDeserializer<BaseResponse<T>>())
.create()
Rest Deseralizers
class RestDeserializer<T> : JsonDeserializer<T> {
#Throws(JsonParseException::class)
override fun deserialize(je: JsonElement, type: Type, jdc: JsonDeserializationContext): T? {
val content = je.asJsonObject
// Deserialize it. You use a new instance of Gson to avoid infinite recursion
// to this deserializer
return Gson().fromJson<T>(content, type)
}
}
Error callback
abstract class ErrorCallBack<T : BaseResponse<T>> : DisposableObserver<T>() {
protected abstract fun onSuccess(t: T)
override fun onNext(t: T) {
//You can return StatusCodes of different cases from your API and handle it here. I usually include these cases on BaseResponse and iherit it from every Response
onSuccess(t)
}
override fun onError(e: Throwable) {
when (e) {
is HttpException -> {
val responseBody = (e).response().errorBody()
responseBody?.let {
L.e("Error in call htttp exception")
}
}
is SocketTimeoutException -> {
// todo
L.e("Error in Socket time out")
}
is IOException -> {
// todo
L.e("Error in IO Exception")
}
else -> {
e.message?.let {
// todo
}
}
}
}
override fun onComplete() {
}
private fun getErrorMessage(responseBody: ResponseBody): String {
return try {
val jsonObject = JSONObject(responseBody.string())
jsonObject.getString("message")
} catch (e: Exception) {
e.message!!
}
}
}
Repository
override fun getValidateUser(validateUser: ValidateUser): LiveData<ValidateUserResponse> {
val mutableLiveData = MutableLiveData<ValidateUserResponse>()
remoteServices.requestValidateUser(validateUser)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : ErrorCallBack<BaseResponse<ValidateUserResponse>>() {
override fun onSuccess(t: BaseResponse<ValidateUserResponse>) {
if (t.type == CommonContents.SUCCESS) {
L.d("Success in validate user")
mutableLiveData.value = transform(t)
} else {
L.e("Validate User Error")
}
}
})
return mutableLiveData
}
Data Class
data class BaseResponse<out T>(
#SerializedName(CommonContents.TYPE) val type: String,
#SerializedName(CommonContents.CODE) val Code: String,
#SerializedName(CommonContents.DESCRIPTION) val Description: String,
#SerializedName(CommonContents.RESULT)val result: T? = null)
These are my structures and I try to make a generic structure but am facing an issue when invoking the Error callback.
Please guide me how to achieve this. Can i call generic method inside generic Response?
.subscribeWith(object : ErrorCallBack<BaseResponse<ValidateUserResponse>>() {
Guide To Achieving Working Code
This is my guide to making some code work. It is based on the principles of Test Driven Development.
Set up your unit test environment in your IDE. Personally, I have been using JUnit 4 with Eclipse, but you may prefer JUnit 5 with JetBrains' IDE.
Write a unit test for your ErrorCallback class constructor. Make it pass. Next, write unit tests for each of the methods, to see that it behaves the way that you expect.
Write unit tests where your test fixture is a PublishSubject<BaseResponse<Integer>> for some number of different cases: normal data, sequence of normal data, normal data followed by error, normal data followed by completion.
From there, add some more tests so that you can test the entire observable chain.