When using <https://crashkios.touchlab.co/docs/cra...
# touchlab-tools
d
When using https://crashkios.touchlab.co/docs/crashlytics is there a "correct" way to send non-fatal reports to Crashlytics? i.e. imagine you are working with an external API that is returning a bunch of parameters that are nullable but "should never be null". Or for example you're expecting a field to hold a specific value but it doesn't ... 🧵
In such cases I'd want to do something like
Copy code
CrashlyticsKotlin.sendHandledException(IllegalStateException("Expected one of X, Y, Z but got $badField"))
But this would group all IllegalStateException under one entry in the Crashlytics dashboard if I am reading this line correctly https://github.com/touchlab/CrashKiOS/blob/239894607b97cad8461dc6ac7a23c227b907655a/crashlytics/src/appleMain/kotlin/co/touchlab/crashkios/crashlytics/CrashlyticsCallsActual.kt#L21
is there something obvious I am missing? Tbh this seems like it would be a problem for any exception that is getting logged, not just "manually" logged ones - grouping via the throwable class name seems a bit weird? I'd expect reports in Crashlytics to be grouped via their stacktrace or something like that...
w
Hi! The specific line you mentioned is about the issue title and the
FIRStackFrameWithAddress(it.convert())
is actually about the grouping input. Firebase's Apple docs say: "Unlike fatal crashes, which are grouped via stack trace analysis, logged errors are grouped by domain and code." That applies to
recordError(NSError)
, but CrashKiOS uses the
record(exceptionModel:)
(see FIRCrashlyticsRecordExceptionModel), which carries a real stack trace, pretty similar to Android with
recordException(throwable)
. By the way, there's a trap when creating a helper function to report an exception so the top frame will be always the helper function name:
Copy code
// BAD: every caller collapses to one issue and the top frame is always `reportBadField`
fun reportBadField(msg: String) = CrashlyticsKotlin.sendHandledException(IllegalStateException(msg))

// GOOD: throwable constructed at the call site → distinct top frame per site
CrashlyticsKotlin.sendHandledException(IllegalStateException("Expected one of X, Y, Z but got $badField"))
Another good practice is to create custom exceptions (I know the question was not about that but worth mention) for a readable dashboard title:
Copy code
class UnexpectedApiValueException(message: String) : IllegalStateException(message)
So, using the
CrashlyticsKotlin.sendHandledException
should be fine.
d
that's very helpful thank you! What I am trying to achieve is combining this and Kermit to allow the project to do:
Copy code
co.touchlab.kermit.Logger.e(IllegalStateException(message), "message")
which then on Production builds can log to Crashlytics, via a custom Kermit LogWriter
Copy code
class CrashlyticsLogWriter : LogWriter() {
    override fun log(
        severity: Severity,
        message: String,
        tag: String,
        throwable: Throwable?
    ) {
        throwable?.let {
            CrashlyticsKotlin.sendHandledException(throwable)
        }
    }
}
LogWriter's
log
function is not inline I think so I am unsure if that will quite work 🤔