While debugging an issue in an app that I’m working on, I noticed some peculiar behavior which we couldn’t explain.
The app that I’m working in currently uses Firebase Remote Config to do A/B testing as well as to extract some configuration options which we want to change on-demand. We’ve been seeing quite a few errors when fetching the configuration so we decided to add a bunch more logging to figure out what was going on.
Firebase Remote config, like a lot of libraries by Google which do asynchronous work use the Tasks API to model the asynchronous work.
When we call the remote config SDK to fetch and activate our values, the SDK returns a Task<Boolean>
. This is a Task
which has a result which is of the type Boolean
. In our logging we wanted to know what the result was, so we added this to the logs like so:
logger.error(
"Firebase error occured",
mapOf(
"isResultSuccessful" to task.result?.let { if (it) "true" else "false" } ?: "null"
)
)
The result can be nullable as well, so we check for that here, if it is defined we output the string equivalent of true
or false
.
This looks pretty straight forward at first sight, right?
We released a new version of our app with this and suddenly we saw our crash rate dropping to almost 50% of our users. (We have quite a few non fatals occuring, but these never resulted in any crash).
After investigating for a couple of hours we found out that task.result
can throw an exception if there was an exception while executing the fetch and activate call. So adding a simple try-catch around this resolved our issue.
Looking at the declaration of getResult
doesn’t show that it can throw an exception though.
The documentation does show that it can indeed throw an exception, but who reads the documentation?
I hope that with this some people will prevent some of their crashes in their apps. I guess this is a note to myself to read the documentation a bit more. ¯\_(ツ)_/¯