← Blog

React Native Crashlytics: installed does not mean reporting

6 min read
Watch the walkthrough on YouTube

The companion video becomes viewable when its YouTube release goes public.

Crashlytics was installed in Podium. The commit records the pods, Android plugin, Firebase initialization, symbol upload phase and JavaScript adapter. The console still showed the setup screen, and no first report had arrived.

That is a useful starting symptom. It does not identify which part of reporting failed.

Verify native collection before JavaScript starts, then prove delivery with a test crash.

This is Day 17 of Ship Native, a standalone Production fixes lesson. It follows Day 16's Arabic text investigation. The examples were reviewed against project source and the installed RN Firebase build script. No native build, simulator session or Firebase report delivery was performed for this article. The video uses illustrated panels and synthetic narration.

The startup boundary

A React Native process starts in native code. It initializes the React host before executing your JavaScript. An early native crash may therefore happen before a JS-side call to enable collection runs.

There are separate questions to answer:

  1. Is the integration present in the binary?
  2. Is collection enabled during the relevant startup window?
  3. Did the SDK detect a crash?
  4. Was a report submitted?
  5. Is that report visible in the intended Firebase project?

Checking a dependency list answers only part of the first question. A test button that turns collection on can answer a later question while concealing the original startup configuration.

The flag with two negatives

The inspected project uses React Native 0.86.0 and installed RN Firebase Crashlytics 25.1.0. Its app build script reads crashlytics_disable_auto_disabler.

The project configuration includes:

{
  "react-native": {
    "crashlytics_disable_auto_disabler": true,
    "crashlytics_auto_collection_enabled": true,
    "crashlytics_javascript_exception_handler_chaining_enabled": true,
    "crashlytics_is_error_generation_on_js_crash_enabled": true,
    "app_data_collection_default_enabled": true
  }
}

This is the project's configuration, not a universal prescription for every application. It is supplied per branded app. Review the intended collection policy and configuration resolution for your own build.

In the installed @react-native-firebase/app/ios_config.sh, the inspected configuration branch skips adding the disabling plist entry when the auto-disabler flag is true. Its other branch queues FirebaseCrashlyticsCollectionEnabled with NO.

true disables the automatic disabler. It does not disable Crashlytics.

There is also a separate missing-configuration branch. Do not shorten this into the claim that every absent firebase.json automatically produces the same plist result. Follow the actual version's branch and the file it resolves.

Inspect the built application

After a native rebuild, read the packaged artifact:

/usr/libexec/PlistBuddy \
  -c 'Print :FirebaseCrashlyticsCollectionEnabled' \
  '<built-app>/Info.plist'

Replace the placeholder with the actual .app path. Record whether the key exists and its value. A missing key is not automatically false: SDK defaults and persisted collection overrides also contribute to effective behavior.

For this lesson, I inspected the project configuration and installed script. I did not inspect a newly built .app. The command is a verification instruction, not a claimed result.

Build a probe with separate answers

The fix introduces a developer probe. These source functions are copied from the project, with the application-specific test message retained:

import {
  crash,
  didCrashOnPreviousExecution,
  getCrashlytics,
  log,
  recordError,
  setCrashlyticsCollectionEnabled,
} from '@react-native-firebase/crashlytics';

export const crashlyticsEnabled = (): boolean =>
  Boolean(
    (getCrashlytics() as unknown as { isCrashlyticsCollectionEnabled: boolean })
      .isCrashlyticsCollectionEnabled,
  );

export const enableCrashlytics = async (): Promise<void> => {
  await setCrashlyticsCollectionEnabled(getCrashlytics(), true);
};

export const crashedOnPreviousRun = (): Promise<boolean> =>
  didCrashOnPreviousExecution(getCrashlytics());

export const sendTestError = (): void => {
  const cl = getCrashlytics();
  log(cl, 'DevOptions: test non-fatal');
  recordError(cl, new Error('Podium test non-fatal from DevOptions'));
};

export const sendTestCrash = (): void => {
  const cl = getCrashlytics();
  log(cl, 'DevOptions: deliberate test crash');
  crash(cl);
};

The cast reflects how this project reads the instance property; it does not provide runtime validation. Confirm the API against your installed version.

Collection state describes the current installation. Previous-execution state describes a crash detected on the prior run. Neither is a delivery receipt or a diagnosis of the cause.

A non-fatal event keeps the application alive. A deliberate native crash terminates it. The developer UI makes the fatal action separate and confirms it before execution.

Why the probe can mask the default

The source handlers enable collection before invoking either sender:

await enableCrashlytics().catch(() => {});
sendTestError();

That sequence tests reporting after explicit enablement. Even if its report arrives, it does not establish whether the original native startup default was correct.

It also catches an enablement failure and continues. The source UI can display a positive toast after the non-fatal action; that toast is not submission evidence. Inspect the enablement result, local capture state and actual delivery separately when adapting this probe.

Verify delivery without the debugger interfering

Firebase's Apple-platform testing procedure says to detach the Xcode debugger before forcing the crash, relaunch afterward, and inspect the dashboard. It also covers symbol-upload setup and submission logs if a report remains missing.

A repeatable verification record should include:

  • The exact app, Firebase project and build version.
  • The packaged collection configuration and effective state before the probe changes it.
  • The test event used: non-fatal or native fatal.
  • Whether the debugger was attached.
  • The relaunch and submission evidence.
  • The report that actually appeared, or the failure observed.

This lesson demonstrates the procedure with labelled illustrations. It does not show a newly delivered report from Podium. Android behavior also needs its own platform-specific check.

Do not turn a hypothesis into a diagnosis

Commit 7eefdb2 also discusses an early first-launch RTL host reload. The author suspected that tearing down the host during startup could contribute to the failure. The commit explicitly leaves the cause unconfirmed until a Crashlytics stack supports it.

The reporting fix makes further investigation possible. It does not prove the host reload caused the original crash. Keep that distinction in the incident record, especially when a configuration change and a suspected crash fix ship together.

Evidence reviewed

Evidence What it establishes What it does not establish
Owner commit 7eefdb2 Investigation intent and the configuration/probe changes A confirmed original crash cause
Installed RNFB 25.1.0 app build script The inspected auto-disabler branch A newly built artifact's contents
Per-app firebase.json Committed configuration Effective state of every installed app
Developer probe and screen handlers Available test paths and their enablement behavior Successful server delivery
Official Firebase testing guide The recommended Apple-platform verification procedure A test executed in this lesson

Test checklist

  1. Confirm the app and the installed SDK versions.
  2. Trace the resolved configuration into the native build script.
  3. Rebuild and inspect the actual packaged plist.
  4. Read effective collection state before a test button changes it.
  5. Check the previous-run crash state without treating it as an upload receipt.
  6. Exercise a non-fatal event in a controlled test build.
  7. Exercise the confirmed native crash with the debugger detached.
  8. Relaunch and inspect real submission/report evidence.
  9. Repeat the relevant checks for each branded app and platform.
  10. Investigate the original cause only with supported crash evidence.

References and series

The article remains a draft until its publication is authorized. The embedded long video is mapped to the supplied upload; it may remain unavailable while private. Short upload.

Comments

No account needed.

  1. Loading comments…