Loading... Search articles

Search for articles

Sorry, but we couldn't find any matches...

But perhaps we can interest you in one of our more popular articles?
How to set up Firebase Crashlytics for a Unity Android app build with Codemagic

How to set up Firebase Crashlytics for a Unity Android app

Apr 5, 2022

This post is written by Mina Pêcheux

This post has been updated in July 2022 to introduce some fixes to the codemagic.yaml file in the sample project.

TL;DR: To add events monitoring with Crashlytics to a Unity Android app, you’ll first need to prepare a Firebase project, then add the Firebase plugin to Unity and configure Unity to work with Firebase. Finally, you also need to update your CI/CD pipeline to support the changes.

Let’s see how to integrate Firebase Crashlytics into a Unity Android app to monitor and troubleshoot errors easily!

A few weeks ago, I shared a post on how to build and publish a Unity Android game using Codemagic and Firebase. In this article, I discussed why automating the distribution of a Unity app is interesting, how we can do it thanks to Codemagic’s CI/CD tools, and how we can finally take advantage of Firebase App Distribution to share any new version of the app with a group of handpicked testers.

That’s already pretty nice because it allows us to get instant feedback, sanitize the overall dev process, and deliver updates continuously.

But now that we have Firebase set up for the app, we can actually do more… For example, we can use another cool Firebase feature: Crashlytics! Basically, this tool gives us a lightweight real-time crash reporter to easily track and troubleshoot any grave or fatal errors your users could be experiencing.

So, this time, let’s see:

  • How to prepare our Firebase project to turn on the Crashlytics feature
  • How to configure our Unity project to use Firebase Crashlytics
  • How to upgrade the Codemagic workflow to integrate Firebase Crashlytics into our fully automated distribution process
  • How to simulate a crash test in our Unity app to fully enable the tool!

Warning: This tutorial builds on the previous one about Codemagic and Firebase, so if you haven’t looked at it, make sure to check it out! Also, it relies on the same sample project, my Slash’n’crack game, which you can find on GitLab over here. 🚀

Part 1: Preparing the Firebase project for Crashlytics

First things first, we need to go to our Firebase dashboard to properly configure Crashlytics on this side.

As explained in Google’s official docs, to actually get Crashlytics crashes or alerts, we first need to enable Google Analytics so that our app can log events to the Firebase project. To do this, go to the project’s settings in the Integration tab:

Then, choose the right Google Analytics account from the dropdown list:

By default, the integration workflow will create a new Google Analytics property to track the errors related to this app. However, if you already have one, you can also click the edit icon:

This will allow you to browse the list of existing properties and pick the one you want:

When you’re done, simply click “Finish”, and Google Analytics will now be enabled for this Firebase project!

Part 2: Setting up the Unity project to work with Crashlytics

This next section on configuring Unity for Crashlytics is the core part of the article — and the most complicated one. In short, we’re going to:

  • Add a new package to our Unity project to get all the Firebase tools we need and access them from our own code
  • Use these tools to set up a Crashlytics instance to run in our Unity app as soon as it starts
  • Update our project’s build and publishing settings so that it builds with the new Crashlytics feature

Making sure we have our Google access info

Erratum: In the previous article, I said that you should not commit the google-services.json file in your Git repo. Actually, this file doesn’t contain any private credentials or keys. It is only a means to access the Firebase project — so it is safe to add it to your versioning! :)

To begin with, you should check that your google-services.json file is inside your Unity project and committed to the Git repository. If you don’t have it or think it may be obsolete, you can download it from your Firebase project’s settings dashboard:

Once you have this file, move it anywhere into your Unity Assets folder. Be careful, as it needs to be named google-services.json exactly for the Firebase SDK to find it.

Importing the Firebase Crashlytics SDK to the Unity project

Now, you’ll need to download the Firebase Unity SDK, which is available as a Unity package, and unzip it somewhere locally on your computer. Then, in your Unity project, import this new custom package from the menu:

You’ll notice that the package contains two versions of the Firebase SDK — one for the .NET 3 framework and one for .NET 4. You’ll need to choose the same option your project uses; usually, you should be able to use .NET 4 for Unity 2017.x and later.

You can check or update your current configuration for this parameter by going to the Project Settings, selecting the Player window, navigating to the Other settings section, and configuring the Api Compatibility Level option:

You’ll also see that there are various Firebase tools in the package — not just Crashlytics! You can, of course, import as many as you want, but for this tutorial, you only need to open the FirebaseCrashlytics.unitypackage file.

In the Import Unity Package window, just click the “Import” button directly to get all the contents of the package.

When you install the Crashlytics SDK, you also automatically add an interesting tool to your Unity project: the Android dependencies auto-resolver. As explained by Patrick Martin in this neat article, this will allow us to quickly fetch, update, and write down all the SDK dependencies in the Gradle files of our Unity project.

Time to talk a bit about those!

Adding a few more Gradle files to the project

Note: From what I could gather, this section should be only necessary if you’re using Unity 2020.1 or later. But if you have some issues with initializing your Crashlytics instance, be sure to give it a look too! ;)

If you read the last article on Codemagic and Firebase, you might remember that we enabled the “Custom Base Gradle Template” option in our Publishing Settings (at the bottom of the Project Settings > Player window) to be able to modify the baseProjectTemplate.gradle ourselves.

But to make Crashlytics work properly and, in particular, initialize upon launching our app, we need to add several more Gradle files to our project! In fact, we want to toggle all the other Gradle file options in the block:

As you can see, this will automatically create several new Gradle files in the Unity project. These will allow us to specify particular build options and to require some packages or configurations in order to get Crashlytics running.

Just to be sure, you should take this opportunity to force resolve the Android dependencies using the new menu added by the SDK:

Then, still following Patrick’s advice for Unity 2020.1 and later configurations, you’ll want to:

  • Open the mainTemplate.gradle file and put these lines at the very bottom:
android {
    sourceSets {
        main {
            def unityProjectPath = $/file:///**DIR_UNITYPROJECT**/$.replace("", "/")
            res.srcDirs += (unityProjectPath +  '/Assets/Plugins/Android/Firebase/res/values/google-services.xml')
        }
    }
} 
  • While you’re at it, in the gradleTemplate.properties, check that the following properties are declared, or add them at the end:
android.useAndroidX=true
android.enableJetifier=true
  • Then, in the baseProjectTemplate.gradle file, you need to tell Gradle about Crashlytics:
// GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAIN

allprojects {
    buildscript {
        repositories {**ARTIFACTORYREPOSITORY**
            google()
            jcenter()
        }

        dependencies {
            // If you are changing the Android Gradle Plugin version, make sure it is compatible with the Gradle version preinstalled with Unity
            // See which Gradle version is preinstalled with Unity here https://docs.unity3d.com/Manual/android-gradle-overview.html
            // See official Gradle and Android Gradle Plugin compatibility table here https://developer.android.com/studio/releases/gradle-plugin#updating-gradle
            // To specify a custom Gradle version in Unity, go do "Preferences > External Tools", uncheck "Gradle Installed with Unity (recommended)" and specify a path to a custom Gradle version
            classpath 'com.android.tools.build:gradle:4.0.1'
            **BUILD_SCRIPT_DEPS**

            // Add the Crashlytics Gradle plugin.
            classpath 'com.google.firebase:firebase-crashlytics-gradle:2.3.0'
        }
    }

    repositories {**ARTIFACTORYREPOSITORY**
        google()
        jcenter()
        flatDir {
            dirs "${project(':unityLibrary').projectDir}/libs"
        }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
  • Finally, in the launcherTemplate.gradle file, add the Crashlytics plugin to the app by writing this line at the very end of the file:
// Apply the Crashlytics Gradle plugin
apply plugin: 'com.google.firebase.crashlytics'

If you’re like me and are using Android API level 30+ in your project build settings, then you’ll also need to toggle the first option to create a Custom Main Manifest (as shown above). Then, in the newly generated Assets/Plugins/Android/AndroidManifest.xml file, add the following attribute to the <application> tag:

<?xml version="1.0" encoding="utf-8"?>
<!-- GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAIN-->
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.unity3d.player"
    xmlns:tools="http://schemas.android.com/tools">
    <application android:allowNativeHeapPointerTagging="false">
        <activity android:name="com.unity3d.player.UnityPlayerActivity"
                  android:theme="@style/UnityThemeSelector">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
        </activity>
    </application>
</manifest>

Of course, don’t forget you can take a look at the sample project on GitLab if you want to see all of the files. 🚀

Building symbols for our app

To get all native stack traces and well-organized crash reports, Crashlytics needs to get some “symbols” associated with the app. These are created at build time alongside the .apk package (as a ZIP archive). They can then be uploaded to the Firebase project, thanks to the Firebase CLI utility.

To generate symbols for the Android app, we just need to enable this build option in our Build.cs script (the one that the Codemagic workflow uses to build the app in headless mode). So let’s update the BuildAndroid() static method:

public static void BuildAndroid()
{

    PlayerSettings.Android.useCustomKeystore = true;
    EditorUserBuildSettings.buildAppBundle = false;
    EditorUserBuildSettings.androidCreateSymbolsZip = true;
    
    // ...
}

Now that this build setting is enabled, whenever we create the Android package, we’ll also generate a .symbols.zip file next to it (the exact filename depends on the current bundle version of the app).

Initializing the Crashlytics instance in Unity

Finally, we need to add a new file to our Unity project: a C# script that will use the Firebase SDK we imported to create a Crashlytics instance and communicate with our Firebase project to fill the Crashlytics data.

As shown in the Google official docs, this class should check for the dependencies when the game starts and, if everything is fine, get access to the instance via the FirebaseApp object. I’ll put all of this in a new CrashlyticsInitializer C# class that I will add to one of my core (cross-scene) objects:

using UnityEngine;
using Firebase;

public class CrashlyticsInitializer : MonoBehaviour
{
    public static CrashlyticsInitializer instance;

    public bool isInitialized;

    void Awake()
    {
        if (instance == null)
            instance = this;

        isInitialized = false;

        Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task => {
            var dependencyStatus = task.Result;
            if (dependencyStatus == DependencyStatus.Available)
            {
                // Create and hold a reference to your FirebaseApp,
                // where app is a Firebase.FirebaseApp property of your application class.
                // Crashlytics will use the DefaultInstance, as well;
                // this ensures that Crashlytics is initialized.
                FirebaseApp app = FirebaseApp.DefaultInstance;

                // Set a flag here to indicate that your project is ready to use Firebase.
                isInitialized = true;
            }
            else
            {
                Debug.LogError(System.String.Format(
                  "Could not resolve all Firebase dependencies: {0}", dependencyStatus));
                // Firebase Unity SDK is not safe to use here.
            }
        });
    }
}

This script will be run as soon as the app starts and will stick around throughout its lifetime. I’ve also made a singleton for the CrashlyticsInitializer to better centralize and access my Crashlytics-related logic.

Part 3: Upgrading our Codemagic workflow to work with Crashlytics

To wrap up our new config, the last thing we need to do is update our Codemagic workflow to automatically upload the Crashlytics symbols we are now generating together with the Android package. We’re taking the configuration from the previous blog post on Unity and Firebase, so make sure you have followed all the steps in it. If you don’t have an account with Codemagic, sign up here.

Try Unity CI/CD now!

So, in the codemagic.yaml config file at the root of the repo, we simply need to use the Firebase CLI to upload the symbols and export them as artifacts.

This can be done in the artifacts and publishing blocks:

workflows:
  unity-android-workflow:
    name: Unity Android Workflow
    max_build_duration: 120
    environment: ...
    scripts: ...      
    artifacts:
        - android/*.apk
        - android/*.zip
    publishing:
      scripts:
        - name: Deactivate Unity License
          script: $UNITY_BIN -batchmode -quit -returnlicense -nographics
        - name: Generate Crashlytics symbol file
          script: firebase crashlytics:symbols:upload --app=$FIREBASE_APP_ID android/*.symbols.zip
      firebase:
        firebase_token: $FIREBASE_TOKEN
        android:
          app_id: $FIREBASE_APP_ID
          groups: # Add one or more groups that you wish to distribute your Android application to. You can create groups in the Firebase console.
            - happy-testers

Part 4: Simulating a test crash to check that Crashlytics is up

To finish setting up Crashlytics, we need to force a test crash to send a crash report to the Firebase project and enable the Crashlytics monitoring dashboard. So we need to add some code to “simulate” a test crash somewhere in our Unity game to run from the built app on our phone.

Basically, this piece of code has to throw an exception. It can be anything: a base system exception, a null reference error on a game object, etc. Here, I’m just going to throw a simple exception in a TestCrash() function in my CrashlyticsInitializer class, like this:

public class CrashlyticsInitializer : MonoBehaviour
{
    // ...

    public void TestCrash()
    {
        throw new System.Exception("(ignore) this is a test crash");
    }
}

I’ve personally “half-hidden” this method since it is callable in Slash’n’crack via a specific “Dev Tools" panel in my main menu. But depending on your use case, you can, of course, make the function more or less accessible to players. ;)

Finale: Building and testing!

Everything is now ready! All that’s left is to test our app and run a first crash test.

So let’s go to our Codemagic dashboard and start a new build; when it’s done, the app will automatically be published to Firebase (along with the symbols) and sent to the testers so that they can install the new version.

If you go back to the Firebase console, you’ll notice that the Crashlytics are now ready to receive events:

If you open the latest build, you’ll be able to run the TestCrash() function to simulate the first crash. Don’t forget that after running this method, you’ll need to re-open the app to actually send the event to Firebase Crashlytics.

Once the test is recorded, the Crashlytics dashboard will show the initial results:

You can even click on an event in the list to get more info (like the exact stack trace of the error, the version(s) that triggered it, and the number of users that experienced it).

Conclusion

In this article, we saw how we can improve the Codemagic Firebase integration to add Crashlytics support: My Slash’n’crack Unity game now logs events whenever the app crashes for any reason, and I can troubleshoot these errors in my Firebase dashboard.

If you want even more control over the Crashlytics events, you can add custom keys to the reports, set velocity alerts, and more. Check out the Google official docs for more info!

I hope you enjoyed this tutorial — and of course, don’t hesitate to share your ideas for other DevOps topics you’d like me to make Unity tutorials on! You can find me on Codemagic’s Slack, on Medium, or on Twitter.

Don’t forget you can get the sample project for the entire Slash’n’crack project on GitLab over here. 🚀


Mina Pêcheux is a freelance full-stack web & game developer. She’s also passionate about computer graphics, music, data science, and other topics! She runs her own blog. Alternatively, you can find her on Twitter.

How did you like this article?

Oops, your feedback wasn't sent

Related articles

Latest articles

Show more posts