Exposing all RevenueCat PurchasesErrorCode codes in flutter - flutter

I need to trap all of the listed PurchasesErrorCode error codes in my Flutter app so I can respond to them accordingly.
Currently I can only trap "userCancelled", for everything else I can only report the information returned in the standard PlatformException code, message and details properties, without knowing what they will contain.
try {
// Code to make purchase..
} on PlatformException catch (e) {
if (!(e.details as Map)["userCancelled"]) {
// Here I need a comprehensive switch statement so I can
// retry where appropriate/control what messages the user sees
String reason = '';
(e.details as Map).forEach((k,v) => reason += '$k => $v');
showError(context, 'Error', '${e.code} : ${e.message}');
} else {
showError(context, 'Purchase Cancelled', 'Your purchase was not completed, you have not been charged.');
}
}
These codes are exposed in IOS/Swift and Android/Kotlin but I can't get them in Flutter/Dart - what am I missing?

I developed the RevenueCat Flutter plugin and I created an issue on GitHub some time ago to track this (https://github.com/RevenueCat/purchases-flutter/issues/3). I am sorry there is some room for improvement in our Flutter error handling.
When we send the platform exceptions we pass the error code as an String:
result.error(error.getCode().ordinal() + "", error.getMessage(), userInfoMap);
Too bad we can't just pass an int as the first parameter and we have to pass a String, I guess we could pass it in the userInfoMap. But for now, since we are not providing the enum with the error codes yet, you would have to do something like this in your code:
enum PurchasesErrorCode {
UnknownError,
PurchaseCancelledError,
StoreProblemError,
PurchaseNotAllowedError,
PurchaseInvalidError,
ProductNotAvailableForPurchaseError,
ProductAlreadyPurchasedError,
ReceiptAlreadyInUseError,
InvalidReceiptError,
MissingReceiptFileError,
NetworkError,
InvalidCredentialsError,
UnexpectedBackendResponseError,
ReceiptInUseByOtherSubscriberError,
InvalidAppUserIdError,
OperationAlreadyInProgressError,
UnknownBackendError,
InsufficientPermissionsError
}
try {
} on PlatformException catch (e) {
PurchasesErrorCode errorCode = PurchasesErrorCode.values[int.parse(e.code)];
switch (errorCode) {
case PurchasesErrorCode.UnknownError:
case PurchasesErrorCode.PurchaseCancelledError:
case PurchasesErrorCode.StoreProblemError:
// Add rest of cases
}
}
When you do e.details, you also get access to a readableErrorCode containing the name of the error code; and an underlyingErrorMessage, that can hopefully help you debug any issue.
I hope that helps

The issue mentioned on the previous answer was solved, after version 1.0.0 you can handle like this:
try {
PurchaserInfo purchaserInfo = await Purchases.purchasePackage(package);
} on PlatformException catch (e) {
var errorCode = PurchasesErrorHelper.getErrorCode(e);
if (errorCode == PurchasesErrorCode.purchaseCancelledError) {
print("User cancelled");
} else if (errorCode == PurchasesErrorCode.purchaseNotAllowedError) {
print("User not allowed to purchase");
}
}

Related

How can I catch errors in my firebase function when setting a document fails?

I have a firebase cloud function to create a user document with user data whenever a user registers. How would I return an error when the set() fails? Since this is not an http request (an I don't want to use an http request in this case) I have no response. So how would I catch errors?
export const onUserCreated = functions.region('europe-west1').auth.user().onCreate(async user => {
const privateUserData = {
phoneNumber: user.phoneNumber
}
const publicUserData = {
name: 'Nameless'
}
try
{
await firestore.doc('users').collection('private').doc('data').set(privateUserData);
}catch(error)
{
//What do I put here?
}
try
{
await firestore.doc('users').collection('public').doc('data').set(publicUserData);
}catch(error)
{
//What do I put here?
}
});
You can't "return" an error, since the client doesn't even "know" about this function running, there is nobody to respond to.
You can make a registration collection, and in your function make a document there for the current user (using the uid as the document id). In that document, you can put any information you'd like your user to know (status, errors, etc).
So your clients would have to add a listener to this document to learn about their registration.
In your particular code, I think the error is in doc('users'). I guess you meant doc('users/'+user.uid).
Your catch -block will receive errors that occur on your set -call:
try {
await firestore.doc('users').collection('public').doc('data').set(publicUserData);
} catch (error) {
// here you have the error info.
}

Dart method has errors but does not return error

I am looking at the flutter firebase auth docs.
Specifically, I am looking at these docs, for the method FirebaseAuth.createUserFromEmailAndPassword.
In the docs it lists three errors. But I am confused as to how these errors get communicated back to the caller. It gives no information on the types of these errors. I am really confused here. If it doesn't return an error type, and it doesn't take an error out parameter as an input, how is this method supposed to communicate errors?
Apparently there are three errors just floating in the ether that I can not get a hold of lol.
The method will throw an AuthException. The exception will have a code field. The contents of the code field will explain what went wrong exactly.
ERROR_WEAK_PASSWORD - If the password is not strong enough.
ERROR_INVALID_EMAIL - If the email address is malformed.
ERROR_EMAIL_ALREADY_IN_USE - If the email is already in use by a
different account.
You can handle these errors like any other exception in Dart: with a try/catch.
try {
var result = await FirebaseAuth.createUserFromEmailAndPassword(...);
} catch (e) {
if (e is AuthException) {
switch (e.code) {
case 'ERROR_WEAK_PASSWORD':
// Handle weak password
break;
case 'ERROR_INVALID_EMAIL':
// Handle invalid email
break;
case 'ERROR_EMAIL_ALREADY_IN_USE':
// Handle email in use
break;
}
} else {
// An error other than an AuthException was thrown
}
}

How to catch errors in GoogleSignIn signInSilently?

I have the following code that I am using to log a user in silently
try {
result = await GoogleSignIn().signInSilently().catchError((x) {
print(x);
});
} catch (e) {
print(e);
}
If the user cannot sign in silently, it results in an error.
PlatformException (PlatformException(sign_in_required, com.google.android.gms.common.api.ApiException: 4: 4: , null))
The problem I am having is that I cannot seem to catch the exception. catchError nor the catch block are being hit. How do I catch this error?
Do the following in your method
try {
result = await GoogleSignIn().signInSilently(suppressErrors: false).catchError((x) {
print(x);
});
} catch (e) {
print(e);
}
By default suppressErrors = true suppressing the error messages you want to catch.
Looking at the source code
The SignInSilently method is used to suppress error messages thus not throwing the exception you want to catch.
From the docs of this method:
/// When [suppressErrors] is set to `false` and an error occurred during sign in
/// returned Future completes with [PlatformException] whose `code` can be
/// either [kSignInRequiredError] (when there is no authenticated user) or
/// [kSignInFailedError] (when an unknown error occurred).
Full Method
/// Attempts to sign in a previously authenticated user without interaction.
///
/// Returned Future resolves to an instance of [GoogleSignInAccount] for a
/// successful sign in or `null` if there is no previously authenticated user.
/// Use [signIn] method to trigger interactive sign in process.
///
/// Authentication process is triggered only if there is no currently signed in
/// user (that is when `currentUser == null`), otherwise this method returns
/// a Future which resolves to the same user instance.
///
/// Re-authentication can be triggered only after [signOut] or [disconnect].
///
/// When [suppressErrors] is set to `false` and an error occurred during sign in
/// returned Future completes with [PlatformException] whose `code` can be
/// either [kSignInRequiredError] (when there is no authenticated user) or
/// [kSignInFailedError] (when an unknown error occurred).
Future<GoogleSignInAccount> signInSilently({bool suppressErrors = true}) {
final Future<GoogleSignInAccount> result = _addMethodCall('signInSilently');
if (suppressErrors) {
return result.catchError((dynamic _) => null);
}
return result;
}
Reference
Google Sign In Plugin Repo
Method Source Code Starting Line 283
This might help.
Checklists :
Did not register a sha fingerprint.
Make sure to have my "support email" set.
Enable the Google Sign-in method.
The catchError block is actually getting hit if you use suppressErrors = false
Try the following code and see what it prints on your console.
result = await GoogleSignIn().signInSilently(suppressErrors: false).
catchError((x) {
print("inside catchError");
});
The problem is the print(e); line in the catch statement. 'e' is not a string so an error occurs. I don't know why but no breakpoints are hit in the catch statement for this error and no error is output to the console. If I put a catch statement around a function that calls the login code ie.
void tryLogin(){
try{
myLoginLogic();
} catch(e) {
print(e);
}
}
Then I do get an error message
Unhandled Exception: type '_TypeError' is not a subtype of type 'String'
So to be clear the correct code would be
try {
result = await GoogleSignIn().signInSilently().catchError((e) {
print(e.toString());
});
} catch (e) {
print(e.toString());
}
Why code doesn't break on error and no message is written to the console for the original function I don't know. Also what I don't understand is why, even when the catch statement is written without errors, in debug mode the code breaks on the signInSilently() line.

Firestore check if document already exist using try await

Trying to check if doc already exist before saving it but I wanted to use try, await here is my example:
try {
const payload = await db.collection('cities').doc('LA').get()
if (!payload.exists) {
try {
const payload = await db.collection('cities').doc('LA').set(data)
if (payload.exists()) {
response.send(payload);
}
} catch (error) {
response.send(error);
}
} else {
response.send({error: "document exists!"});
}
} catch (error) {
response.send(error);
}
Just wanted to check if it's the best way to do it?
try/catch is not functionally any different than then()/catch(). It's just a different syntax that simplifies dealing with promises.
I don't know what your definition of "best way" is, but if it works OK for you with a minimal amount of code, I'm sure it's OK.

Call recursive function in Observable not working in ionic3

I have one function in my app getServerData() which I call from home page and passing Token as param in my API calling in this function.
if Token is valid API will return data otherwise it will return unauthorised access with token expired error at that time I am calling same function with new generated token form another API but some how recursive function calling not working in Observable.
Check below code for more detail :
/**
* Get Search result from server.
*/
getServerData(searchText: string): Observable<any> {
let self = this;
return Observable.create(function(observer) {
self.getToken().then((token) => {
console.log('Token : ', token);
self.httpPlugin.get(self.url + searchText, {}, {}).then((response) => {
console.log("Response Success : " + JSON.stringify(response));
observer.next(jsonResponse);
}).catch(error => {
if (error.status == 403) {
//Call Same method Again
self.getServerData(searchText);
} else {
console.log("Error : " + error);
console.log("Error " + JSON.stringify(error));
observer.error(error);
}
});
}).catch((error) => {
observer.error(error);
console.log("Error : " + error);
})
});
}
While calling same function no code execution done.
Edit based on comment:
I am subscribe like below:
this.subscription = this.api.getServerData(this.searchString.toUpperCase()).subscribe((response: any) => {
console.log("back with data :-",response);
}, error => {
console.log("InLine Error : ",error);
});
Not able to understand whats going wrong or Am I doing some mistake in calling function from Observable().
Guide me on this.
Thanks in advance!
It's not good practice to use promise in observable. Use Obserable.fromPromise and also use mergeMap. What will happen if you will use. Whenever any error will come Observable will throw error and you will able to catch. I will suggest to use Subject rather than creating your own observable and also remember one thing that don't subscribe in your service.
Hope it will help
Finally after lots of research I found solution of my issue.
1st thing was I need to update my rxjx library as my installed version of rxjx was 5.5.2 so I upgraded it to latest one 5.5.11.
2nd thing was I am calling Observable without subscribe() to that Observable so it will never return so I updated my recursive call from error block from where I call its subscriber() like below.
getSearchData(){
this.subscription = this.api.getServerData(this.searchString.toUpperCase()).subscribe((response: any) => {
console.log("back with data :-",response);
}, error => {
if (response.status == 403) {
this.getSearchData();
}else{
console.log("InLine Error : ",response);
this.showAlert('Error', 'Something went wrong. please try again.');
}
});
}
By doing above 2 things I am able to solve my issue.
Thanks all for your quick reply on my issue.
Hope this will help someone who is facing same issue like me.