So my goal like the the title says is to pass API response results throw an onSuccess.run() method to a fragment. Just to give some context, I start to do the method like this in a Manager class for example:
override fun callUser(onSuccess:Runnable, onFailure:Runnable){
NetworkManager.instance.performCall(NetworkManager.REGISTRATION.verifyUser(id),
object : NetworkManager.OnRequestCallback<UserInfoResponse> {
override fun onSuccess(body: UserInfoResponse?) {
body?.data?.let {
onSuccess.run()
}
}
override fun onError(errorBody: String?) {
onFailure.run()
}
})
}
Then I go to a fragment and call the method above like this:
objectManager.callVerifyAdvisor(
Runnable {[On Success stuff },
Runnable {[On Error]}
}
The problem is that, although I can decide in the fragment the actions I want to do in the onSuccess() and onFailure() methods, I cant get the API Results to that fragment by doing this way.
So my ideia is to do something like [I used comments to specify the sections that matter]:
NetworkManager.instance.performCall(NetworkManager.REGISTRATION.verifyUser(id),
object : NetworkManager.OnRequestCallback<UserInfoResponse> {
override fun onSuccess(body: UserInfoResponse?) {
body?.data?.let {
it.userName // I get this from api response
onSuccess.run()
}
}
override fun onError(errorBody: String?) {
onFailure.run()
}
})
}
Then on my fragment I want something like this:
objectManager.callVerifyAdvisor(
Runnable {[On Success stuff }, //receive here the it.userName
Runnable {[On Error]}
}
Can someone give any ideia how to do this? Side note -> I put kotlin on the tag because this has some kind of functional stuff.
You just need to replace you onSuccess from Runnable to your custom functional type callback:
override fun callUser(onSuccess: (String) -> Unit, onFailure: Runnable) {...
Then pass userName to callback:
NetworkManager.instance.performCall(NetworkManager.REGISTRATION.verifyUser(id),
object : NetworkManager.OnRequestCallback<UserInfoResponse> {
override fun onSuccess(body: UserInfoResponse?) {
body?.data?.let {
onSuccess.invoke(it.userName) // Pass userName
}
}
override fun onError(errorBody: String?) {
onFailure.run()
}
})
}
And then you can get userName from this callback:
objectManager.callVerifyAdvisor(
{userName -> } // Here you get your result
Runnable {[On Error]}
}
Related
I'm new to kotlin coroutines. I've been trying to run multiple API calls in parallel and then when all the calls are done update my UI and dismiss the loader, but with no success. This is my code
private fun getScoreForType() {
val job = CoroutineScope(Dispatchers.IO).launch {
types.forEach { type ->
getScore(type)
}
}
runBlocking {
job.join()
// do some ui work
dismissLoader()
}
}
private fun getScore(type: String) {
val call = MyApi.getScores(type)
call.enqueue(object : Callback<Score> {
override fun onResponse(call: Call<Score>, response: Response<Score>) {
setScore(response)
}
override fun onFailure(call: Call<Score>, t: Throwable) {
}
})
}
I've also tried using async and awaitAll but couldn't make it work either. The loader is always dismissed before all the calls are done. Any help on how I could make this work would be much appreciated
Use Flow and collectData it will works as LiveData.
For example:
val myIntFlow = MutableStateFlow(-1)
Try something like;
in ViewModelMethods
private fun getScoreForType() {
It goes first:
CoroutineScope(Dispatchers.IO).launch {
types.forEach { type ->
getScore(type)
}
// it means to change value of flow
myIntFlow.value = 1
}
// Now collect data in fragment to change UI
}
// in fragment like:
CoroutineScope(Dispatchers.Main).launch {
// flow will be triggered, on every changed value
viewModel.myIntFlow.collect {
viewModel.methodFromViewModelToChangeUI()
dissmisloader()
myIntFlow.value = -1
}
}
// try the same here as you wish
private fun getScore(type: String) {
val call = MyApi.getScores(type)
call.enqueue(object : Callback<Score> {
override fun onResponse(call: Call<Score>, response: Response<Score>) {
setScore(response)
}
override fun onFailure(call: Call<Score>, t: Throwable) {
}
})
}
I am using Stripe library which provides me with custom callback functionality.
I want a custom callback convert to Kotlin coroutine
Here is the code
override fun retrievePaymentIntent(clientSecret: String): Flow<Resource<PaymentIntent>> = flow{
emit(Resource.Loading())
Terminal.getInstance().retrievePaymentIntent(clientSecret,
object : PaymentIntentCallback {
override fun onFailure(e: TerminalException) {}
override fun onSuccess(paymentIntent: PaymentIntent) {
emit(Resource.Success(paymentIntent))
}
})
}
The problem is I can't call emit function inside onSuccess/onFailure. The error shown in the picture.
Is it possible to change something here to make it work or how could I convert custom callback to coroutine?
You can use suspendCancellableCoroutine to model your callback-based one-shot request like so:
suspend fun retrievePaymentIntent(clientSecret: String): PaymentIntent =
suspendCancellableCoroutine { continuation ->
Terminal.getInstance().retrievePaymentIntent(clientSecret,
object : PaymentIntentCallback {
override fun onFailure(e: TerminalException)
{
continuation.resumeWithException(e)
}
override fun onSuccess(paymentIntent: PaymentIntent)
{
continuation.resume(paymentIntent)
}
})
continuation.invokeOnCancellation { /*cancel the payment intent retrieval if possible*/ }
}
Android Studio 3.6
My custom callback interface:
interface RecoveryPasswordConfirmCodeCallback {
fun onSuccess()
fun onError(ex: Throwable?)
}
Use:
val result = TransportService.recoverPasswordConfirmCode(
confirmCode,
ex,
object : RecoveryPasswordConfirmCodeCallback {
override fun onSuccess() {
}
override fun onError(ex: Throwable?) {
if (ex is InvalidOtpException) {
toastMessage.value = SingleEvent(
getApplication<Application>().applicationContext.getString(
R.string.incorrect_confirm_code
)
)
} else {
toastMessage.value = SingleEvent(
getApplication<Application>().applicationContext.getString(
R.string.default_error_message
))
}
}
})
fun recoverPasswordConfirmCode(
confirmCode: String,
ex: NeedTfaException,
callBack: RecoveryPasswordConfirmCodeCallback
) {
//some code here
}
Nice. It's work fine. But... is it possible to replace my custom callback interface by Kotlin's coroutine. I don't want to create custom interface only for execute method recoverPasswordConfirmCode
You can convert recoverPasswordConfirmCode() to a suspend function and return the result in the form of a sealed class to indicate if it's an error or the valid response. Something like this:
// Generic response class
sealed class Response<out T>{
data class Error(val ex: Throwable) : Response<Nothing>()
data class Data<T>(val data: T) : Response<T>()
}
// in your TransportService class
suspend fun recoverPasswordConfirmCode(confirmCode, ex): Response<RecoverPasswordResponse>{
// Do your stuff here
// return Response.Data<RecoverPasswordResponse>(/* your data object here */)
}
Then call it like this and check the response type:
val result = TransportService.recoverPasswordConfirmCode(confirmCode, ex)
when(result){
is Response.Error -> // Do something
is Response.Data -> // Do something
}
Note that you will have to call the suspend function inside a coroutine context.
You don't need to create a custom interface. Consume your API like this:
suspend fun recoverPasswordConfirmCode(confirmCode: String): YourReturnType = suspendCancellableCoroutine { cont ->
try {
val result = //Do your blocking API calls here
if(result.code == confirmCode) //Check confirm code is correct
cont.resume(YourResult) //Return your result here
else
cont.resumeWithException(YourException) //Throw an exception otherwise
} catch (e: Exception) {
cont.resumeWithException(e)
}
}
Call recoverPasswordConfirmCode method inside a Coroutine Scope.
I'm using RxJava and I know about concat, and I guess it does fit to me, because I want to finish first all of first call and then do the second one but I don't know how to implement it.
I have this from now :
private fun assignAllAnswersToQuestion(questionId: Long) {
answerListCreated.forEach { assignAnswerToQuestion(questionId, it.id) }
}
private fun assignAnswerToQuestion(questionId: Long, answerId: Long) {
disposable = questionService.addAnswerToQuestion(questionId,answerId,MyUtils.getAccessTokenFromLocalStorage(context = this))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
result -> //Do nothing it should call the next one
},
{ error -> toast(error.message.toString())}
)
}
But then, once this is finished all of this forEach I'd like to do something like this :
private fun assignAllAnswersToQuestion(questionId: Long) {
answerListCreated.forEach { assignAnswerToQuestion(questionId, it.id)
anotherCallHere(questionId) //Do it when the first forEach is finished!!
}
Any idea?
Also, is a way to do it with coroutines this?
I think you have to .map your list (answerListCreated) to a list of Flowables, and then use Flowable.zip on this list.
zip is used to combine the results of the Flowables into a single result. Since you don't need these results we ignore them.
After zip you are sure that all previous Flowables ended, and you can .flatMap to execute your next call (assuming anotherCallHere returns a Flowable.
In the end, it will be something like:
val flowableList = answerListCreated.map { assignAnswerToQuestion(questionId, it.id) }
disposable = Flowable.zip(flowableList) { /* Ignoring results */ }
.flatMap { anotherCallHere(questionId) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
// ...
}
It should be noted that if any of the calls fails, the whole chain will fail (onError will be called).
I'm new to coroutines but I think I can answer for them:
You can use coroutines runBlocking {} for this.
private fun assignAllAnswersToQuestion(questionId: Long) = launch {
runBlocking {
answerListCreated.forEach { assignAnswerToQuestion(questionId, it.id) }
}
anotherCallHere(questionId)
}
private fun assignAnswerToQuestion(questionId: Long, answerId: Long) = launch (Dispatchers.IO) {
questionService.addAnswerToQuestion(
questionId,
answerId,
MyUtils.getAccessTokenFromLocalStorage(context = this)
)
}
launch {} returns a Job object which becomes a child job of the parent coroutine. runBlocking {} will block until all its child jobs have finished, (an alternative is to use launch {}.join() which will have the same affect).
Note that I have made both functions wrap their code in a launch {} block.
To be able to call launch {} like this, you will likely want to make your class implement CoroutineScope
class MyActivityOrFragment: Activity(), CoroutineScope {
lateinit var job = SupervisorJob()
private val exceptionHandler =
CoroutineExceptionHandler { _, error ->
toast(error.message.toString()
}
override val coroutineContext = Dispatchers.Main + job + exceptionHandler
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
job = Job()
}
override fun onDestroy() {
super.onDestroy()
job.cancel()
}
...
}
I am new to using rxjava and I am trying to run a function in background using rxjava2 but the method is not called the code I am using is given below let me know if its the right way to execute a function in background:
Observable.fromCallable<OrderItem>(Callable {
saveToDb(existingQty, newOty, product_id)
}).doOnSubscribe {
object : Observable<OrderItem>() {
override fun subscribeActual(emitter: Observer<in OrderItem>?) {
try {
val orderItem = saveToDb(existingQty, newOty, product_id)
emitter?.onNext(orderItem)
emitter?.onComplete()
} catch (e: Exception) {
emitter?.onError(e)
}
}
}
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).doOnSubscribe {
object : Observer<OrderItem> {
override fun onComplete() {
}
override fun onNext(t: OrderItem) {
}
override fun onError(e: Throwable) {
}
override fun onSubscribe(d: Disposable) {
}
}
}
You are dong it wrong way. doOnSubscribe() operator is called when observable is subscribed using subscribe() method and you haven't subscribed the observable using subscribe() method.
You have called saveToDb method in callable, then why are you calling it in doOnSubscribe? it doesn't make sense.
You should have written following code:
Observable.fromCallable { saveToDb(existingQty, newOty, product_id) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ orderItem ->
// set values to UI
}, { e ->
// handle exception if any
}, {
// on complete
})
to work with your logic.
DoOnSubscribe means "do when someone subscribe to it". But there is no subscribe in your code. Maybe you want to use subsribe instead of doOnSubscribe