| name | crashlytics-setup |
| description | Set up Firebase Crashlytics correctly across Android (Kotlin), iOS (Swift), Flutter, and React Native with custom keys, user ids, and release tagging. Use when adding or auditing Crashlytics integration. |
Firebase Crashlytics Setup
Instructions
Follow these steps to wire Firebase Crashlytics into a mobile app so that every crash, non-fatal, and ANR is captured, symbolicated, and attributable to a release.
1. Prerequisites
- A Firebase project with the app registered for every target platform (one app per bundle id / application id).
google-services.json (Android) and GoogleService-Info.plist (iOS) checked into the project or injected at build time per flavor.
- Crashlytics enabled in the Firebase console for each app; ANR collection enabled under Android settings.
- A release pipeline that can upload mapping files / dSYMs per build (see the
symbolication skill).
2. Android (Kotlin) Integration
Add Gradle plugins in settings.gradle.kts or the root build.gradle.kts:
plugins {
id("com.google.gms.google-services") version "4.4.2" apply false
id("com.google.firebase.crashlytics") version "3.0.2" apply false
}
App module build.gradle.kts:
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("com.google.gms.google-services")
id("com.google.firebase.crashlytics")
}
dependencies {
implementation(platform("com.google.firebase:firebase-bom:33.5.1"))
implementation("com.google.firebase:firebase-crashlytics-ktx")
implementation("com.google.firebase:firebase-analytics-ktx")
}
android {
buildTypes {
getByName("release") {
isMinifyEnabled = true
configure<com.google.firebase.crashlytics.buildtools.gradle.CrashlyticsExtension> {
mappingFileUploadEnabled = true
nativeSymbolUploadEnabled = true
unstrippedNativeLibsDir = file("build/intermediates/merged_native_libs/release/out/lib")
}
}
getByName("debug") {
configure<com.google.firebase.crashlytics.buildtools.gradle.CrashlyticsExtension> {
mappingFileUploadEnabled = false
}
}
}
}
Initialize in Application.onCreate:
class App : Application() {
override fun onCreate() {
super.onCreate()
FirebaseApp.initializeApp(this)
val crashlytics = FirebaseCrashlytics.getInstance()
crashlytics.isCrashlyticsCollectionEnabled = BuildConfig.BUILD_TYPE == "release"
crashlytics.setCustomKey("flavor", BuildConfig.FLAVOR)
crashlytics.setCustomKey("build_number", BuildConfig.VERSION_CODE)
}
}
3. iOS (Swift) Integration
Add the Firebase SPM package (https://github.com/firebase/firebase-ios-sdk) and include FirebaseCrashlytics and FirebaseAnalytics.
Add a Run Script build phase after [CP] Embed Pods Frameworks that runs:
"${PODS_ROOT}/FirebaseCrashlytics/run"
with input files $(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH) and $(DWARF_DSYM_FOLDER_PATH)/$(DWARF_DSYM_FILE_NAME)/Contents/Resources/DWARF/$(EXECUTABLE_NAME).
Initialize in AppDelegate:
import FirebaseCore
import FirebaseCrashlytics
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
Crashlytics.crashlytics().setCustomValue(Bundle.main.infoDictionary?["CFBundleVersion"] ?? "?", forKey: "build_number")
#if DEBUG
Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(false)
#endif
return true
}
}
4. Flutter Integration
dependencies:
firebase_core: ^3.6.0
firebase_crashlytics: ^4.1.3
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError;
PlatformDispatcher.instance.onError = (error, stack) {
FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
return true;
};
await FirebaseCrashlytics.instance
.setCrashlyticsCollectionEnabled(!kDebugMode);
runApp(const MyApp());
}
Obfuscated release builds must pass --obfuscate --split-debug-info=build/symbols and you must upload the resulting symbols (see symbolication).
5. React Native Integration
yarn add @react-native-firebase/app @react-native-firebase/crashlytics
cd ios && pod install
import crashlytics from '@react-native-firebase/crashlytics';
export async function initCrashReporting(userIdHash: string, appVersion: string) {
await crashlytics().setCrashlyticsCollectionEnabled(!__DEV__);
await crashlytics().setUserId(userIdHash);
await crashlytics().setAttributes({
build_number: appVersion,
js_engine: 'hermes',
});
}
Configure firebase.json for automatic JS error capture:
{
"react-native": {
"crashlytics_javascript_exception_handler_chaining_enabled": true,
"crashlytics_auto_collection_enabled": true
}
}
6. Custom Keys and User Ids
- User id: set a stable hashed user id (
SHA-256(userId + app_salt)), never the raw id. Do not set emails or phone numbers.
- Standard keys:
release_channel, feature_flags_hash, last_screen, network_type, locale, experiment_bucket.
- Breadcrumbs via logs: call
log("navigated to /cart") at key state transitions; Crashlytics attaches the last 64 log lines to the crash.
7. Non-Fatals and ANRs
- Capture caught exceptions that represent bugs with
recordError / recordException so they surface alongside crashes.
- Tag non-fatals with a
severity custom key (low/med/high) so dashboards can filter.
- ANRs are auto-captured on Android; you cannot opt specific traces out, but you can improve attribution by setting
last_screen before any long-running operation.
8. Dashboards and Alerts
- Pin the Crash-free users and Crash-free sessions panels for the last release.
- Create Velocity alerts per app for new issues affecting more than 1% of sessions in the last hour.
- Alert on ANR rate > 0.47% of daily users (Play Store bad-behavior threshold) with a burn-rate policy (see the
alerting-sla skill).
- Every alert must deep-link to the Crashlytics issue URL and include the affected release.
Checklist