How to use Retrofit2 - java

I called my service with AsyncTask and DefaultHttpClient before but now i want to use Retrofit. I read about this and implement that and onResponse method called but my response has Bad Request Message and can't get body of response.
My error is:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.retrofitsample.f.retrofitsample/com.retrofitsample.f.retrofitsample.MainActivity}: java.lang.IllegalArgumentException: Unable to create converter for com.retrofitsample.f.retrofitsample.model.HttpResponse<com.retrofitsample.f.retrofitsample.model.MS>
Caused by: java.lang.IllegalArgumentException: class com.retrofitsample.f.retrofitsample.model.MS declares multiple JSON fields named Id
I have a wcf service like below :
[OperationContract]
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.WrappedRequest,
UriTemplate = "GetMS")]
HttpResponse<MS> GetMS(string token,string Id);
I have a HttpResponse and all of my services return a type of T.. I write belo code in client side:
MyActivity:
retrofit2.Call<HttpResponse<MS>> call=service.getAllMonitoringScheduale("971048F6-7ABA-4060-8CC3-BC57EC259FA3","1292");
call.enqueue(new Callback<HttpResponse<MS>>() {
#Override
public void onResponse(retrofit2.Call<HttpResponse<MS>> call, Response<HttpResponse<MS>> response) {
Log.e("Response=",response.body().getResultMessage());
}
#Override
public void onFailure(retrofit2.Call<HttpResponse<MS>> call, Throwable t) {
Toast.makeText(MainActivity.this, "Something went wrong...Please try later!", Toast.LENGTH_SHORT).show();
}
});
My Interface:
public interface GetDataService {
#POST("GetMonitoringSchedule")
Call<HttpResponse<MS>> getAllMS(#Query("token")String token,#Query("Id") String Id);
}
and this is my models Class:
public class HttpResponse<T> {
#SerializedName("ResultMessage")
private String ResultMessage;
#SerializedName("Result")
private T Result;
public HttpResponse(String ResultMessage,T Result){
this.ResultMessage=ResultMessage;
this.Result=Result;
}
public String getResultMessage(){
return ResultMessage;
}
public void setResultMessage(String ResultMessage){
this.ResultMessage=ResultMessage;
}
public T getResult(){
return Result;
}
public void setResult(T Result){
this.Result=Result;
}
}
public class MS {
#SerializedName("Id")
public long Id;
#SerializedName("PId")
public long PId;
#SerializedName("SType")
public int SType;
#SerializedName("SDateF")
public Date SDateF ;
#SerializedName("SDateT")
public Date SDateT;
..Constructor and setter and getter
}
How to fix this errors and what is my problem?
I don't know how to use HttpResponse type in Retrofit2?

I have found a project that uses Retrofit in order to make network requests.
You can use it as a reference in order to understand how to use Retrofit.
https://github.com/AcademyTLV/fundamentals-2018-exercise/tree/ex-7-networking
Also be sure to use a POJO In order to create your classes to handle the request:
http://www.jsonschema2pojo.org/

Here i have fully example for it.
This dependancy add in gradle
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
annotationProcessor 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
Here now create ApiClient.kt file
object ApiClient {
val BASE_URL = "http://yourwebsite/services/"
private var retrofit: Retrofit? = null
val client: Retrofit
get() {
if (retrofit == null) {
retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
return retrofit!!
}
}
Now create APIInterface.kt
#FormUrlEncoded
#POST("users/login")
fun POST_LOGIN(
#Field("imei_number") imei_number: String,
#Field("device_token") device_token: String,
#Field("mobile") mobile: String,
#Field("password") password: String
): Call<LoginResponse>
#GET("general/init-data")
fun GENERAL_MODULE(
#Header("Authorization") auth_key: String
): Call<InitResponse>
#GET("event-gallery/list")
fun GET_Event_GALLERY(
#Header("Authorization") auth_key: String
): Call<EventListResponse>
#GET("event-gallery/photo-list")
fun GET_Event_GALLERY_PHOTO(
#Header("Authorization") auth_key: String,
#Query("id") id: Int
): Call<EventGallerListResponse>
if Any Header for token the use #Header and also When call #GET that time params use #Query and #Post that time #Fields
Now Response file
data class EventListResponse(
#SerializedName("success")
var success: Boolean,
#SerializedName("data")
var data: EventgalleryModel?,
#SerializedName("server_error"),
#SerializedName("eventgallery")
var eventgallery: ArrayList<EventListData>
var server_error: Boolean,
#SerializedName("message")
var message: String
)
Then create Model class of Response
Now time to Activity code
private fun loadData() {
card_progress.visibility = View.VISIBLE
val apiService = ApiClient.client.create(ApiInterface::class.java)
val call =
apiService.GET_FEE_INSTALMENT_LIST(PreferenceManager.getAuthKey(this#FeesInstalmentActivity)!!)
call.enqueue(object : Callback<FeeInstalmentListResponse> {
override fun onResponse(
call: Call<FeeInstalmentListResponse>,
response: Response<FeeInstalmentListResponse>
) {
card_progress.visibility = View.GONE
val data = response.body()!!.data
if (response.code() == 200 && data != null) {
if (response.body()!!.server_error) {
txt_no_data_fee.visibility = View.VISIBLE
txt_no_data_fee.text = response.body()!!.message
} else {
Log.e("data", data.toString())
if (data != null && data.feesinstalment.isEmpty()) {
txt_no_data_fee.visibility = View.VISIBLE
} else {
txt_no_data_fee.visibility = View.GONE
adapter!!.setItem(data.feesinstalment)
}
}
} else if (response.code() == 401) {
PreferenceManager.removePref(this#FeesInstalmentActivity)
startActivity(
Intent(this#FeesInstalmentActivity, LoginActivity::class.java)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
)
finish()
} else {
Toast.makeText(
this#FeesInstalmentActivity,
R.string.somethingWrong,
Toast.LENGTH_SHORT
).show()
}
}
override fun onFailure(call: Call<FeeInstalmentListResponse>, t: Throwable) {
card_progress.visibility = View.GONE
Log.e("onFailure", t.message)
txt_no_data_fee.visibility = View.VISIBLE
}
})
}

Sry i forget Adapter here
class FeeInstalmentAdapter(
private val context: Context,
private var items: ArrayList<FeeInstalmentListData>
) : RecyclerView.Adapter() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(LayoutInflater.from(context).inflate(R.layout.row_fees_instalment_item, parent, false))
}
#SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.due_date.text = DateHelper.parseData(items[position].due_date!!, "yyyy-MM-dd", "dd MMM yyyy")
holder.instalment_title.text = items[position].instalment_title
if (items[position].paid_date == null) {
holder.paid_text.visibility = View.GONE
holder.paid_date.text = context.resources.getString(R.string.UnPaid)
holder.paid_date.setTextColor(Color.parseColor("#DC143C"))
} else {
holder.paid_date.text = DateHelper.parseData(items[position].due_date!!, "yyyy-MM-dd", "dd MMM yyyy")
holder.paid_date.setTextColor(Color.parseColor("#58A259"))
}
//holder.paid_date.text = items[position].paid_date
holder.amount.text = "Rs. " + items[position].amount
holder.amount.setTextColor(Color.parseColor("#ED7136"))
}
override fun getItemCount(): Int {
return items.size
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItemViewType(position: Int): Int {
return position
}
fun setItem(holidays: ArrayList<FeeInstalmentListData>) {
items = holidays
notifyDataSetChanged()
}
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val due_date = view.due_date
val instalment_title = view.instalment_title
val paid_date = view.paid_date
val amount = view.amount
val paid_text = view.paidText
}
}

Related

Retrofit Post on Android kotlin not working but working on postman

I am trying to Hit the API using the Retrofit Post method to put the form data but the problem is that the API returns null all the time even if I try to pass the params. I tested the API by running it in Postman it works fine and I also added internet permission and set usesCleartextTraffic to true under manifest.xml, so it would be really helpful if anyone guides me to rectify this issue.
Output When I tried to debugg
call = Unresolved reference: call
response = Unresolved reference: response
this = {MainActivity#9661}
params = {HashMap#10022} size = 5
"password" -> "Ram#123"
"countryCode" -> {Integer#10042} 91
"name" -> "Ranjith"
"mobile" -> {Long#10046} 99********
"emailID" -> "var***#gmail.com"
Response back from the API with body null all the time
response = {Response#10220} "Response{protocol=http/1.1, code=400, message=Bad Request, url=http://*.***.***.***:***0/api/v1/****/auth/register}"
body = null
errorBody = {ResponseBody$1#10224}
content = {Buffer#10228} "[text={"code":400,"error":true,"msg":"Enter a valid name"}]"
contentLength = 52
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
uploadBt.setOnClickListener {
callApi()
}
}
private fun callApi() {
val params = HashMap<String?, Any?>()
params["name"] = "Ranjith"
params["mobile"] = 99*******
params["emailID"] = "var***#gmail.com"
params["password"] = "Ram#123"
params["countryCode"] = 91
RetrofitClient.instance.registerUser(params)
.enqueue(object : Callback<DefaultResponse>{
override fun onResponse(call: Call<DefaultResponse>, response: Response<DefaultResponse>) {
print(response)
if(response.isSuccessful)
{
Toast.makeText(applicationContext, response.body()?.msg, Toast.LENGTH_SHORT).show()
}else
{
var jsonObject: JSONObject? = null
try {
jsonObject = JSONObject(response.errorBody()!!.string())
val code = jsonObject.getString("code")
val error = jsonObject.getString("error")
val message = jsonObject.getString("msg")
Toast.makeText(applicationContext, message+"\n"+code+"\n"+error, Toast.LENGTH_LONG).show()
} catch (e: JSONException) {
Toast.makeText(applicationContext, e.toString(), Toast.LENGTH_LONG).show()
e.printStackTrace()
}
}
}
override fun onFailure(call: Call<DefaultResponse>, t: Throwable) {
Toast.makeText(applicationContext, t.toString(), Toast.LENGTH_SHORT).show()
}
})
}
}
Interface Api
interface Api {
#FormUrlEncoded
#POST("/api/v1/******/****/register")
fun registerUser(
#FieldMap params: HashMap<String?, Any?>
): Call<DefaultResponse>
}
RetrofitClient.kt object class
object RetrofitClient {
private const val BASE_URL = "http://3.1**.**5.1*1:****"
private val okHttpClient = OkHttpClient.Builder()
.addInterceptor { chain ->
val original = chain.request()
val requestBuilder = original.newBuilder()
.method(original.method(), original.body())
val request = requestBuilder.build()
chain.proceed(request)
}.build()
val instance: Api by lazy{
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build()
retrofit.create(Api::class.java)
}
}
DefaultResponse.kt model data class
data class DefaultResponse(
#SerializedName("code")
val code: Int,
#SerializedName("err")
val error: Boolean,
#SerializedName("msg")
val msg: String
)
Through Postman

Post request to Firebase push notifications nothing happens

//Iam trying to do a post request for push notification, but nothing happens..
//The interface
interface RestInterface {
#Headers("Content-Type: application/json", "Authorization: key)
#POST("fcm/send")
fun sendingNotification(#Body body: NotificationBody?): Call<ResponseBody?>?
}
//NotificationBody
class NotificationBody(data: Data, to: String) {
#SerializedName("notification")
private val data: Data
#SerializedName("to")
private val to: String
init {
this.data = data
this.to = to
}
}
//Data class
class Data
(#field:SerializedName("title")
private val title: String,
#field:SerializedName("content")
private val content: String)
//Retrofit Request
fun sentNotification() {
val retrofit = Retrofit.Builder()
.baseUrl("https://fcm.googleapis.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
var title = "My Title";
var content = "My message";
var to = deviceId
var data = Data(title, content);
var body = NotificationBody(data, to);
val service = retrofit.create(RestInterface::class.java)
val response = service.sendingNotification(body)
i implemented response code but get a a lot of error. Remind, iam with kotlin not java.
response.enqueue( Callback<ResponseBody>() {
#Override
public void onResponse(retrofit2.Call<ResponseBody> call,
retrofit2.Response<ResponseBody> response) {
Log.d("kkkk","done");
}
#Override
public void onFailure(retrofit2.Call<ResponseBody> call,
Throwable t) {
}
})
}
//Whats wrong with above request setup ?
//if is sent the message with postman The notification is showing on the phone, so the onMessageReceived settings are ok

Creating Live Data for Auth Token in Account Manager

I am trying to create live data for the authToken in AccountManager.
This is how I am getting the auth token.
suspend fun Fragment.getAuthToken(): String? {
val am: AccountManager = AccountManager.get(activity)
val accounts: Array<out Account> = am.getAccountsByType(getAccountType())
var authToken: String? = null
if (accounts.isNotEmpty()) {
val account = accounts.first()
withContext(Dispatchers.IO) {
authToken = am.blockingGetAuthToken(account, getAccountType(), true)
}
}
return authToken
}
According to the documentation I should do something like this:
class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
private val stockManager = StockManager(symbol)
private val listener = { price: BigDecimal ->
value = price
}
override fun onActive() {
stockManager.requestPriceUpdates(listener)
}
override fun onInactive() {
stockManager.removeUpdates(listener)
}
}
However I can't figure out how to convert the example to match my case.
According to another link
you can use live-data builder for coroutine:
val token: LiveData<String> = liveData {
val tokenValue = someYourFragment.getAuthToken()
emit(tokenValue)
}

OkHttp EventListener : byteCount value is always zero in responseBodyEnd callback

I am using EventListener callback for some analysis in my app but I am facing an issue where I am not getting the correct value byteCount in responseBodyEnd callback. It is always 0.
I am attaching the code below.
val client = OkHttpClient.Builder()
.eventListenerFactory(HttpEventListenerFactory.FACTORY)
.build()
val request = Request.Builder()
.url("http://jsonplaceholder.typicode.com/comments?postId=1")
.build()
with(client) {
newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.d("OkHttp##", "Request failed")
}
override fun onResponse(call: Call, response: Response) {
handleResponse(response.body())
response.close()
Log.d("OkHttp##", "Response received")
}
})
}
MyEventListenerImpl.kt
public class HttpEventListenerFactory extends EventListener {
public static final Factory FACTORY = new Factory() {
final AtomicLong nextCallId = new AtomicLong(1L);
#Override
public EventListener create(Call call) {
long callId = nextCallId.getAndIncrement();
Log.d("OkHttp##", "next call id : " + nextCallId);
String message = String.format(Locale.US, "%04d %s%n", callId, call.request().url());
Log.d("OkHttp##", message);
return new HttpEventListenerFactory(callId, System.nanoTime());
}
};
#Override
public void responseBodyEnd(Call call, long byteCount) {
// this method never gets called
// byteCount here is always 0
printEvent("Response body end", callId);
}

How to create a call adapter for suspending functions in Retrofit?

I need to create a retrofit call adapter which can handle such network calls:
#GET("user")
suspend fun getUser(): MyResponseWrapper<User>
I want it to work with Kotlin Coroutines without using Deferred. I have already have a successful implementation using Deferred, which can handle methods such as:
#GET("user")
fun getUser(): Deferred<MyResponseWrapper<User>>
But I want the ability make the function a suspending function and remove the Deferred wrapper.
With suspending functions, Retrofit works as if there is a Call wrapper around the return type, so suspend fun getUser(): User is treated as fun getUser(): Call<User>
My Implementation
I have tried to create a call adapter which tries to handle this. Here is my implementation so far:
Factory
class MyWrapperAdapterFactory : CallAdapter.Factory() {
override fun get(returnType: Type, annotations: Array<Annotation>, retrofit: Retrofit): CallAdapter<*, *>? {
val rawType = getRawType(returnType)
if (rawType == Call::class.java) {
returnType as? ParameterizedType
?: throw IllegalStateException("$returnType must be parameterized")
val containerType = getParameterUpperBound(0, returnType)
if (getRawType(containerType) != MyWrapper::class.java) {
return null
}
containerType as? ParameterizedType
?: throw IllegalStateException("MyWrapper must be parameterized")
val successBodyType = getParameterUpperBound(0, containerType)
val errorBodyType = getParameterUpperBound(1, containerType)
val errorBodyConverter = retrofit.nextResponseBodyConverter<Any>(
null,
errorBodyType,
annotations
)
return MyWrapperAdapter<Any, Any>(successBodyType, errorBodyConverter)
}
return null
}
Adapter
class MyWrapperAdapter<T : Any>(
private val successBodyType: Type
) : CallAdapter<T, MyWrapper<T>> {
override fun adapt(call: Call<T>): MyWrapper<T> {
return try {
call.execute().toMyWrapper<T>()
} catch (e: IOException) {
e.toNetworkErrorWrapper()
}
}
override fun responseType(): Type = successBodyType
}
runBlocking {
val user: MyWrapper<User> = service.getUser()
}
Everything works as expected using this implementation, but just before the result of the network call is delivered to the user variable, I get the following error:
java.lang.ClassCastException: com.myproject.MyWrapper cannot be cast to retrofit2.Call
at retrofit2.HttpServiceMethod$SuspendForBody.adapt(HttpServiceMethod.java:185)
at retrofit2.HttpServiceMethod.invoke(HttpServiceMethod.java:132)
at retrofit2.Retrofit$1.invoke(Retrofit.java:149)
at com.sun.proxy.$Proxy6.getText(Unknown Source)
...
From Retrofit's source, here is the piece of code at HttpServiceMethod.java:185:
#Override protected Object adapt(Call<ResponseT> call, Object[] args) {
call = callAdapter.adapt(call); // ERROR OCCURS HERE
//noinspection unchecked Checked by reflection inside RequestFactory.
Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1];
return isNullable
? KotlinExtensions.awaitNullable(call, continuation)
: KotlinExtensions.await(call, continuation);
}
I'm not sure how to handle this error. Is there a way to fix?
Here is a working example of an adapter, which automatically wraps a response to the Result wrapper. A GitHub sample is also available.
// build.gradle
...
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.6.1'
implementation 'com.squareup.retrofit2:converter-gson:2.6.1'
implementation 'com.google.code.gson:gson:2.8.5'
}
// test.kt
...
sealed class Result<out T> {
data class Success<T>(val data: T?) : Result<T>()
data class Failure(val statusCode: Int?) : Result<Nothing>()
object NetworkError : Result<Nothing>()
}
data class Bar(
#SerializedName("foo")
val foo: String
)
interface Service {
#GET("bar")
suspend fun getBar(): Result<Bar>
#GET("bars")
suspend fun getBars(): Result<List<Bar>>
}
abstract class CallDelegate<TIn, TOut>(
protected val proxy: Call<TIn>
) : Call<TOut> {
override fun execute(): Response<TOut> = throw NotImplementedError()
override final fun enqueue(callback: Callback<TOut>) = enqueueImpl(callback)
override final fun clone(): Call<TOut> = cloneImpl()
override fun cancel() = proxy.cancel()
override fun request(): Request = proxy.request()
override fun isExecuted() = proxy.isExecuted
override fun isCanceled() = proxy.isCanceled
abstract fun enqueueImpl(callback: Callback<TOut>)
abstract fun cloneImpl(): Call<TOut>
}
class ResultCall<T>(proxy: Call<T>) : CallDelegate<T, Result<T>>(proxy) {
override fun enqueueImpl(callback: Callback<Result<T>>) = proxy.enqueue(object: Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {
val code = response.code()
val result = if (code in 200 until 300) {
val body = response.body()
Result.Success(body)
} else {
Result.Failure(code)
}
callback.onResponse(this#ResultCall, Response.success(result))
}
override fun onFailure(call: Call<T>, t: Throwable) {
val result = if (t is IOException) {
Result.NetworkError
} else {
Result.Failure(null)
}
callback.onResponse(this#ResultCall, Response.success(result))
}
})
override fun cloneImpl() = ResultCall(proxy.clone())
}
class ResultAdapter(
private val type: Type
): CallAdapter<Type, Call<Result<Type>>> {
override fun responseType() = type
override fun adapt(call: Call<Type>): Call<Result<Type>> = ResultCall(call)
}
class MyCallAdapterFactory : CallAdapter.Factory() {
override fun get(
returnType: Type,
annotations: Array<Annotation>,
retrofit: Retrofit
) = when (getRawType(returnType)) {
Call::class.java -> {
val callType = getParameterUpperBound(0, returnType as ParameterizedType)
when (getRawType(callType)) {
Result::class.java -> {
val resultType = getParameterUpperBound(0, callType as ParameterizedType)
ResultAdapter(resultType)
}
else -> null
}
}
else -> null
}
}
/**
* A Mock interceptor that returns a test data
*/
class MockInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
val response = when (chain.request().url().encodedPath()) {
"/bar" -> """{"foo":"baz"}"""
"/bars" -> """[{"foo":"baz1"},{"foo":"baz2"}]"""
else -> throw Error("unknown request")
}
val mediaType = MediaType.parse("application/json")
val responseBody = ResponseBody.create(mediaType, response)
return okhttp3.Response.Builder()
.protocol(Protocol.HTTP_1_0)
.request(chain.request())
.code(200)
.message("")
.body(responseBody)
.build()
}
}
suspend fun test() {
val mockInterceptor = MockInterceptor()
val mockClient = OkHttpClient.Builder()
.addInterceptor(mockInterceptor)
.build()
val retrofit = Retrofit.Builder()
.baseUrl("https://mock.com/")
.client(mockClient)
.addCallAdapterFactory(MyCallAdapterFactory())
.addConverterFactory(GsonConverterFactory.create())
.build()
val service = retrofit.create(Service::class.java)
val bar = service.getBar()
val bars = service.getBars()
...
}
...
When you use Retrofit 2.6.0 with coroutines you don't need a wrapper anymore. It should look like below:
#GET("user")
suspend fun getUser(): User
You don't need MyResponseWrapper anymore, and when you call it, it should look like
runBlocking {
val user: User = service.getUser()
}
To get the retrofit Response you can do the following:
#GET("user")
suspend fun getUser(): Response<User>
You also don't need the MyWrapperAdapterFactory or the MyWrapperAdapter.
Hope this answered your question!
Edit
CommonsWare# has also mentioned this in the comments above
Edit
Handling error could be as follow:
sealed class ApiResponse<T> {
companion object {
fun <T> create(response: Response<T>): ApiResponse<T> {
return if(response.isSuccessful) {
val body = response.body()
// Empty body
if (body == null || response.code() == 204) {
ApiSuccessEmptyResponse()
} else {
ApiSuccessResponse(body)
}
} else {
val msg = response.errorBody()?.string()
val errorMessage = if(msg.isNullOrEmpty()) {
response.message()
} else {
msg
}
ApiErrorResponse(errorMessage ?: "Unknown error")
}
}
}
}
class ApiSuccessResponse<T>(val data: T): ApiResponse<T>()
class ApiSuccessEmptyResponse<T>: ApiResponse<T>()
class ApiErrorResponse<T>(val errorMessage: String): ApiResponse<T>()
Where you just need to call create with the response as ApiResponse.create(response) and it should return correct type. A more advanced scenario could be added here as well, by parsing the error if it is not just a plain string.
This question came up in the pull request where suspend was introduced to Retrofit.
matejdro: From what I see, this MR completely bypasses call adapters when using suspend functions. I'm currently using custom call adapters for centralising parsing of error body (and then throwing appropriate exceptions), smilarly to the official retrofit2 sample. Any chance we get alternative to this, some kind of adapter that is injected between here?
It turns out this is not supported (yet?).
Source: https://github.com/square/retrofit/pull/2886#issuecomment-438936312
For error handling I went for something like this to invoke api calls:
suspend fun <T : Any> safeApiCall(call: suspend () -> Response<T>): MyWrapper<T> {
return try {
val response = call.invoke()
when (response.code()) {
// return MyWrapper based on response code
// MyWrapper is sealed class with subclasses Success and Failure
}
} catch (error: Throwable) {
Failure(error)
}
}

Categories

Resources