Description
When running firebase-analytics on the Kotlin/JS target, calling the common API function FirebaseAnalytics.setUserProperty(name, value) throws a runtime TypeError: setUserProperty is not a function.
Underlying Cause
In the jsMain actual wrapper for FirebaseAnalytics, the library defines an external binding mapping directly to an export named setUserProperty in the "firebase/analytics" NPM module:
// dev.gitlive.firebase.analytics.externals.analytics.kt
@file:JsModule("firebase/analytics")
@file:JsNonModule
package dev.gitlive.firebase.analytics.externals
public external fun setUserProperty(app: FirebaseAnalytics, name: String, value: String)
However, the official Firebase JS SDK (v9/v10 modular API) does not export a function named setUserProperty (singular) under "firebase/analytics". Instead, the official JS SDK only exports setUserProperties (plural) which takes an analytics instance and a properties object:
import { setUserProperties } from "firebase/analytics";
setUserProperties(analytics, { favorite_food: "apples" });
Because of this, the imported JS function resolves to undefined at runtime, causing any invocation of setUserProperty(...) in Kotlin/JS common code to crash.
Environment Details
- Library Group/Artifact:
dev.gitlive:firebase-analytics / dev.gitlive:firebase-analytics-js
- Library Version:
2.4.0
- Kotlin Compiler Version:
2.3.21 (Kotlin/JS)
- Firebase JS SDK NPM version:
10.13.2
- Platform Target: Kotlin/JS (Browser Webpack / Web target)
Steps to Reproduce
- In
commonMain code, instantiate and call Firebase Analytics:
val analytics = Firebase.analytics
analytics.setUserProperty("app_os_family", "Web")
- Build and launch the Kotlin/JS target in a browser environment.
- The application crashes immediately during initialization with the following stack trace:
TypeError: setUserProperty is not a function
at protoOf.setUserProperty_rx7df6_k$ (webpack-internal:///./kotlin/firebase-kotlin-sdk-firebase-analytics.js:225:5)
at firebaseModule$lambda$lambda (webpack-internal:///./kotlin/EvolynClient-data.js:28802:12)
Expected Behavior
The library's setUserProperty(name, value) implementation should map to the official JS SDK's setUserProperties method under the hood:
// Expected jsMain delegation mapping
public actual fun setUserProperty(name: String, value: String) {
setUserProperties(this.js, json(name to value))
}
Workaround / Mitigation
We mitigated this locally in our codebase by declaring the correct external JS binding and using an expect/actual extension to bypass the broken wrapper function on the JS target:
// webMain actual mapping
@file:JsModule("firebase/analytics")
@file:JsNonModule
package app.evolyn.data.firebase
import dev.gitlive.firebase.analytics.externals.FirebaseAnalytics
@JsName("setUserProperties")
public external fun setUserProperties(app: FirebaseAnalytics, properties: dynamic)
Description
When running
firebase-analyticson the Kotlin/JS target, calling the common API functionFirebaseAnalytics.setUserProperty(name, value)throws a runtimeTypeError: setUserProperty is not a function.Underlying Cause
In the
jsMainactual wrapper forFirebaseAnalytics, the library defines an external binding mapping directly to an export namedsetUserPropertyin the"firebase/analytics"NPM module:However, the official Firebase JS SDK (v9/v10 modular API) does not export a function named
setUserProperty(singular) under"firebase/analytics". Instead, the official JS SDK only exportssetUserProperties(plural) which takes an analytics instance and a properties object:Because of this, the imported JS function resolves to
undefinedat runtime, causing any invocation ofsetUserProperty(...)in Kotlin/JS common code to crash.Environment Details
dev.gitlive:firebase-analytics/dev.gitlive:firebase-analytics-js2.4.02.3.21(Kotlin/JS)10.13.2Steps to Reproduce
commonMaincode, instantiate and call Firebase Analytics:Expected Behavior
The library's
setUserProperty(name, value)implementation should map to the official JS SDK'ssetUserPropertiesmethod under the hood:Workaround / Mitigation
We mitigated this locally in our codebase by declaring the correct external JS binding and using an expect/actual extension to bypass the broken wrapper function on the JS target: