Skip to content

Firebase

Herald's Firebase module sends to Firebase Analytics (GA4). Firebase usually gets every event, with typed parameters, and screen views arrive as GA4's own screen_view event.

SDK Package
Android io.github.mkhytarmkhoian:herald-firebase
Flutter herald_firebase, over firebase_analytics
iOS herald-ios-firebase, module HeraldFirebase, over the Firebase iOS SDK
React Native @herald-analytics/firebase, over @react-native-firebase/analytics

Setup

Firebase sets itself up from its config files: google-services.json on Android, on Flutter whatever flutterfire configure generated, once Firebase.initializeApp() has run, and on iOS GoogleService-Info.plist, once FirebaseApp.configure() has run. React Native Firebase reads both native files, which an Expo app names in app.json. Herald takes the FirebaseAnalytics instance, the Analytics from getAnalytics() on React Native, and never changes its settings. On iOS, Firebase Analytics is static functions, so Herald's classes take no Firebase object.

fun firebaseProvider(firebaseAnalytics: FirebaseAnalytics): Herald.Provider {
    val tracker = FirebaseAnalyticsTrackerService(
        CompositeFirebaseEventTrackerFactory(
            ScreenViewFirebaseEventTrackerFactory(firebaseAnalytics), // GA4's reserved screen_view
            GenericFirebaseEventTrackerFactory(firebaseAnalytics),    // everything else, as-is
        ),
        CompositeFirebasePropertySetterFactory(
            GenericFirebasePropertySetterFactory(firebaseAnalytics),
        ),
    )
    val service = FirebaseAnalyticsService(firebaseAnalytics)
    return Herald.Provider(
        name = "firebase",
        events = tracker,
        properties = tracker,
        identity = service,
        lifecycle = service,
        consent = service,
    )
}
func firebaseProvider() -> HeraldProvider {
    let tracker = FirebaseAnalyticsTrackerService(
        eventTrackerFactory: CompositeFirebaseEventTrackerFactory([
            ScreenViewFirebaseEventTrackerFactory(),  // GA4's reserved screen_view
            GenericFirebaseEventTrackerFactory(),  // everything else, as-is
        ]),
        propertySetterFactory: GenericFirebasePropertySetterFactory()
    )
    let service = FirebaseAnalyticsService()
    return HeraldProvider(
        name: "firebase",
        events: tracker,
        properties: tracker,
        identity: service,
        lifecycle: service,
        consent: service
    )
}
HeraldProvider firebaseProvider(FirebaseAnalytics firebaseAnalytics) {
  final tracker = FirebaseAnalyticsTrackerService(
    eventTrackerFactory: CompositeFirebaseEventTrackerFactory([
      ScreenViewFirebaseEventTrackerFactory(firebaseAnalytics), // GA4's reserved screen_view
      GenericFirebaseEventTrackerFactory(firebaseAnalytics), // everything else, as-is
    ]),
    propertySetterFactory: CompositeFirebasePropertySetterFactory([
      GenericFirebasePropertySetterFactory(firebaseAnalytics),
    ]),
  );
  final service = FirebaseAnalyticsService(firebaseAnalytics);
  return HeraldProvider(
    name: 'firebase',
    events: tracker,
    properties: tracker,
    identity: service,
    lifecycle: service,
    consent: service,
  );
}
export function firebaseProvider(firebaseAnalytics: Analytics): HeraldProvider {
  const tracker = new FirebaseAnalyticsTrackerService({
    eventTrackerFactory: new CompositeFirebaseEventTrackerFactory([
      new ScreenViewFirebaseEventTrackerFactory(firebaseAnalytics), // GA4's reserved screen_view
      new GenericFirebaseEventTrackerFactory(firebaseAnalytics), // everything else, as-is
    ]),
    propertySetterFactory: new CompositeFirebasePropertySetterFactory([
      new GenericFirebasePropertySetterFactory(firebaseAnalytics),
    ]),
  });
  const service = new FirebaseAnalyticsService(firebaseAnalytics);
  return new HeraldProvider({
    name: 'firebase',
    events: tracker,
    properties: tracker,
    identity: service,
    lifecycle: service,
    consent: service,
  });
}

What reaches Firebase

Herald Firebase
an event logEvent(name, bundle): integers via putLong, reals via putDouble, text and booleans as strings
a ScreenViewEvent logEvent("screen_view") with its name as screen_name, sent by ScreenViewFirebaseEventTrackerFactory; a screen view with its own screen_name parameter is refused and reported
a property, including a UserProperty setUserProperty(name, value), as text
identify / reset setUserId(userId) / setUserId(null)
setEnabled setAnalyticsCollectionEnabled(enabled)
start, flush nothing: Firebase starts and delivers on its own
Herald Firebase
an event Analytics.logEvent(name, parameters:): numbers as numbers, text and booleans as strings
a ScreenViewEvent Analytics.logEvent(AnalyticsEventScreenView) with its name as screen_name, sent by ScreenViewFirebaseEventTrackerFactory; a screen view with its own screen_name parameter is refused and reported
a property, including a UserProperty Analytics.setUserProperty(value, forName:), as text
identify / reset Analytics.setUserID(userId) / Analytics.setUserID(nil)
setEnabled Analytics.setAnalyticsCollectionEnabled(enabled)
start, flush nothing: Firebase starts and delivers on its own
Herald Firebase
an event logEvent(name:, parameters:): numbers as numbers, text and booleans as strings
a ScreenViewEvent logScreenView(screenName: name, parameters:), sent by ScreenViewFirebaseEventTrackerFactory; a screen view with its own screen_name parameter is refused and reported
a property, including a UserProperty setUserProperty(name:, value:), as text
identify / reset setUserId(id: userId) / setUserId()
setEnabled setAnalyticsCollectionEnabled(enabled)
start, flush nothing: Firebase starts and delivers on its own
Herald Firebase
an event logEvent(analytics, name, parameters): numbers as numbers, text and booleans as strings
a ScreenViewEvent logEvent(analytics, 'screen_view') with its name as screen_name, sent by ScreenViewFirebaseEventTrackerFactory; a screen view with its own screen_name parameter is refused and reported
a property, including a UserProperty setUserProperty(analytics, name, value), as text
identify / reset setUserId(analytics, userId) / setUserId(analytics, null)
setEnabled setAnalyticsCollectionEnabled(analytics, enabled)
start, flush nothing: Firebase starts and delivers on its own

Factories

Factory Handles
ScreenViewFirebaseEventTrackerFactory every ScreenViewEvent
GenericFirebaseEventTrackerFactory everything else; usually last in the chain
RequireMappedFirebaseEventTrackerFactory nothing: throws for any event that reaches it
GenericFirebasePropertySetterFactory, RequireMappedFirebasePropertySetterFactory the same, for properties

To send one of GA4's recommended events (purchase, refund, select_content, …), either name your event after it with its parameters, or write a tracker for it. See Custom markers. Its parameters convert as Herald's do with parameters.toFirebaseParameters(), or toFirebaseParameters(parametersOf(event)) in TypeScript.

A fresh install collects until your app says otherwise. Firebase's off switch is a native setting, read before any Herald code runs. On Flutter, set it in both native projects; on iOS, in Info.plist; on React Native, in firebase.json, which React Native Firebase writes into both:

AndroidManifest.xml
<meta-data android:name="firebase_analytics_collection_enabled" android:value="false" />
Info.plist (iOS; ios/Runner/Info.plist on Flutter)
<key>FIREBASE_ANALYTICS_COLLECTION_ENABLED</key>
<false/>
firebase.json (React Native, next to package.json)
{
  "react-native": {
    "analytics_auto_collection_enabled": false
  }
}

setEnabled(true) then turns collection on, and Firebase remembers the choice across launches. Consent Mode (setConsent) is set on FirebaseAnalytics directly.

Identity

To never send a user id to Firebase, register the Firebase provider without identity.

Watch out for

  • GA4's limits. GA4 truncates or drops data beyond them: 40-character event names, 25 parameters per event, 100-character parameter values, and 24-character user property names with 36-character values. Structured naming conventions hit these first.
  • Names GA4 keeps for itself. GA4 reserves event names such as app_remove and prefixes such as firebase_, google_ and ga_. Firebase drops an event that uses them; Herald doesn't.