Created
August 15, 2023 05:53
-
-
Save rickyManalo/0fd37b4c23efdddd28c3e36d787f99a4 to your computer and use it in GitHub Desktop.
Retrofit API call and Room update helper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Handles calling the Retrofit endpoints | |
* [apiCallFunc] is the Retrofit endpoint you want to call | |
* During runtime, [V] would be equal to the type wrapped by [Response] | |
* [V] is returned as the reflection would already be executed during that time | |
* giving us the contents of the inferred [V] | |
*/ | |
suspend fun <V> callAPI(apiCallFunc: suspend () -> Response<V>): V? { | |
val actsResult: Response<V>? | |
if (utils.isInternetAvailable(ctx)) { | |
try { | |
actsResult = apiCallFunc.invoke() | |
val respBody = retrofitErrorHandler(actsResult) | |
if (respBody != null) { | |
return respBody | |
} | |
} catch (e: Exception) { | |
Log.e(TAG, "callAPI Error: ${e.localizedMessage}") | |
withContext(Dispatchers.Main) { | |
Toast.makeText(ctx, "Retrofit Error: ${e.localizedMessage}", Toast.LENGTH_LONG) | |
.show() | |
} | |
} | |
} | |
return null | |
} | |
/** | |
* Handles the checking of the diff between the server and local data, and puts those diff in the Room database | |
* [vm] is the ViewModel handling the local data | |
* [inAPIData] is the list of items that came from the server, which was done partially by [callAPI] | |
* [inLocalDBData] is the local data | |
* [T] would be inferred from the data passed | |
*/ | |
suspend fun <T> updateInfoLst( | |
vm: IViewModel<T>, | |
inAPIData: List<T>, | |
inLocalDBData: List<T> | |
): List<T> { | |
val toInsertLst: List<T> = if (inAPIData.size > inLocalDBData.size) { | |
val diffLst = | |
inAPIData.slice((if (inLocalDBData.size > 1) inLocalDBData.size - 1 else 0) until inAPIData.size) | |
for (item in diffLst) { | |
val id = vm.insertReturnId(item) | |
Log.i(TAG, "id: $id") | |
} | |
withContext(Dispatchers.Main) { | |
Toast.makeText(ctx, "$caller: Using server data", Toast.LENGTH_LONG).show() | |
Log.i(TAG, "using server data") | |
} | |
diffLst | |
} else { | |
//update the local db with the server's data | |
for (item in inAPIData) { | |
vm.update(item) | |
} | |
withContext(Dispatchers.Main) { | |
Toast.makeText(ctx, "$caller: Using local data", Toast.LENGTH_LONG).show() | |
Log.i(TAG, "using local data") | |
} | |
//to not need to wait for the DB, which would eventually be the same as inAPIData, | |
//I just returned inAPIData | |
inAPIData | |
} | |
//this data is usually used as the list for the adapter that needs the fetched server data | |
return toInsertLst | |
} | |
private fun <T> retrofitErrorHandler(res: Response<T>): T { | |
if (res.isSuccessful) { | |
Log.d(TAG, "retrofitErrorHandler success: ${res.body()}") | |
return res.body()!! | |
} else { | |
Log.d(TAG, "retrofitErrorHandler errMsg: ${res.errorBody().toString()}") | |
throw Exception(res.errorBody().toString()) | |
} | |
} | |
fun getHeaderMap(ctx: Context): Map<String, String> { | |
//what this function does, at least in my use case, is get the "access_token" I saved on a sharedPreferences | |
//and use it for the header on my Retrofit calls that need it | |
val accessToken = PreferenceHelper.customPrefs(ctx, "project_prefs")["access_token", ""] | |
if (accessToken == "") { | |
return mapOf() | |
} | |
val headerMap = mutableMapOf<String, String>() | |
headerMap["access_token"] = accessToken | |
return headerMap | |
} | |
Examples: | |
//inside a Fragment/Activity, put these lines | |
private lateinit var retrofitIntf: RetrofitInterface | |
private lateinit var retFetchHlpr: RetrofitFetchHelper | |
//RetrofitHelper is the class that handles the creation of a Retrofit instance | |
//RetrofitInterface contains all the endpoint declarations | |
atlAPI = RetrofitHelper(ctx).getInstance().create(RetrofitInterface::class.java) | |
retFetchHlpr = RetrofitFetchHelper(ctx, "I put the fragment or activity name here just for logging purposes") | |
//this is a sample endpoint | |
@Headers("Content-Type: application/json") | |
@POST("/order") | |
suspend fun postOrder( | |
@HeaderMap headers: Map<String, String>, | |
@Body data: Order | |
): Response<OrderPostResponse> | |
//and the response declaration | |
data class OrderPostResponse( | |
val order_post_response: OrderResponse | |
) | |
//then below is how you'll use the helper. quite neat, right? | |
val inAPIDataResp = retFetchHlpr.callAPI { | |
retrofitIntf.postOrder(retFetchHlpr.getHeaderMap(ctx), someOrderObj) | |
} | |
var inAPIData = listOf<OrderResponse>() | |
if (inAPIDataResp != null) { | |
inAPIData = inAPIDataResp.order_post_response | |
} | |
if (inAPIData != null && inAPIData.isNotEmpty()) { | |
withContext(Dispatchers.Main) { | |
itemLst.clear() | |
itemLst.addAll(inAPIData) | |
adapter.notifyDataSetChanged() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment