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.
Consent¶
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:
<meta-data android:name="firebase_analytics_collection_enabled" android:value="false" />
<key>FIREBASE_ANALYTICS_COLLECTION_ENABLED</key>
<false/>
{
"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_removeand prefixes such asfirebase_,google_andga_. Firebase drops an event that uses them; Herald doesn't.