Error handling
NOTE: This feature is available only in Android SDK and iOS SDK.
ApiError is an object for error handling. It's designed to help you handle errors within your application. It provides details about failures in communication with the Synerise API.
This error is normally returned from the SDK methods that communicate with the Synerise API.
Properties
| Property | Method |
|---|---|
| Throwable | apiError.getThrowable() |
| Stacktrace | apiError.printStackTrace() |
| Type | apiError.getErrorType() |
| Reason | apiError.getReason() |
| Message | apiError.getMessage() |
| HTTP code | apiError.getHttpCode() |
| HTTP error category | apiError.getHttpErrorCategory() |
| Body | apiError.getErrorBody() |
| Background retry | apiError.isBackgroundRetryScheduled() |
Throwable
Returns the original Throwable instance. It may be null if ApiError was instantiated with the ApiError(Response) constructor.
Stacktrace
Prints the stack trace of the original Throwable instance.
Type
ErrorType.HTTP_ERROR is returned when a request is executed, but something else goes wrong and an error code is returned (for example, 403).ErrorType.NETWORK_ERROR is returned when a request failed to execute (for example, due to no Internet connection, a timeout, or a TLS failure).ErrorType.UNKNOWN is returned when an unknown error occurs (for example, no response from the server when expected, or a response that couldn't be parsed).
In Android SDK 6.17.0 and later:
- TLS failures and other I/O errors are reported as
ErrorType.NETWORK_ERROR. In earlier versions, they were reported asErrorType.UNKNOWN. - The SDK also checks the causes of wrapped exceptions. A network failure wrapped in another exception is reported as
ErrorType.NETWORK_ERRORinstead ofErrorType.UNKNOWN.
Reason
Available in Android SDK 6.17.0 and later.
Returns the detailed reason of the failure as an ApiErrorReason value, for example ApiErrorReason.TIMEOUT or ApiErrorReason.TLS_HANDSHAKE_FAILED. The value is never null. Each reason belongs to one error type, so you can use the reason when the error type is not specific enough.
New values may be added in future SDK versions. When you use switch or when with this enum, always handle the default or else branch.
Message
Available in Android SDK 6.17.0 and later.
Returns a human-readable description of the failure, suitable for logging. The value is never null.
- For HTTP errors, this is the error message from the server. If the response has no message, it contains the HTTP code and category, for example
HTTP 404 NOT_FOUND - For other errors, this is the description of the reason, followed by the message of the underlying exception if there is one, for example
Connection timed out: failed to connect to api.snrapi.com/... after 60000ms
HTTP code
Returns the HTTP status code. If the request failed to execute (for example, due to no Internet connection), this value will be -1.
HTTP error category
Returns the mapped response's HTTP code (for example, HTTP 400 code will be mapped to HttpErrorCategory.BAD_REQUEST, or 403 to HttpErrorCategory.FORBIDDEN).
Body
Returns the ApiErrorBody parsed from the error's response body. May be null if error type is different than ErrorType.HTTP_ERROR.
Background retry
Returns true if the request failed for a temporary reason (no connection, a timeout, or HTTP 408, 429, or 5xx) and the SDK will send it again in the background when the device is connected. In this case, you don't need to retry the request yourself. The other properties describe the failure of the first attempt.
Currently, only push registration requests are retried in the background.
Available in Android SDK 6.17.0 and later.
| Property | Method |
|---|---|
| Type | apiError.getType() |
| HTTP code | apiError.getHttpCode() |
| Body | apiError.getBody() |
Type
SNRApiErrorType.Http is returned when a request is executed, but something else goes wrong and an error code is returned (for example, 403).SNRApiErrorType.Network is returned when a request failed to execute (for example, due to no Internet connection).SNRApiErrorType.Unknown is returned when an unknown error occurs (for example, no response from the server when expected).
HTTP code
The method returns the HTTP status code. If a request failed to execute (for example, due to no Internet connection), this value is -1.
Body
Returns a description parsed from the response's error cause list. It may be null if the error type is different than ApiErrorTypeHttp.
Sample
See the following code samples for reading errors:
private void showAlertError(ApiError apiError) {
ApiErrorBody errorBody = apiError.getErrorBody();
int httpCode = apiError.getHttpCode();
// create AlertDialog with icon and title
AlertDialog.Builder dialog = new AlertDialog.Builder(this).setIcon(R.drawable.sygnet_synerise);
if (httpCode != ApiError.UNKNOWN_CODE) {
dialog.setTitle(String.valueOf(httpCode));
} else {
dialog.setTitle(R.string.default_error);
}
// append all available messages from API
if (errorBody != null) {
List<ApiErrorCause> errorCauses = errorBody.getErrorCauses();
StringBuilder message = new StringBuilder(errorBody.getMessage());
if (!errorCauses.isEmpty())
for (ApiErrorCause errorCause : errorCauses)
message.append("\n").append(errorCause.getCode()).append(": ").append(errorCause.getMessage());
dialog.setMessage(message.toString());
// if there is no available messages, set default one
} else {
switch (apiError.getErrorType()) {
case HTTP_ERROR:
if (apiError.getHttpErrorCategory() == UNAUTHORIZED) {
dialog.setMessage(getString(R.string.error_unauthorized));
} else {
dialog.setMessage(getString(R.string.error_http));
}
break;
case NETWORK_ERROR:
dialog.setMessage(getString(R.string.error_network));
break;
default:
dialog.setMessage(getString(R.string.error_default));
}
}
// show dialog
dialog.show();
}
private fun showAlertError(apiError: ApiError) {
val errorBody = apiError.errorBody
val httpCode = apiError.httpCode
// create AlertDialog with icon and title
val dialog: AlertDialog.Builder = Builder(this).setIcon(R.drawable.sygnet_synerise)
if (httpCode != ApiError.UNKNOWN_CODE) {
dialog.setTitle(httpCode.toString())
} else {
dialog.setTitle(R.string.default_error)
}
// append all available messages from API
if (errorBody != null) {
val errorCauses = errorBody.errorCauses
val message = StringBuilder(errorBody.message!!)
if (!errorCauses.isEmpty()) for (errorCause in errorCauses) message.append("\n").append(errorCause.code).append(": ").append(errorCause.message)
dialog.setMessage(message.toString())
// if there is no available messages, set default one
} else {
when (apiError.errorType) {
ErrorType.HTTP_ERROR -> if (apiError.httpErrorCategory == UNAUTHORIZED) {
dialog.setMessage(getString(R.string.error_unauthorized))
} else {
dialog.setMessage(getString(R.string.error_http))
}
ErrorType.NETWORK_ERROR -> dialog.setMessage(getString(R.string.error_network))
else -> dialog.setMessage(getString(R.string.error_default))
}
}
// show dialog
dialog.show()
}
func presentAlert(title: String, message: String) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(okAction)
self.present(alertController, animated: true, completion: nil)
}
func showErrorInfo(_ error: NSError, title: String = "Error", debug: Bool = true) {
if let apiError = error as? SNRApiError {
var apiErrorDebugInfo: String = String()
let apiErrorType: SNRApiErrorType = apiError.getType()
switch (apiErrorType) {
case .network: apiErrorDebugInfo.append("NETWORK ERROR")
case .unauthorizedSession: apiErrorDebugInfo.append("UNAUTHORIZED SESSION ERROR")
case .http: apiErrorDebugInfo.append("HTTP ERROR: \(apiError.getHttpCode())")
case .unknown: apiErrorDebugInfo.append("UNKNOWN ERROR")
}
apiErrorDebugInfo.append("\n\n")
apiErrorDebugInfo.append("\(apiError.localizedDescription)")
// first approach
if let apiErrorCauses = apiError.errors, !apiErrorCauses.isEmpty {
apiErrorDebugInfo.append("\n\n")
apiErrorCauses.forEach({ (error) in
let apiErrorCause: NSError = error as NSError
var apiErrorCauseString: String = String()
apiErrorCauseString.append("CODE: \(apiErrorCause.code)")
apiErrorCauseString.append("\n")
apiErrorCauseString.append("MESSAGE: \(apiErrorCause.localizedDescription)")
apiErrorDebugInfo.append(apiErrorCauseString)
apiErrorDebugInfo.append("\n\n")
})
}
// second approach
// apiErrorDebugInfo.append("\n\n")
//
// let apiErrorCauseString: String = apiError.getBody() ?? ""
// apiErrorDebugInfo.append(apiErrorCauseString)
self.presentAlert(title: "Debug SNRApiError", message: apiErrorDebugInfo)
if debug {
DebugUtils.print("\(title) \(apiError.code) \(apiError.localizedDescription)")
}
return
}
if debug {
DebugUtils.print("\(title) \(error.code) \(error.localizedDescription)")
}
}
func signIn(email: String, password: String) {
Client.signIn(email: email, password: password, deviceId: nil, success: { (success) in
//...
}, failure: { (error) in
self.showErrorInfo(error as NSError)
})
}
[SNRClient signInWithEmail:email password:password deviceId:nil success:^(BOOL isSuccess) {
} failure:^(NSError * _Nonnull error) {
if ([error isKindOfClass:[SNRApiError class]]) {
SNRApiError *apiError = (SNRApiError *)error;
NSLog(apiError.localizedFailureReason) // print information string about all issues
NSLog(apiError.errors) // print list of error objects about issues that occurred
}
}];
Future<void> _signInCall(email, password) async {
await Synerise.client.signIn(email, password).catchError((error) {
final String errorCode = error.code;
final String errorMessage = error.message;
print("Error: $errorCode - $errorMessage");
});
}
Crash handling
Crash handler allows you to detect mobile app users whose mobile applications crashed. This information is saved on the activity list of a mobile app user in Behavioral Data Hub in the form of an event.
The crash is connected with a customer. Using the data from our crash handler, you can send a personalized apology when the application crashes.
You can enable crash handling for Synerise SDK by using an SDK method (Android and iOS) or during initialization by using a builder method (React Native).
When it is enabled, the Synerise SDK passes info about an application crash in the form of a dedicated event to the backend (client.applicationCrashed is the action parameter of those events).
See sample codes below:
Synerise.crashHandlingEnabled(true);
Synerise.crashHandlingEnabled(true)
Synerise.setCrashHandlingEnabled(true)
[SNRSynerise setCrashHandlingEnabled:YES];
Synerise.Initializer()
.withBaseUrl("YOUR_API_BASE_URL")
.withApiKey('YOUR_PROFILE_API_KEY')
.withCrashHandlingEnabled(true)
.init();
Cache Manager
NOTE: This feature is available only in Android and iOS SDK.
Cache Manager provides you with an easy-to-use option to retrieve cached data if communication problems with the backend occur. If a request fails, you can obtain the cached data.
Currently, our Cache Manager supports:
| Model | Description |
|---|---|
GetAccountInformation |
Caching after a successful Client.getAccount() response. |
| Model | Description |
|---|---|
ClientAccountInformation |
Caching after a successful Client.getAccount() response. |
See the following code examples for accessing the cache:
YourClass cachedModel = (YourClass) CacheManager.getInstance().get(YourClass.class);
val cachedModel: YourClass = CacheManager.getInstance<Any>()[YourClass::class.java] as YourClass
let clientAccountInformation: ClientAccountInformation? = CacheManager.get(ClientAccountInformation.self) as? ClientAccountInformation
SNRClientAccountInformation *clientAccountInformation = [CacheManager get:ClientAccountInformation.class];