I'm having trouble handling error in the following function. I'm basically new to Kotlin. Here's my RevenueCat Login Code and I want to handle ::error in this code:
Purchases.sharedInstance.logInWith(
myUserID,
::error // <- How to handle this? I want to retrieve error Code and Error Message.
)
{ customerInfo, created ->
// Handle Successful login here
}
Here's the code behind the function (within RevenueCat SDK)
#Suppress("unused")
fun Purchases.logInWith(
appUserID: String,
onError: (error: PurchasesError) -> Unit = ON_ERROR_STUB,
onSuccess: (customerInfo: CustomerInfo, created: Boolean) -> Unit
) {
logIn(appUserID, logInSuccessListener(onSuccess, onError))
}
The double colon in ::error is a function reference. It is basically a reference to the function error().
And from your logInWith() function, we have onError: (error: PurchasesError) -> Unit = ON_ERROR_STUB, meaning that the function should take PurchasesError as input parameter and does not need to return.
So we can derive a function as the following:
fun error(error: PurchasesError) {
// And you can do something with the error here
}
I solved it like this:
Purchases.sharedInstance.logInWith(
myUserID,
onError = { error ->
// Handle error here
}
Related
At 'urichecking2' log, I can see there is value. But in 'uriChecking' the uriList is null.
why the uriList.add not work??
private fun getPhotoList() {
val fileName = intent.getStringExtra("fileName")
Log.d("fileNameChecking", "$fileName")
val listRef = FirebaseStorage.getInstance().reference.child("image").child(fileName!!)
var tmpUrl:Uri = Uri.parse(fileName)
Log.d("firstTmpUri","$tmpUrl")
listRef.listAll()
.addOnSuccessListener { listResult ->
for (item in listResult.items) {
item.downloadUrl.addOnCompleteListener { task ->
if (task.isSuccessful) {
tmpUrl = task.result
Log.d("secondTmpUri","$tmpUrl")
Log.d("urichecking2","$task.result")
uriList.add(task.result)
} else {
}
}.addOnFailureListener {
// Uh-oh, an error occurred!
}
}
}
Log.d("thirdTmpUri","$tmpUrl")
Log.d("urichecking", "$uriList")
}
If I do this, the log is output in the order of first, third, and second, and the desired value is in second, but when third comes out, it returns to the value of first.
The listAll method (like most cloud APIs these days, including downloadUrl which you also use) is asynchronous, since it needs to make a call to the server - which may take time. This means the code executes in a different order than you may expect, which is easiest to see if you add some logging:
Log.d("Firebase","Before starting listAll")
listRef.listAll()
.addOnSuccessListener { listResult ->
Log.d("Firebase","Got listResult")
}
Log.d("Firebase","After starting listAll")
When you run this code it outputs:
Before starting listAll
After starting listAll
Got listResult
This is probably not the order you expected, but it perfectly explains why you can't see the list result. By the time your Log.d("urichecking", "$uriList") runs, none of the uriList.add(task.result) has been called yet.
The solution for this is always the same: any code that needs the list result, has to be inside the addOnCompleteListener callback, be called from there, or be otherwise synchronized.
So in its simplest way:
listRef.listAll()
.addOnSuccessListener { listResult ->
for (item in listResult.items) {
item.downloadUrl.addOnCompleteListener { task ->
if (task.isSuccessful) {
uriList.add(task.result)
Log.d("urichecking", "$uriList")
}
}
}
}
This is an incredibly common mistake to make if you're new to programming with asynchronous APIs, so I recommend checking out
Asynchronous programming techniques in the Kotlin language guide
How to get URL from Firebase Storage getDownloadURL
Can someone help me with logic of the firebase on success listener
Why does my function that calls an API or launches a coroutine return an empty or null value?
I'm just learning Mutiny and I need to implement retry logic.
I have this code:
fun main() {
getResult()
.onFailure().invoke { t -> println("Got error: $t") }
.onFailure().retry().atMost(2)
.subscribe().with(
{ result -> println(result) },
{ t -> t.printStackTrace() }
)
}
fun getResult(): Uni<String?> {
println("Preparing result...")
return Uni.createFrom().failure(Exception("Some error happened"))
}
So, the getResult() is a function that may misbehave and needs to be called multiple times on failure.
When I run this program, this is what's happening:
Preparing result...
Got error: java.lang.Exception: Some error happened
Got error: java.lang.Exception: Some error happened
Got error: java.lang.Exception: Some error happened
java.lang.Exception: Some error happened
at MainKt.getResult(Main.kt:16)
at MainKt.main(Main.kt:4)
Obiously, the getResult() function is called only once, while the onFailure() stages actually executed three times.
Is there anything that Mutiny could help me to execute getResult() function on each failure? I sure can implement this with a simple loop, but I feel like Mutiny should already have something like this.
Unfortunately, I didn't find anything suitable in the docs.
Your Uni in getResult is created with an "immediate" item, which is cached and never computed again.
Use Uni.createFrom().failure(() -> Exception("Some error happened"))
In this case, it's a supplier, so it won't be cached but called on every attempt.
So, the right solution for this is actually using the Uni.deferred() method like this:
fun main() {
Uni.createFrom().deferred { getResult() }
.onFailure().invoke { t -> println("Got error: $t") }
.onFailure().retry().atMost(2)
.subscribe().with(
{ result -> println(result) },
{ t -> t.printStackTrace() }
)
}
Thanks to Boris the Spider, who suggested to use the deferred(), and to Clement, who clarified its use with null values.
Initially, I misinterpreted the deferred() documentation thinking it's not allowed to return a null value, but actually it's OK for a Supplier to return a Uni of null:
Uni.createFrom.deferred { Uni.createFrom().nullItem() }
What the docs really are prohibiting is returning a null instead of a Uni:
Uni.createFrom().deferred { null }
I'm trying to implement "Try again" functionality, which means, when some request failed, user will be able to tap on "Try Again" button to resend the same request again.
In short, I have BaseViewModel with
lateinit var pendingMethod: suspend () -> Unit
and
fun runAsync(tryFunction: suspend () -> Unit) {
viewModelScope.launch(errorHandler) {
try {
tryFunction()
} catch (ex: Exception) {
pendingMethod = tryFunction
}
}
}
And from view, when "Try Again" button is clicked, I call
viewModel.runAsync { viewModel.pendingMethod() }
First tap works well, but when I tap second time, it throws
StackOverflow error: stack size 8MB
and bunch of invokeSuspend(..) in the logs, which looks like there are suspend functions call each other infinitely.
Any thoughts about this?
Update:
I have fixed this by storing suspend function in extra variable like this
val temp = viewModel.pendingMethod
viewModel.runAsync { temp() }
Instead of
viewModel.runAsync { viewModel.pendingMethod() }
Your issue can be traced by following points
pendingMethod property is not initialized
tryFunction() call throws kotlin.UninitializedPropertyAccessException
Exception is caught and pendingMethod = tryFunction() which effectivelly means pendingMethod = pendingMethod() // this is recursive call
next time when you call runAsync it essentially calls pendingMethod() but this time its initialized, and since pendingMethod does nothing but call itself, you get the stack overflow error
So how to implement the try again functionality
Lets say you have a suspending function which executes the given request, it can be network call(Retrofit supports suspend keyword) or it can be database access (Room also supports suspend keyword). so your function looks something like
suspend fun executeRequest(request: RequestModel): Result
Now update your ViewModel as
// declare a live data property to notfify user that request has failed
val requestFailed: MutableLiveData<Boolean> = MutableLiveData(false)
// update runAsync
fun runAsync(request: RequestModel) = viewModelScope.launch {
try {
executeRequest(request)
}
catch (ex: Exception) {
requestFailed.postValue(true)
}
}
And in your Activity, Fragment observe the LiveData object to display the error to user
viewModel.requestFailed.observe(this, Observer{
if(it) {
// Show error toast
}
}
I'm new to swift and trying to call a method when the user presses some hotkey shortcut.
I have the following code: https://pastebin.com/pF8wk7jL
I'm trying to run togglePopover on this handler: hotKey.keyDownHandler.
This:
hotKey.keyDownHandler = {
self.togglePopover(_:)
}
returns the error "Expression resolves to an unused function"
and this:
hotKey.keyDownHandler = {
let call = self.togglePopover(_:)
call(_:)
}
returns the error "Cannot find call in scope".
Cannot fix the issue, can someone help?
If I do:
hotKey.keyDownHandler = {
print("teste")
}
works good!
my question is similar to this one How to throw error from RxJS map operator (angular), but I'm on angular6 with rxjs6 and I guess all is changed ;)
I want to know, how I could propagate an Error-Object within map of an observable to the subscribe OnError Part. I always end up in the OnNext Part.
Here is what I have so far:
Within a ng-component I have maybe the following method call
[...]
this.dataStreamService.execCall({ method : 'order_list',params : {}})
.subscribe( r => {
// here r provides the result data from http call
console.log("execCall result", r);
}, err => {
// HERE the "MAP ERROR OCCURED" Error should be occured as well,
// but in doesn't
console.log("execCall error",err);
});
[...]
The called service method looks like:
execCall(dataStreamCall: DataStreamCall): Observable<DataStreamResult> {
let apiURL = '<some API-URL>';
let params = dataStreamCall.params;
// do HTTP request (this.http calls an extra service handler which wraps
// the angular httpClient and the API errors there
// There is NO Problem with that part :)
let apiResult = this.http.post(apiURL, params);
// Build a new Observable from type "DataStreamResult"
let dsr : Observable<DataStreamResult> = apiResult
.pipe(
map( httpresult => {
if (httpresult['status'] == false){
// the http call was basically successful,
// but state in data is false
// *** THIS IS NOT PROPAGATE TO SUBSCRIBE OnERROR ***
throwError({'msg' : 'MAP ERROR OCCURED'});
// also tried as alternative
return throwError({'msg' : 'MAP ERROR OCCURED'});
} else {
// here the http call was successful
let d = new DataStreamResult();
d.result = httpresult;
return d;
}
}),
catchError( err => {
// error is bubble up from http request handler
return throwError(err);
})
);
return dsr;
}
Finally the Question:
How could manage, that the "throwError" within the piped "map" is propagated to subscribe "err => { ... }".
The actual behavior for:
throwError({..})
I ended up in the subscribe OnNext Part with r = undefined
If I use:
return throwError({..})
I also ended up in the subscribe OnNext Part where r is the throwError-Observable
Thx in Advance
Best Regards
throwError({'msg' : 'MAP ERROR OCCURED'}) will return an observable that, when subscribed to, will effect an error notification. That is, it will call the subscriber's error method.
In your snippet, you either call throwError and ignore the value. Or you return its return value from a project function passed to the map operator.
Neither will effect an error.
There is no subscriber in the first situation, because the return value is ignored. And, in the second situation, there is no subscriber because the map operator doesn't subscribe to what it receives from the project function - the map operator's project function can return anything; it doesn't have to return an observable.
To throw an error within map, use:
throw {'msg' : 'MAP ERROR OCCURED'};