Table Of Contents
- Introduction
- Android
- iOS
- Conclusion
Introduction
Many of us have deployed mobile applications to pre-production/production environments and got an email from Firebase saying “Heads up! We detected a new fatal issue in !” if you integrated Firebase crashlytics.
You wonder what it could be about, and head over to the firebase console under the crashlytics section to find out nothing makes sense, everything looks like a hexadecimal fest.
In this article, we’ll look at steps to take for both Android & iOS applications built with Flutter to ensure our crash reports are readable for every app version we release.
Before we get into it, Flutter is an open-source cross-platform development kit that supports but is not limited to Android and iOS and will generate binaries for these platforms when instructed to.
These binaries may be in .aab
or .apk
format for Android and in .ipa
for iOS.
It’s safe to say that if you stumbled upon this article, you already understand what obfuscation means
In any case, obfuscation is a means by which during the compilation of an application, the resulting symbols or tokens that you as the developer understand and can make meaning of in the first instance, are obscured. i.e. functions, classes, and variable names will be rendered meaning less, thus making it extremely hard to reverse-engineer. This is a security measure that production-grade applications go through.
This is why stack traces or error messages on the crashlytics dashboards are a bunch of gibberish without the steps we will go into detail on.
If Firebase crashlytics was set up properly, you probably don’t need to read this article. But for some wired reason, I’ve set up a couple of Flutter applications with Firebase crashlytics following all the steps in the official documentation and I still get the warning for missing dSYM files for iOS even though there are build steps that are meant to take care of this. If you are in the same situation, then read on.
Before we proceed, I assume that you’ve already integrated Firebase crashlytics with your Flutter application for Android and iOS. If you’ve not done that, See Integrating Firebase Crashlytics with your Fluter Application
Android
When developing Android applications using Flutter and you want obfuscation enabled, you’d pass the --obfuscate
flag and the --split-debug-info
argument. The --split-debug-info
flag is the most important argument for what we want to achieve as this tells the flutter binary where to output the debug symbols files.
We’ll upload these files to Firebase using the Firebase CLI tool.
Here’s a full flutter build command that obfuscates the resulting application and outputs the debug symbols to a specified path
flutter build apk --target-platform android-arm,android-arm64,android-x64 --release --obfuscate --split-debug-info=build/app/outputs/symbols --verbose
So we’ve told Flutter to output debug symbols to the path build/app/outputs/symbols
. Note that the path will be created if it doesn’t exist.
The next step is to use the Firebase CLI tool. If you don’t have it installed, use the below command to do so
curl -sL https://firebase.tools | bash
And then use the below to log in to your Firebase account
firebase login
To upload the debug symbols, you then use the command below while pointing to the folder containing the debug symbols
firebase crashlytics:symbols:upload --app=FIREBASE_APP_ID build/app/outputs/symbols
You can obtain FIREBASE_APP_ID
from your ‘Project Settings’ page in the Firebase console as shown below
If running the firebase crashlytics:symbols:upload
command in a CI environment like GitHub Actions, set an environment variable GOOGLE_APPLICATION_CREDENTIALS
to point to the location of your service account .json
key file.
Below is a typical re-usable and manually dispatchable GitHub Actions workflow that downloads the symbols folder as a zipped artifact, extracts it, and uploads it to Firebase. the workflow is expected to be re-used by an actual job in a workflow consisting of other jobs.
name: upload-flutter-debug-symbols-to-firebase on: workflow_dispatch: inputs: flutter-debug-symbols-artifact-name: description: AAB Artifact Name. required: true type: string workflow_call: inputs: flutter-debug-symbols-artifact-name: description: AAB Artifact Name. required: true type: string secrets: FIREBASE_APP_ID: description: Firebase App ID required: true FIREBASE_SERVICE_ACCOUNT_JSON_CONTENT: description: Firebase Service Account Key Content required: true jobs: upload-debug-symbols: name: Upload Flutter Debug Symbols to Firebase runs-on: ubuntu-latest steps: - name: Install Firebase Tools run: curl -sL https://firebase.tools | bash - name: Download Flutter Debug Symbols uses: actions/download-artifact@v2 with: name: ${{ inputs.flutter-debug-symbols-artifact-name }} - name: Extract Flutter Debug Symbols run: unzip -q ./symbols.zip -d symbols - name: Prepare Google Service Account Credential run: echo '${{ secrets.FIREBASE_SERVICE_ACCOUNT_JSON_CONTENT }}' >> firebase-service-account.json - name: Upload Flutter Debug Symbols to Firebase env: GOOGLE_APPLICATION_CREDENTIALS: firebase-service-account.json run: firebase crashlytics:symbols:upload --app=${{ secrets.FIREBASE_APP_ID }} ./symbols
iOS
Running the flutterfire configure
command after integrating your Flutter project with Firebase crashlytics will attempt to add a run script to your project’s Xcode workspace that finds and uploads the necessary dSYM symbol files to Crashlytics. Without these files, you’ll see a “Missing dSYM” alert in the Crashlytics dashboard and exceptions will be held by the backend until the missing files are uploaded – firebase.google.com.
If you did not use flutterfire configure
or did and you still get the “Missing dSYM” alert from Firebase, you will need to follow through on the steps from the highlighted text downwards
Sometimes, the steps in the official documentation (previous link) don’t help and sometimes cause the build process to fail depending on the complexity of your application, especially when you use the --split-debug-info
argument and the optional --obfuscate
flag.
There’s also the question of build schemes or flavors, concerning which Firebase credentials will be used while executing the script.
If you’ve followed through with the links above and still not getting readable crash reports from the Firebase console, two things are certain at this point.
- You have an upload script present at
ios/Pods/FirebaseCrashlytics/upload-symbols
After a successful build process, + dSYMs are being generated atbuild/ios/archive/Runner.xcarchive/dSYMs
.
To upload your dSYM files, the command should be somewhat like this
ios/Pods/FirebaseCrashlytics/upload-symbols -gsp <Absolute-Path-To-Flutter-Project-Root>/ios/Runner/GoogleService-Info.plist -p ios <Absolute-Path-To-Flutter-Project-Root>/build/ios/archive/Runner.xcarchive/dSYMs
The concept is the same in workflow or flavored or multi-build scheme Flutter iOS apps
Conclusion
Crash reports are essential for improving and maintaining the stability of any application as they help you identify issues before they become a major disaster. Hopefully, this article will help you achieve stability in your Flutter apps.