
## Error handling
---

<div class="admonition admonition-note"><div class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg></div><div class="admonition-body"><div class="admonition-content">

This feature is available only in Android SDK and iOS SDK.

</div></div></div>


`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 {id=error-handling-properties}


<div class="content-tabs" data-tab-group="tabgrp-1201">
<div class="tab-buttons"><button class="tab-button" data-tab-id="tabgrp-1201-0" data-tab-group="tabgrp-1201" data-tab-active="true">Android</button><button class="tab-button" data-tab-id="tabgrp-1201-1" data-tab-group="tabgrp-1201">iOS</button></div>

<div class="tab-panel" data-tab-id="tabgrp-1201-0" data-tab-group="tabgrp-1201" data-tab-active="true">

| Property | Method |
| --- | --- |
| Throwable | apiError.getThrowable() |
| Stacktrace | apiError.printStackTrace() |
| Type | apiError.getErrorType() |
| HTTP code | apiError.getHttpErrorCategory() |
| Body | apiError.getErrorBody() |

#### Throwable {id=error-handling-android-throwable-parameter}
Returns the original Throwable instance. It may be null if ApiError was instantiated with the `ApiError(Response)` constructor.

#### Stacktrace {id=error-handling-android-stacktrace-parameter}
Prints the stack trace of the original Throwable instance.

#### Type {id=error-handling-android-type-parameter}
`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).  
`ErrorType.UNKNOWN` is returned when an unknown error occurs (for example, no response from the server when expected).  

#### HTTP code {id=error-handling-android-http-code-parameter}
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 {id=error-handling-android-http-error-category-parameter}
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 {id=error-handling-android-body-parameter}
Returns the ApiErrorBody parsed from the error's response body. May be null if error type is different than `ErrorType.HTTP_ERROR`.

</div>

<div class="tab-panel" data-tab-id="tabgrp-1201-1" data-tab-group="tabgrp-1201">

| Property | Method |
| --- | --- |
| Type | apiError.getType() |
| HTTP code | apiError.getHttpCode() |
| Body | apiError.getBody() |

#### Type {id=error-handling-ios-body-parameter}
`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 {id=error-handling-ios-http-code-parameter}
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 {id=error-handling-ios-body-parameter}
Returns a description parsed from the response's error cause list. It may be null if the error type is different than `ApiErrorTypeHttp`.

</div>
</div>


### Sample {id=sample-code-for-error-handling}

See the following code samples for reading errors:


<div class="content-tabs code-tabs" data-tab-group="tabgrp-1198">
<div class="tab-buttons"><button class="tab-button" data-tab-id="tabgrp-1198-0" data-tab-group="tabgrp-1198" data-tab-active="true">Java</button><button class="tab-button" data-tab-id="tabgrp-1198-1" data-tab-group="tabgrp-1198">Kotlin</button><button class="tab-button" data-tab-id="tabgrp-1198-2" data-tab-group="tabgrp-1198">Swift</button><button class="tab-button" data-tab-id="tabgrp-1198-3" data-tab-group="tabgrp-1198">Objective-C</button><button class="tab-button" data-tab-id="tabgrp-1198-4" data-tab-group="tabgrp-1198">Dart</button></div>

<div class="tab-panel" data-tab-id="tabgrp-1198-0" data-tab-group="tabgrp-1198" data-tab-active="true">

```Java
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();
}
```

</div>

<div class="tab-panel" data-tab-id="tabgrp-1198-1" data-tab-group="tabgrp-1198">

```Kotlin
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()
}
```

</div>

<div class="tab-panel" data-tab-id="tabgrp-1198-2" data-tab-group="tabgrp-1198">

```Swift
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)
  })
}
```

</div>

<div class="tab-panel" data-tab-id="tabgrp-1198-3" data-tab-group="tabgrp-1198">

```Objective-C
[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
  }
}];
```

</div>

<div class="tab-panel" data-tab-id="tabgrp-1198-4" data-tab-group="tabgrp-1198">

```Dart
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");
});
}
```

</div>
</div>



## 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:


<div class="content-tabs code-tabs" data-tab-group="tabgrp-1199">
<div class="tab-buttons"><button class="tab-button" data-tab-id="tabgrp-1199-0" data-tab-group="tabgrp-1199" data-tab-active="true">Java</button><button class="tab-button" data-tab-id="tabgrp-1199-1" data-tab-group="tabgrp-1199">Kotlin</button><button class="tab-button" data-tab-id="tabgrp-1199-2" data-tab-group="tabgrp-1199">Swift</button><button class="tab-button" data-tab-id="tabgrp-1199-3" data-tab-group="tabgrp-1199">Objective-C</button><button class="tab-button" data-tab-id="tabgrp-1199-4" data-tab-group="tabgrp-1199">JavaScript</button></div>

<div class="tab-panel" data-tab-id="tabgrp-1199-0" data-tab-group="tabgrp-1199" data-tab-active="true">

```Java
Synerise.crashHandlingEnabled(true);
```

</div>

<div class="tab-panel" data-tab-id="tabgrp-1199-1" data-tab-group="tabgrp-1199">

```Kotlin
Synerise.crashHandlingEnabled(true)
```

</div>

<div class="tab-panel" data-tab-id="tabgrp-1199-2" data-tab-group="tabgrp-1199">

```Swift
Synerise.setCrashHandlingEnabled(true)
```

</div>

<div class="tab-panel" data-tab-id="tabgrp-1199-3" data-tab-group="tabgrp-1199">

```Objective-C
[SNRSynerise setCrashHandlingEnabled:YES];
```

</div>

<div class="tab-panel" data-tab-id="tabgrp-1199-4" data-tab-group="tabgrp-1199">

```JavaScript
Synerise.Initializer()
  .withBaseUrl("YOUR_API_BASE_URL")
  .withApiKey('YOUR_PROFILE_API_KEY')
  .withCrashHandlingEnabled(true)
.init();
```

</div>
</div>



## Cache Manager
---

<div class="admonition admonition-note"><div class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg></div><div class="admonition-body"><div class="admonition-content">

This feature is available only in Android and iOS SDK.

</div></div></div>


**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:


<div class="content-tabs" data-tab-group="tabgrp-1202">
<div class="tab-buttons"><button class="tab-button" data-tab-id="tabgrp-1202-0" data-tab-group="tabgrp-1202" data-tab-active="true">Android</button><button class="tab-button" data-tab-id="tabgrp-1202-1" data-tab-group="tabgrp-1202">iOS</button></div>

<div class="tab-panel" data-tab-id="tabgrp-1202-0" data-tab-group="tabgrp-1202" data-tab-active="true">

| Model | Description |
| --- | --- |
| [`GetAccountInformation`](/developers/mobile-sdk/class-reference/android/client#getaccountinformation) | Caching after a successful `Client.getAccount()` response. |

</div>

<div class="tab-panel" data-tab-id="tabgrp-1202-1" data-tab-group="tabgrp-1202">

| Model | Description |
| --- | --- |
| [`ClientAccountInformation`](/developers/mobile-sdk/class-reference/ios/client#clientaccountinformation) | Caching after a successful `Client.getAccount()` response. |

</div>
</div>


See the following code examples for accessing the cache:


<div class="content-tabs code-tabs" data-tab-group="tabgrp-1200">
<div class="tab-buttons"><button class="tab-button" data-tab-id="tabgrp-1200-0" data-tab-group="tabgrp-1200" data-tab-active="true">Java</button><button class="tab-button" data-tab-id="tabgrp-1200-1" data-tab-group="tabgrp-1200">Kotlin</button><button class="tab-button" data-tab-id="tabgrp-1200-2" data-tab-group="tabgrp-1200">Swift</button><button class="tab-button" data-tab-id="tabgrp-1200-3" data-tab-group="tabgrp-1200">Objective-C</button></div>

<div class="tab-panel" data-tab-id="tabgrp-1200-0" data-tab-group="tabgrp-1200" data-tab-active="true">

```Java
YourClass cachedModel = (YourClass) CacheManager.getInstance().get(YourClass.class);
```

</div>

<div class="tab-panel" data-tab-id="tabgrp-1200-1" data-tab-group="tabgrp-1200">

```Kotlin
val cachedModel: YourClass = CacheManager.getInstance<Any>()[YourClass::class.java] as YourClass
```

</div>

<div class="tab-panel" data-tab-id="tabgrp-1200-2" data-tab-group="tabgrp-1200">

```Swift
let clientAccountInformation: ClientAccountInformation? = CacheManager.get(ClientAccountInformation.self) as? ClientAccountInformation
```

</div>

<div class="tab-panel" data-tab-id="tabgrp-1200-3" data-tab-group="tabgrp-1200">

```Objective-C
SNRClientAccountInformation *clientAccountInformation = [CacheManager get:ClientAccountInformation.class];
```

</div>
</div>
