Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ To help ensure this plugin is kept updated, new features are added and bugfixes
- [setScreenName](#setscreenname)
- [setUserId](#setuserid)
- [setUserProperty](#setuserproperty)
- [getAppInstanceId](#getappinstanceid)
- [initiateOnDeviceConversionMeasurement](#initiateondeviceconversionmeasurement)
- [Crashlytics](#crashlytics)
- [setCrashlyticsCollectionEnabled](#setcrashlyticscollectionenabled)
Expand Down Expand Up @@ -2580,6 +2581,25 @@ Set a user property for use in Analytics:
FirebasePlugin.setUserProperty("name", "value");
```

### getAppInstanceId

Get the app instance ID for use with the [Google Analytics Measurement Protocol](https://developers.google.com/analytics/devguides/collection/protocol/ga4). The app instance ID is used to join Measurement Protocol events with online interactions using the `app_instance_id` parameter, but Measurement Protocol events will only attribute if the `app_instance_id` has already been observed by the Firebase SDK in that app session.

**Parameters**:

- {function} success - callback function which will be invoked on success.
Will be passed a {string} containing the app instance ID (or `null` if Analytics storage consent is denied or Analytics collection is disabled, per [Firebase Analytics consent behavior](https://firebase.google.com/docs/analytics/consent)).
- {function} error - (optional) callback function which will be passed a {string} error message as an argument

```javascript
FirebasePlugin.getAppInstanceId(function(appInstanceId) {
console.log("App Instance ID: " + appInstanceId);
// Use appInstanceId with Google Analytics Measurement Protocol
}, function(error) {
console.error("Error getting app instance ID: " + error);
});
```
Comment on lines +2584 to +2601
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Clarify GA4 Measurement Protocol attribution and add cross‑refs

Two tweaks:

  • Replace “only attribute if ... observed by the Firebase SDK in that app session” with “only attribute if the app_instance_id has been previously observed by the Firebase SDK for this app.” The “same session” requirement is stricter than GA4 docs imply; please verify. Also add a note that this ID is distinct from Firebase Installation ID (FID) exposed by getInstallationId.
  • Add a brief consent gating note so consumers expect null and can branch accordingly.

Suggested doc edit:

-Get the app instance ID for use with the Google Analytics Measurement Protocol (...). The app instance ID is used to join Measurement Protocol events with online interactions using the `app_instance_id` parameter, but Measurement Protocol events will only attribute if the `app_instance_id` has already been observed by the Firebase SDK in that app session.
+Get the Firebase Analytics app instance ID for use with the Google Analytics Measurement Protocol (...). Use the `app_instance_id` parameter to join Measurement Protocol events with client‑side analytics. Attribution occurs only after the same `app_instance_id` has been observed by the Firebase SDK for this app. (Not the same as the Firebase Installation ID (FID) returned by [getInstallationId](#getinstallationid).)
+
+Note: This method may return `null` until analytics consent/collection is granted/enabled. You can gate usage with `isAnalyticsCollectionEnabled` or set consent via `setAnalyticsConsentMode`.

Please verify the attribution wording against GA4 MP docs:

Optionally update the example to demonstrate a null check before using the ID.


🌐 Web query:

GA4 Measurement Protocol app_instance_id attribution previously observed session requirement

💡 Result:

Short answer

  • GA4 Measurement Protocol for App streams requires a valid app_instance_id that was already produced/used by the Firebase (Google Analytics for Firebase) SDK — Measurement Protocol events must include that app_instance_id to be accepted/verified. [1][2]
  • If you want those MP events to join/receive the same session attribution as an online session, include session_id and send the MP request within 24 hours of that session's start. For advertising export there are longer windows (e.g., up to 63 days for some use cases). [2][3]

Sources

  • Verify implementation (GA4 Measurement Protocol): app_instance_id must have been used by the Firebase SDK. [1]
  • Measurement Protocol use cases / session attribution and export windows (session_id within 24 hours; ad export up to 63 days). [2]
  • Measurement Protocol reference: session_id and engagement_time_msec requirements for session-based reporting. [3]

I need to verify two more aspects to complete the assessment:

  1. Consent gating behavior - whether getAppInstanceId actually returns null under consent denial, as the reviewer suggests
  2. FID distinction accuracy - whether the cross-reference to getInstallationId is correct

Based on my verification of the review comment against Firebase and GA4 documentation:

All review suggestions are accurate and well-founded

getAppInstanceId returns null if ANALYTICS_STORAGE consent is DENIED, confirming the consent gating note is appropriate. The app instance ID is associated with the Firebase Analytics instance, whereas the Installation ID covers broader scope and is used for various Firebase services, validating the distinction from getInstallationId. The GA4 MP wording change from "same session" to "previously observed by the Firebase SDK for this app" aligns with actual GA4 behavior, where session attribution requires session_id (not just app_instance_id in the same session).

Suggested documentation update is valid and recommended for:

  • Clarifying that app_instance_id is distinct from Firebase Installation ID
  • Adding consent gating behavior so developers expect null and can branch accordingly
  • Improving GA4 MP attribution explanation
  • Demonstrating null check pattern in example code



### initiateOnDeviceConversionMeasurement

Expand Down
16 changes: 16 additions & 0 deletions src/android/FirebasePlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,9 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo
case "setUserProperty":
this.setUserProperty(callbackContext, args.getString(0), args.getString(1));
break;
case "getAppInstanceId":
this.getAppInstanceId(callbackContext);
break;
case "activateFetched":
this.activateFetched(callbackContext);
break;
Expand Down Expand Up @@ -1163,6 +1166,19 @@ public void run() {
});
}

private void getAppInstanceId(final CallbackContext callbackContext) {
cordova.getThreadPool().execute(new Runnable() {
@Override
public void run() {
try {
handleTaskOutcomeWithStringResult(mFirebaseAnalytics.getAppInstanceId(), callbackContext);
} catch (Exception e) {
handleExceptionWithContext(e, callbackContext);
}
}
});
}

private void fetch(CallbackContext callbackContext) {
fetch(callbackContext, FirebaseRemoteConfig.getInstance().fetch());
}
Expand Down
1 change: 1 addition & 0 deletions src/ios/FirebasePlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
- (void)setUserId:(CDVInvokedUrlCommand*)command;
- (void)setUserProperty:(CDVInvokedUrlCommand*)command;
- (void)initiateOnDeviceConversionMeasurement:(CDVInvokedUrlCommand*)command;
- (void)getAppInstanceId:(CDVInvokedUrlCommand*)command;

// Crashlytics
- (void)setCrashlyticsCollectionEnabled:(CDVInvokedUrlCommand*)command;
Expand Down
27 changes: 27 additions & 0 deletions src/ios/FirebasePlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -1750,6 +1750,33 @@ - (void) initiateOnDeviceConversionMeasurement:(CDVInvokedUrlCommand*)command {
}];
}

- (void)getAppInstanceId:(CDVInvokedUrlCommand*)command {
[self.commandDelegate runInBackground:^{
@try {
NSString *appInstanceID = [FIRAnalytics appInstanceID];

CDVPluginResult *pluginResult = nil;

if (appInstanceID != nil && appInstanceID.length > 0) {
pluginResult = [CDVPluginResult
resultWithStatus:CDVCommandStatus_OK
messageAsString:appInstanceID];
} else {
NSString *errorMessage = @"Failed to get app instance ID: value is nil or empty. Ensure Firebase Analytics is initialized and collection is enabled.";
pluginResult = [CDVPluginResult
resultWithStatus:CDVCommandStatus_ERROR
messageAsString:errorMessage];
}

[self.commandDelegate sendPluginResult:pluginResult
callbackId:command.callbackId];
}
@catch (NSException *exception) {
[self handlePluginExceptionWithContext:exception :command];
}
}];
}

/*
* Crashlytics
*/
Expand Down
4 changes: 4 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ export interface FirebasePlugin {
userName: string,
userValue: string
): void
getAppInstanceId(
success?: (appInstanceId: string | null) => void,
error?: (err: string) => void
): void
initiateOnDeviceConversionMeasurement(
userIdentifier: { emailAddress?:string, phoneNumber?: string },
success?: () => void,
Expand Down
4 changes: 4 additions & 0 deletions www/firebase.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,10 @@ exports.initiateOnDeviceConversionMeasurement = function(userIdentifier, success
exec(success, error, "FirebasePlugin", "initiateOnDeviceConversionMeasurement", [userIdentifier]);
}

exports.getAppInstanceId = function(success, error) {
exec(success, error, "FirebasePlugin", "getAppInstanceId", []);
};

exports.fetch = function (cacheExpirationSeconds, success, error) {
var args = [];
if (typeof cacheExpirationSeconds === 'number') {
Expand Down