Skip to content

Instantly share code, notes, and snippets.

@rickyManalo
Created August 15, 2023 05:53
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 rickyManalo/0fd37b4c23efdddd28c3e36d787f99a4 to your computer and use it in GitHub Desktop.
Save rickyManalo/0fd37b4c23efdddd28c3e36d787f99a4 to your computer and use it in GitHub Desktop.
Retrofit API call and Room update helper
/**
* 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