The mysterious case of the hardcoded properties
When implementing Crashlytics is harder than solving the bugs.
Something we usually hide
Every day, we developers incur in crashes. We may deny it, avoid saying it out loud, but we have them. Mostly, this is thanks to some specific brands of product, but we still need to be aware of them. So we decide to track them down using a tool recently acquired by Twitter.
The project setup
My current task is based on a quite complex architecture made of a top project divided in several flavours that define the final package name and (mostly) UI details, sitting on a common library that will compile an additional video player only for some of the flavours.
Strange requests have been made
One day, one of the clients (aka one of the flavours) asked the team to implement Crashlytics in his release and our PM decided to just include it overall and use different configurations based on clients’ request.
The setup was as easy as the image shows, pretty straight forward, from plugin download to the project up and running.
But I was just so wrong.
Unfortunately, the plugin would not allow us to choose the correct library between those provided by the Fabric suite, forcing us to just integrate it manually, following the instructions provided by the team.
Implementing it the easy way
Importing the dependencies and the plugins after having read the documentation was fairly easy and we got it up and running pretty quickly. But we noticed something really strange, that suddenly made our duty much harder: Fabric suite needs a fabric.properties file, and it will look out for it right at the root of the project, right next to the build.gradle file and very far away from the flavours folders. There comes the trap. Sheldon was right.
One rough solution
Having the situation clear in mind, we came up with an idea:
What if we ask gradle to write the property file for every client that needs a different pair of key and secret?
Together with my gradle-ninja friend Eugenio, we created a script that was doing it… and it worked out of the box. -ish.
The idea behind it was pretty easy and we could write it down, in pseudo-code, more or less like this:
for each clientFlavour in clientFlavours {
if (exists fabricProperties) {
delete fabricProperties
}
fabricApiSecret = getApiSecretFor clientFlavour
fabricApiKey = getApiKeyFor clientFlavour write fabricApiSecret, fabricApiKey to fabricProperties
}
We wrote down the logic and we saw that our task was not able to grab the specific one from Crashlytics. We suddenly realised that, maybe, this crash reporting library was doing the same thing we were trying to do: dynamically creating the task and attaching it to the build sequence, so that we could not really find something to rely on.
Since we could not really create the file before the Crashlytics task, the full build was failing because no properties were found.
But we could still do something to achieve what we wanted and so we did!
We studied a little bit more the process and we noticed that, every time, the task
process${variantName}Manifest
was invoked before Fabric suite could do its magic.
At this point, we decided to hook our task right before the aforementioned one, and make this to depend on ours. Inside our routine, we would then check what kind of flavour we were running into, so we could load the secret data accordingly.
TL;DR
We couldn’t integrate Crashlytics using the nice plugin because of our flavour-based key-secret pairs: to solve this specific issure, we created a custom task that generates the requested fabric.properties file for every flavour.
You can find the gist related to this task on GitHub.