Hi I am getting an Unresolved reference Enqueue message and running out of options to resolve.
The Callback is made in a button setOnCLickListener in a Fragment.
Other suggestions have been to set the Gradle version to 3.1.3 (previously using 3.6.1) but the error persists.
btnSearch.setOnClickListener {
val service = RetrofitClientInstance.retrofitInstance?.create(GetFlightService::class.java)
val call = service?.getFlightData(
"QF",
"642",
"departing",
"2020",
"05",
"09",
"FS",
"xxxxxxxxx",
"xxxxxxxxxxxxxxxxxxxxxxxxxxx") //appID and appKey x'd out :)
call?.enqueue(object : Callback<RetrievedFlightData>{
override fun onResponse(
call: Call<RetrievedFlightData>,
response: Response<RetrievedFlightData>
) {
if (response.code() == 200) {
val flightDataBody = response.body()!!
val flightData: FlightDTO = getFlightDTOData(flightDataBody)
}
}
override fun onFailure(call: Call<RetrievedFlightData>, t: Throwable) {
TODO("Not yet implemented")
Toast.makeText(activity,"Error: Callback faliure - ${t.message}", Toast.LENGTH_LONG).show()
}
})
}
Interface is:
interface GetFlightService {
#GET ("/flex/schedules/rest/v1/json/flight/")
fun getFlightData(#Query("carrierFSCode") carrierFSCode: String,
#Query("flightNumber")flightNumber:String,
#Query("departing") departing:String,
#Query("year") year:String,
#Query("month") month:String,
#Query("day") day:String,
#Query("codeType") codeType: String,
#Query("appID") appID: String,
#Query("appKey") appKey:String
)
}
and ClientInstance
object RetrofitClientInstance {
private var retrofit: Retrofit? = null
private const val BASE_URL = "https://api.flightstats.com"
//create a retrofit instance, only if it has not been created yet.
val retrofitInstance: Retrofit?
get() {
if (retrofit == null){
retrofit = retrofit2.Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
return retrofit
}
}
The Client instance works as I have made other calls that work OK.
Another issue is that I need to wait for the response before I proceed. I have previously used newSingleThreadExecutor on another call but I understand this is not best practice. Any help on this would also be appreciated.
thanks
joncb
Change your getFlightData() to return Call<RetrievedFlightData>. So it becomes:
#GET ("/flex/schedules/rest/v1/json/flight/")
fun getFlightData(#Query("carrierFSCode") carrierFSCode: String,
#Query("flightNumber")flightNumber:String,
#Query("departing") departing:String,
#Query("year") year:String,
#Query("month") month:String,
#Query("day") day:String,
#Query("codeType") codeType: String,
#Query("appID") appID: String,
#Query("appKey") appKey:String
): Call<RetrievedFlightData>
Related
I am trying to make an app that reads data from the firebase firestore and then shows in a screen that same data
The problem the data only appears when shown in log but i want the in text(string)
Can anyone help me understand how to do it
I already tried many videos explaining but none of then work so my last option is really ask in here for help.
Here is the code
#SuppressLint("UnrememberedMutableState")
#Composable
fun DB () {
val db = Firebase.firestore
val collectionReference = db.collection("Inventário")
.document("Bloco E")
.collection("Sala E0.05")
val data = mutableStateOf(mapOf<String, Any>())
val job = remember { Job() }
remember {
GlobalScope.launch(Dispatchers.Main) {
val documentSnapshot = collectionReference.document("Computador").get().await()
try {
data.value = documentSnapshot.data ?: mapOf()
}catch (e: Exception){
Log.e("Firestore", "Error retrieving data", e)
}
}
}
Column() {
data.value.forEach { (key, value) ->
Text("$key: $value")}
}
}
And here is the database structure:
It seems like you forgot to use remember on your MutableState (and also suppressed the lint warning with (#SuppressLint("UnrememberedMutableState"))):
val (data, setData) = remember { mutableStateOf(mapOf<String, Any>()) }
Also, using GlobalScope in Android is not recommended. Consider using a LaunchedEffect instead:
#Composable
fun DB () {
val db = Firebase.firestore
val collectionReference = db.collection("Inventário")
.document("Bloco E")
.collection("Sala E0.05")
val (data, setData) = remember { mutableStateOf(mapOf<String, Any>()) }
LaunchedEffect(collectionReference) {
try {
val documentSnapshot = collectionReference.document("Computador").get().await()
setData(documentSnapshot.data ?: mapOf())
} catch (e: Exception) {
Log.e("Firestore", "Error retrieving data", e)
}
}
Column() {
data.value.forEach { (key, value) ->
Text("$key: $value")}
}
}
firebase method is working on worker thread automatically. but I have used coroutine and callbackflow to implement firebase listener code synchronously or get return from the listener.
below is my code that I explained
coroutine await with firebase for one shot
override suspend fun checkNickName(nickName: String): Results<Int> {
lateinit var result : Results<Int>
fireStore.collection("database")
.document("user")
.get()
.addOnCompleteListener { document ->
if (document.isSuccessful) {
val list = document.result.data?.get("nickNameList") as List<String>
if (list.contains(nickName))
result = Results.Exist(1)
else
result = Results.No(0)
//document.getResult().get("nickNameList")
}
else {
}
}.await()
return result
}
callbackflow with firebase listener
override fun getOwnUser(): Flow<UserEntity> = callbackFlow{
val document = fireStore.collection("database/user/userList/")
.document("test!!!!!")
val subscription = document.addSnapshotListener { snapshot,_ ->
if (snapshot!!.exists()) {
val ownUser = snapshot.toObject<UserEntity>()
if (ownUser != null) {
trySend(ownUser)
}
}
}
awaitClose { subscription.remove() }
}
so I really wonder these way is good or bad practice and its reason
Do not combine addOnCompleteListener with coroutines await(). There is no guarantee that the listener gets called before or after await(), so it is possible the code in the listener won't be called until after the whole suspend function returns. Also, one of the major reasons to use coroutines in the first place is to avoid using callbacks. So your first function should look like:
override suspend fun checkNickName(nickName: String): Results<Int> {
try {
val userList = fireStore.collection("database")
.document("user")
.get()
.await()
.get("nickNameList") as List<String>
return if (userList.contains(nickName)) Results.Exist(1) else Results.No(0)
} catch (e: Exception) {
// return a failure result here
}
}
Your use of callbackFlow looks fine, except you should add a buffer() call to the flow you're returning so you can specify how to handle backpressure. However, it's possible you will want to handle that downstream instead.
override fun getOwnUser(): Flow<UserEntity> = callbackFlow {
//...
}.buffer(/* Customize backpressure behavior here */)
I'm trying to run a Firebase Transaction under a suspended function in Kotlin and i see no documentation about it.
I'm using
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.5.2'
for coroutines with firebase (eg: setValue(*).await() ) but there seems to be no await function for runTransaction(*)
override suspend fun modifyProductStock(
product: ProductModel,
valueToModify: Long,
replace: Boolean
) {
CoroutineScope(Dispatchers.Main).launch {
val restaurantId = authRepository.restaurantId.value ?: throw Exception("No restaurant!")
val productId = product.id ?: throw Exception("No Product ID!")
val reference = FirebaseDatabase.getInstance().getReference("database/$restaurantId").child("products")
if (replace) {
reference.child(productId).child("stock").setValue(valueToModify).await()
} else {
reference.child(productId).child("stock")
.runTransaction(object : Transaction.Handler {
override fun doTransaction(p0: MutableData): Transaction.Result {
//any operation
return Transaction.success(p0)
}
override fun onComplete(p0: DatabaseError?, p1: Boolean, p2: DataSnapshot?) {
}
})
}
}
}
You could wrap it in suspendCoroutine:
val result: DataSnapshot? = suspendCoroutine { c ->
reference.child(productId).child("stock")
.runTransaction(object : Transaction.Handler {
override fun doTransaction(p0: MutableData): Transaction.Result {
//any operation
return Transaction.success(p0)
}
override fun onComplete(error: DatabaseError?, p1: Boolean, snapshot: DataSnapshot?) {
c.resume(snapshot)
}
})
}
suspendCoroutine
Obtains the current continuation instance inside suspend functions and suspends the currently running coroutine.
In this function both Continuation.resume and Continuation.resumeWithException can be used either synchronously in the same stack-frame where the suspension function is run or asynchronously later in the same thread or from a different thread of execution.
Given that the Kotlin example in the Firebase documentation on transactions uses the same callback style that you have, it seems indeed that there is no specific support for co-routines there.
It might be worth posting an issue on the Android SDK repo to get it added, or hear why it wasn't added.
I am recently working with Flow in my retrofit's repository.
Sealed class for Result
enum class ApiStatus{
SUCCESS,
ERROR,
LOADING
}
sealed class ApiResult <out T> (val status: ApiStatus, val data: T?, val message:String?) {
data class Success<out R>(val _data: R?): ApiResult<R>(
status = ApiStatus.SUCCESS,
data = _data,
message = null
)
data class Error(val exception: String): ApiResult<Nothing>(
status = ApiStatus.ERROR,
data = null,
message = exception
)
data class Loading<out R>(val _data: R?, val isLoading: Boolean): ApiResult<R>(
status = ApiStatus.LOADING,
data = _data,
message = null
)
}
Example repository call for 3 state - Loading, Error, Success
fun googleDisconnect() = flow {
emit(ApiResult.Loading(null, true))
val call = userDataSource.self("v4").googleDisconnect()
if(call.isSuccessful) {
emit(ApiResult.Success(call.body()))
} else {
emit(ApiResult.Error("Google Disconnect Failed"))
}
}
However, I have multiple network call with different function in my repository. Is there any idea to write a generic function for these flow so that these flow can be emitted to the flow builder?
My attempt but problem is How can I pass suspend function into the function?
Finally I got myself the answer. I wonder if this will helps but I will post out my answer.
fun <T> toResultFlow(call: suspend () -> Response<T>?) : Flow<ApiResult<T>?> {
return flow {
emit(ApiResult.Loading())
val c = call() <-- have to initialize the call method first
c?.let {
try{
if(c.isSuccessful) {
c.body()?.let {
emit(ApiResult.Success(it))
}
} else {
c.errorBody()?.let {
val error = it.string()
it.close()
emit(ApiResult.Error(error))
}
}
}catch (e: Exception) {
emit(ApiResult.Error(e.toString()))
}
}
}.flowOn(Dispatchers.IO)
}
Then, pass in your suspend function as lambda
fun googleDisconnect() = toResultFlow {
userDataSource.self("v4").googleDisconnect()
}
Finally, the toResultFlow will be return Flow<ApiResult> and T is your preferred datatype! Volla!
I need your help since I'm struggling, I want to make some HTTP GET requests which allow me to retrieve each JSON object one by one in order to store them in a List which I will then put in an adapter. I intend to do it by incrementing idPlayer at each loop in the URL but given how the retrofit library was designed I wonder if this is possible.
Thx :)
fun getplayer() {
var instancePlayer = InstancePlayer()
var playerApi = instancePlayer.getPlayerAPI()
var call = playerApi?.getPlayer("/api/v1/players/" + idPlayer)
if (call != null) {
call.enqueue(object : Callback<Player?> {
override fun onResponse(call: Call<Player?>, response: Response<Player?>) {
var responseBody = response.body()!!
var p1: Player = Player()
p1.id = responseBody.id
p1.first_name = responseBody.first_name
p1.last_name = responseBody.last_name
playerAdapter.setData(playerList)
recyclerView?.setAdapter(playerAdapter)
playerList.add(p1)
}
override fun onFailure(call: Call<Player?>, t: Throwable) {
TODO("Not yet implemented")
}
})
}
}