Most SDK operations report failures through their listener's onError(Throwable?). For a global view — logging SDK errors to your crash reporter, surfacing integration problems in QA builds — register a IPulsateErrorListener:
val pulsateManager = PulsateFactory.getInstance()
pulsateManager.setPulsateErrorListener(object : IPulsateErrorListener {
override fun onError(error: PulsateError) {
Log.w("Pulsate", "type=${error.type} message=${error.message}", error.exception)
// e.g. forward to Crashlytics/New Relic as a non-fatal
}
})
PulsateError carries:
| Field | Type | Meaning |
|---|---|---|
type | String | Error category |
message | String | Human-readable description |
exception | Exception? | Underlying exception, when available |
Listener threading
Threading differs by listener type.
Delivered on the main thread — the SDK dispatches these through their ...MainThread() default helpers (Dispatchers.Main), so the onSuccess / onError / onFeedClose / onUnauthorizedAction you override run on the main thread and may touch UI directly:
IPulsateRequestListenerIPulsateValueListener<T>IPulsateFeedListenerIPulsateUserUnauthorizedListener
No main-thread guarantee — these are invoked directly from whatever thread the SDK happens to be running on (background jobs, receivers):
IPulsateErrorListenerIPulsateBadgeUpdateListenerIPulsateGeofenceListener
For the second group, hop to the main thread (or use postValue / MutableStateFlow) before touching UI:
pulsateManager.setPulsateErrorListener(object : IPulsateErrorListener {
override fun onError(error: PulsateError) {
Handler(Looper.getMainLooper()).post { showErrorBanner(error.message) }
}
})

