From 6b33570672f3b004ebdfd6a9961e6422a4551912 Mon Sep 17 00:00:00 2001 From: Jack Gill Date: Thu, 15 Oct 2026 11:59:47 +0100 Subject: [PATCH] fix(plugin-firebase): use the modular API functions instead of instance methods React Native Firebase v22 deprecates calling methods on the analytics instance. #1075 created the instance with getAnalytics(getApp()), but the plugin still called logEvent, logScreenView, setUserId, setUserProperties and resetAnalyticsData on that instance. Each call logged a deprecation warning. Call the modular functions with the instance as the first argument. logScreenView is deprecated in both APIs, so send a screen_view event with logEvent and keep the screen_name and screen_class payload. Refs #1105 --- .../plugin-firebase/src/FirebasePlugin.tsx | 5 ++- .../src/methods/___tests__/identify.test.ts | 27 ++++++++++---- .../src/methods/___tests__/reset.test.ts | 10 +++-- .../src/methods/___tests__/screen.test.ts | 28 +++++++++++--- .../src/methods/___tests__/track.test.ts | 37 +++++++++++++++---- .../plugin-firebase/src/methods/reset.ts | 3 +- .../plugin-firebase/src/methods/screen.ts | 7 +++- .../plugin-firebase/src/methods/track.ts | 3 +- 8 files changed, 89 insertions(+), 31 deletions(-) diff --git a/packages/plugins/plugin-firebase/src/FirebasePlugin.tsx b/packages/plugins/plugin-firebase/src/FirebasePlugin.tsx index d26b3eaaf..eab7ff039 100644 --- a/packages/plugins/plugin-firebase/src/FirebasePlugin.tsx +++ b/packages/plugins/plugin-firebase/src/FirebasePlugin.tsx @@ -11,13 +11,14 @@ import screen from './methods/screen'; import track from './methods/track'; import reset from './methods/reset'; import { firebaseAnalytics } from './firebaseAnalytics'; +import { setUserId, setUserProperties } from '@react-native-firebase/analytics'; export class FirebasePlugin extends DestinationPlugin { type = PluginType.destination; key = 'Firebase'; async identify(event: IdentifyEventType) { if (event.userId !== undefined) { - await firebaseAnalytics.setUserId(event.userId); + await setUserId(firebaseAnalytics, event.userId); } if (event.traits) { const eventTraits = event.traits; @@ -45,7 +46,7 @@ export class FirebasePlugin extends DestinationPlugin { {} ); - await firebaseAnalytics.setUserProperties(safeTraits); + await setUserProperties(firebaseAnalytics, safeTraits); } return event; } diff --git a/packages/plugins/plugin-firebase/src/methods/___tests__/identify.test.ts b/packages/plugins/plugin-firebase/src/methods/___tests__/identify.test.ts index 9a957a37f..1151b2884 100644 --- a/packages/plugins/plugin-firebase/src/methods/___tests__/identify.test.ts +++ b/packages/plugins/plugin-firebase/src/methods/___tests__/identify.test.ts @@ -1,11 +1,16 @@ const mockSetUserId = jest.fn(); const mockSetUserProperties = jest.fn(); +const mockDeprecatedSetUserId = jest.fn(); +const mockDeprecatedSetUserProperties = jest.fn(); +const mockAnalytics = { + setUserId: mockDeprecatedSetUserId, + setUserProperties: mockDeprecatedSetUserProperties, +}; jest.mock('@react-native-firebase/analytics', () => ({ - getAnalytics: jest.fn().mockImplementation(() => ({ - setUserId: mockSetUserId, - setUserProperties: mockSetUserProperties, - })), + getAnalytics: jest.fn().mockImplementation(() => mockAnalytics), + setUserId: mockSetUserId, + setUserProperties: mockSetUserProperties, isString: (a: unknown) => typeof a === 'string', })); jest.mock('@react-native-firebase/app', () => ({ @@ -34,7 +39,7 @@ describe('#identify', () => { expect(mockSetUserId).toHaveBeenCalledTimes(1); expect(mockSetUserProperties).not.toHaveBeenCalled(); - expect(mockSetUserId).toHaveBeenCalledWith('123'); + expect(mockSetUserId).toHaveBeenCalledWith(mockAnalytics, '123'); }); it('forwards the identify event with ID and properties', async () => { @@ -54,8 +59,12 @@ describe('#identify', () => { expect(mockSetUserId).toHaveBeenCalledTimes(1); expect(mockSetUserProperties).toHaveBeenCalledTimes(1); - expect(mockSetUserId).toHaveBeenCalledWith('123'); - expect(mockSetUserProperties).toHaveBeenCalledWith({ name: 'Mary' }); + expect(mockSetUserId).toHaveBeenCalledWith(mockAnalytics, '123'); + expect(mockSetUserProperties).toHaveBeenCalledWith(mockAnalytics, { + name: 'Mary', + }); + expect(mockDeprecatedSetUserId).not.toHaveBeenCalled(); + expect(mockDeprecatedSetUserProperties).not.toHaveBeenCalled(); }); it('forwards the identify event without ID', async () => { @@ -74,6 +83,8 @@ describe('#identify', () => { expect(mockSetUserId).not.toHaveBeenCalled(); expect(mockSetUserProperties).toHaveBeenCalledTimes(1); - expect(mockSetUserProperties).toHaveBeenCalledWith({ name: 'Mary' }); + expect(mockSetUserProperties).toHaveBeenCalledWith(mockAnalytics, { + name: 'Mary', + }); }); }); diff --git a/packages/plugins/plugin-firebase/src/methods/___tests__/reset.test.ts b/packages/plugins/plugin-firebase/src/methods/___tests__/reset.test.ts index 3ec925070..f84e52a5e 100644 --- a/packages/plugins/plugin-firebase/src/methods/___tests__/reset.test.ts +++ b/packages/plugins/plugin-firebase/src/methods/___tests__/reset.test.ts @@ -1,9 +1,10 @@ const mockReset = jest.fn(); +const mockDeprecatedReset = jest.fn(); +const mockAnalytics = { resetAnalyticsData: mockDeprecatedReset }; jest.mock('@react-native-firebase/analytics', () => ({ - getAnalytics: jest.fn().mockImplementation(() => ({ - resetAnalyticsData: mockReset, - })), + getAnalytics: jest.fn().mockImplementation(() => mockAnalytics), + resetAnalyticsData: mockReset, })); jest.mock('@react-native-firebase/app', () => ({ @@ -21,6 +22,7 @@ describe('#reset', () => { await reset(); expect(mockReset).toHaveBeenCalledTimes(1); - expect(mockReset).toHaveBeenCalledWith(); + expect(mockReset).toHaveBeenCalledWith(mockAnalytics); + expect(mockDeprecatedReset).not.toHaveBeenCalled(); }); }); diff --git a/packages/plugins/plugin-firebase/src/methods/___tests__/screen.test.ts b/packages/plugins/plugin-firebase/src/methods/___tests__/screen.test.ts index 39c924c27..628253e18 100644 --- a/packages/plugins/plugin-firebase/src/methods/___tests__/screen.test.ts +++ b/packages/plugins/plugin-firebase/src/methods/___tests__/screen.test.ts @@ -1,9 +1,10 @@ -const mockScreen = jest.fn(); +const mockLogEvent = jest.fn(); +const mockDeprecatedLogScreenView = jest.fn(); +const mockAnalytics = { logScreenView: mockDeprecatedLogScreenView }; jest.mock('@react-native-firebase/analytics', () => ({ - getAnalytics: jest.fn().mockImplementation(() => ({ - logScreenView: mockScreen, - })), + getAnalytics: jest.fn().mockImplementation(() => mockAnalytics), + logEvent: mockLogEvent, })); jest.mock('@react-native-firebase/app', () => ({ @@ -30,10 +31,25 @@ describe('#screen', () => { await screen(event); - expect(mockScreen).toHaveBeenCalledTimes(1); - expect(mockScreen).toHaveBeenCalledWith({ + expect(mockLogEvent).toHaveBeenCalledTimes(1); + expect(mockLogEvent).toHaveBeenCalledWith(mockAnalytics, 'screen_view', { screen_name: 'HomeScreen', screen_class: 'HomeScreen', }); }); + + it('does not call the deprecated logScreenView method', async () => { + const event = { + type: 'screen', + name: 'HomeScreen', + anonymousId: 'anon', + messageId: 'message-id', + timestamp: '00000', + properties: {}, + } as ScreenEventType; + + await screen(event); + + expect(mockDeprecatedLogScreenView).not.toHaveBeenCalled(); + }); }); diff --git a/packages/plugins/plugin-firebase/src/methods/___tests__/track.test.ts b/packages/plugins/plugin-firebase/src/methods/___tests__/track.test.ts index 1ae83af93..565d688f2 100644 --- a/packages/plugins/plugin-firebase/src/methods/___tests__/track.test.ts +++ b/packages/plugins/plugin-firebase/src/methods/___tests__/track.test.ts @@ -1,9 +1,10 @@ const mockLogEvent = jest.fn(); +const mockDeprecatedLogEvent = jest.fn(); +const mockAnalytics = { logEvent: mockDeprecatedLogEvent }; jest.mock('@react-native-firebase/analytics', () => ({ - getAnalytics: jest.fn().mockImplementation(() => ({ - logEvent: mockLogEvent, - })), + getAnalytics: jest.fn().mockImplementation(() => mockAnalytics), + logEvent: mockLogEvent, isString: (a: unknown) => typeof a === 'string', })); jest.mock('@react-native-firebase/app', () => ({ @@ -32,7 +33,22 @@ describe('#track', () => { await track(event); expect(mockLogEvent).toHaveBeenCalledTimes(1); - expect(mockLogEvent).toHaveBeenCalledWith('test_event', {}); + expect(mockLogEvent).toHaveBeenCalledWith(mockAnalytics, 'test_event', {}); + }); + + it('uses the modular API instead of the deprecated instance method', async () => { + const event = { + type: EventType.TrackEvent, + event: 'test_event', + anonymousId: 'anon', + messageId: 'message-id', + timestamp: '00000', + properties: {}, + } as TrackEventType; + + await track(event); + + expect(mockDeprecatedLogEvent).not.toHaveBeenCalled(); }); it('forwards a track event with name and properties', async () => { @@ -48,9 +64,13 @@ describe('#track', () => { await track(event); expect(mockLogEvent).toHaveBeenCalledTimes(1); - expect(mockLogEvent).toHaveBeenCalledWith('another_test_event', { - foo: 'bar', - }); + expect(mockLogEvent).toHaveBeenCalledWith( + mockAnalytics, + 'another_test_event', + { + foo: 'bar', + } + ); }); it('removes non-alphanumeric characters from', async () => { @@ -67,6 +87,7 @@ describe('#track', () => { expect(mockLogEvent).toHaveBeenCalledTimes(1); expect(mockLogEvent).toHaveBeenCalledWith( + mockAnalytics, 'yet_another__test____event_CAPS', {} ); @@ -85,6 +106,6 @@ describe('#track', () => { await track(event); expect(mockLogEvent).toHaveBeenCalledTimes(1); - expect(mockLogEvent).toHaveBeenCalledWith('refund', {}); + expect(mockLogEvent).toHaveBeenCalledWith(mockAnalytics, 'refund', {}); }); }); diff --git a/packages/plugins/plugin-firebase/src/methods/reset.ts b/packages/plugins/plugin-firebase/src/methods/reset.ts index ae8937b17..99fe5b1a0 100644 --- a/packages/plugins/plugin-firebase/src/methods/reset.ts +++ b/packages/plugins/plugin-firebase/src/methods/reset.ts @@ -1,5 +1,6 @@ +import { resetAnalyticsData } from '@react-native-firebase/analytics'; import { firebaseAnalytics } from '../firebaseAnalytics'; export default async () => { - await firebaseAnalytics.resetAnalyticsData(); + await resetAnalyticsData(firebaseAnalytics); }; diff --git a/packages/plugins/plugin-firebase/src/methods/screen.ts b/packages/plugins/plugin-firebase/src/methods/screen.ts index 538886343..e15a9b376 100644 --- a/packages/plugins/plugin-firebase/src/methods/screen.ts +++ b/packages/plugins/plugin-firebase/src/methods/screen.ts @@ -1,4 +1,5 @@ import type { ScreenEventType } from '@segment/analytics-react-native'; +import { logEvent } from '@react-native-firebase/analytics'; import { firebaseAnalytics } from '../firebaseAnalytics'; export default async (event: ScreenEventType) => { @@ -8,5 +9,9 @@ export default async (event: ScreenEventType) => { ...event.properties, }; - await firebaseAnalytics.logScreenView(screenProps); + // logScreenView is deprecated in both the namespaced and modular APIs. + // The typings of the 'screen_view' overload use the web parameter names + // (firebase_screen), but the native SDKs read screen_name and screen_class, + // which is what logScreenView forwarded. Keep the payload and cast the type. + await logEvent(firebaseAnalytics, 'screen_view', screenProps as never); }; diff --git a/packages/plugins/plugin-firebase/src/methods/track.ts b/packages/plugins/plugin-firebase/src/methods/track.ts index 330fd4e34..01638242b 100644 --- a/packages/plugins/plugin-firebase/src/methods/track.ts +++ b/packages/plugins/plugin-firebase/src/methods/track.ts @@ -2,6 +2,7 @@ import { type TrackEventType, generateMapTransform, } from '@segment/analytics-react-native'; +import { logEvent } from '@react-native-firebase/analytics'; import { firebaseAnalytics } from '../firebaseAnalytics'; import { mapEventProps, transformMap } from './parameterMapping'; @@ -22,5 +23,5 @@ export default async (event: TrackEventType) => { if (safeEventName.length > 40) { safeEventName = safeEventName.substring(0, 40); } - await firebaseAnalytics.logEvent(safeEventName, safeProps); + await logEvent(firebaseAnalytics, safeEventName, safeProps); };