From a971498e43ccce543c97f98b499cec0371327880 Mon Sep 17 00:00:00 2001 From: Sunita Prajapati <> Date: Tue, 22 Oct 2024 12:10:53 +0530 Subject: [PATCH 1/6] fix: to show 'Queue restoration timeout' only once --- packages/core/src/plugins/QueueFlushingPlugin.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/core/src/plugins/QueueFlushingPlugin.ts b/packages/core/src/plugins/QueueFlushingPlugin.ts index 49279366..4ca749aa 100644 --- a/packages/core/src/plugins/QueueFlushingPlugin.ts +++ b/packages/core/src/plugins/QueueFlushingPlugin.ts @@ -84,15 +84,15 @@ export class QueueFlushingPlugin extends UtilityPlugin { } } catch (e) { // If the queue is not restored before the timeout, we will notify but not block flushing events - this.analytics?.reportInternalError( - new SegmentError( - ErrorType.InitializationError, - 'Queue restoration timeout', - e - ) - ); - if (this.timeoutWarned === false) { + this.analytics?.reportInternalError( + new SegmentError( + ErrorType.InitializationError, + 'Queue restoration timeout', + e + ) + ); + this.analytics?.logger.warn( 'Flush triggered but queue restoration and settings loading not complete. Flush will be retried.', e From 867e52afb8239eed5d7112a36d74e34d9aed6e9f Mon Sep 17 00:00:00 2001 From: Sunita Prajapati <> Date: Wed, 23 Oct 2024 12:08:08 +0530 Subject: [PATCH 2/6] fix: advertising id being sent for Android application installed events --- .../src/AdvertisingIdPlugin.ts | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/plugins/plugin-advertising-id/src/AdvertisingIdPlugin.ts b/packages/plugins/plugin-advertising-id/src/AdvertisingIdPlugin.ts index 612fcba1..b16c28af 100644 --- a/packages/plugins/plugin-advertising-id/src/AdvertisingIdPlugin.ts +++ b/packages/plugins/plugin-advertising-id/src/AdvertisingIdPlugin.ts @@ -5,6 +5,7 @@ import { getNativeModule, ErrorType, SegmentError, + SegmentEvent } from '@segment/analytics-react-native'; import { Platform, NativeModule } from 'react-native'; @@ -15,6 +16,8 @@ type AdvertisingIDNativeModule = NativeModule & { export class AdvertisingIdPlugin extends Plugin { type = PluginType.enrichment; + queuedEvents: SegmentEvent[] = []; + advertisingId?: string = undefined; configure(analytics: SegmentClient): void { if (Platform.OS !== 'android') { @@ -34,6 +37,7 @@ export class AdvertisingIdPlugin extends Plugin { 'LimitAdTrackingEnabled (Google Play Services) is enabled' ); } else { + this.advertisingId = id void this.setContext(id); } }) @@ -48,6 +52,16 @@ export class AdvertisingIdPlugin extends Plugin { }); } + execute(event: SegmentEvent){ + + if (this.advertisingId === undefined) { + this.queuedEvents.push(event); + }else{ + return event; + } + return; + } + async setContext(id: string): Promise { try { await this.analytics?.context.set({ @@ -56,6 +70,7 @@ export class AdvertisingIdPlugin extends Plugin { adTrackingEnabled: true, }, }); + this.sendQueued(); } catch (error) { const message = 'AdvertisingID failed to set context'; this.analytics?.reportInternalError( @@ -64,4 +79,11 @@ export class AdvertisingIdPlugin extends Plugin { this.analytics?.logger.warn(`${message}: ${JSON.stringify(error)}`); } } + + sendQueued() { + this.queuedEvents.forEach(event => { + void this.analytics?.process(event); + }); + this.queuedEvents = []; + } } From 919b65d5fc5d6dd20054a64776fdd78a9695368b Mon Sep 17 00:00:00 2001 From: Sunita Prajapati <> Date: Mon, 28 Oct 2024 11:42:54 +0530 Subject: [PATCH 3/6] chore: add privacy manifest file to core package --- .../project.pbxproj | 22 +----- packages/core/ios/PrivacyInfo.xcprivacy | 70 +++++++++++++++++++ 2 files changed, 73 insertions(+), 19 deletions(-) create mode 100644 packages/core/ios/PrivacyInfo.xcprivacy diff --git a/packages/core/ios/AnalyticsReactNative.xcodeproj/project.pbxproj b/packages/core/ios/AnalyticsReactNative.xcodeproj/project.pbxproj index 9fc6613f..62ed7dac 100644 --- a/packages/core/ios/AnalyticsReactNative.xcodeproj/project.pbxproj +++ b/packages/core/ios/AnalyticsReactNative.xcodeproj/project.pbxproj @@ -7,11 +7,7 @@ objects = { /* Begin PBXBuildFile section */ - - - F4FF95D7245B92E800C19C63 /* AnalyticsReactNative.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4FF95D6245B92E800C19C63 /* AnalyticsReactNative.swift */; }; - - 5E555C0D2413F4C50049A1A2 /* AnalyticsReactNative.mm in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* AnalyticsReactNative.mm */; }; + F4FF95D7245B92E800C19C63 /* AnalyticsReactNative.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4FF95D6245B92E800C19C63 /* AnalyticsReactNative.swift */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -28,12 +24,10 @@ /* Begin PBXFileReference section */ 134814201AA4EA6300B7C361 /* libAnalyticsReactNative.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libAnalyticsReactNative.a; sourceTree = BUILT_PRODUCTS_DIR; }; - - B3E7B5891CC2AC0600A0062D /* AnalyticsReactNative.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AnalyticsReactNative.m; sourceTree = ""; }; F4FF95D5245B92E700C19C63 /* AnalyticsReactNative-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AnalyticsReactNative-Bridging-Header.h"; sourceTree = ""; }; F4FF95D6245B92E800C19C63 /* AnalyticsReactNative.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsReactNative.swift; sourceTree = ""; }; - + F615F88C2CCF5F4C007645FE /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -58,12 +52,10 @@ 58B511D21A9E6C8500147676 = { isa = PBXGroup; children = ( - - + F615F88C2CCF5F4C007645FE /* PrivacyInfo.xcprivacy */, F4FF95D6245B92E800C19C63 /* AnalyticsReactNative.swift */, B3E7B5891CC2AC0600A0062D /* AnalyticsReactNative.m */, F4FF95D5245B92E700C19C63 /* AnalyticsReactNative-Bridging-Header.h */, - 134814211AA4EA7D00B7C361 /* Products */, ); sourceTree = ""; @@ -125,11 +117,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - - F4FF95D7245B92E800C19C63 /* AnalyticsReactNative.swift in Sources */, - B3E7B58A1CC2AC0600A0062D /* AnalyticsReactNative.m in Sources */, - ); runOnlyForDeploymentPostprocessing = 0; }; @@ -242,11 +230,9 @@ OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = AnalyticsReactNative; SKIP_INSTALL = YES; - SWIFT_OBJC_BRIDGING_HEADER = "AnalyticsReactNative-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - }; name = Debug; }; @@ -263,10 +249,8 @@ OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = AnalyticsReactNative; SKIP_INSTALL = YES; - SWIFT_OBJC_BRIDGING_HEADER = "AnalyticsReactNative-Bridging-Header.h"; SWIFT_VERSION = 5.0; - }; name = Release; }; diff --git a/packages/core/ios/PrivacyInfo.xcprivacy b/packages/core/ios/PrivacyInfo.xcprivacy new file mode 100644 index 00000000..c8fd0beb --- /dev/null +++ b/packages/core/ios/PrivacyInfo.xcprivacy @@ -0,0 +1,70 @@ + + + + + NSPrivacyTracking + + NSPrivacyCollectedDataTypes + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeDeviceID + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + + + + + NSPrivacyCollectedDataType + App Name + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeDeveloperAdvertising + + + + NSPrivacyCollectedDataType + App Version + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeDeveloperAdvertising + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeAdvertisingData + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeDeveloperAdvertising + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + 1C8F.1 + + + + + \ No newline at end of file From f4b913ae3edc3ba6ddf1a7ed1e26cd8667dd65dc Mon Sep 17 00:00:00 2001 From: Sunita Prajapati <> Date: Mon, 11 Nov 2024 15:45:29 +0530 Subject: [PATCH 4/6] fix: events not being sent when adTrackingEnabled is false --- ...icsReactNativePluginAdvertisingIdModule.kt | 77 ++++++++++--------- .../src/AdvertisingIdPlugin.ts | 77 +++++++++++++------ 2 files changed, 93 insertions(+), 61 deletions(-) diff --git a/packages/plugins/plugin-advertising-id/android/src/main/java/com/reactnativeanalyticsreactnativepluginadvertisingid/AnalyticsReactNativePluginAdvertisingIdModule.kt b/packages/plugins/plugin-advertising-id/android/src/main/java/com/reactnativeanalyticsreactnativepluginadvertisingid/AnalyticsReactNativePluginAdvertisingIdModule.kt index 2442e7b2..7ceb2ac2 100644 --- a/packages/plugins/plugin-advertising-id/android/src/main/java/com/reactnativeanalyticsreactnativepluginadvertisingid/AnalyticsReactNativePluginAdvertisingIdModule.kt +++ b/packages/plugins/plugin-advertising-id/android/src/main/java/com/reactnativeanalyticsreactnativepluginadvertisingid/AnalyticsReactNativePluginAdvertisingIdModule.kt @@ -18,42 +18,47 @@ class AnalyticsReactNativePluginAdvertisingIdModule(reactContext: ReactApplicati return "AnalyticsReactNativePluginAdvertisingId" } - @ReactMethod + @ReactMethod fun getAdvertisingId(promise: Promise) { - if (currentActivity?.application == null) { - promise.resolve(null) - return - } - - val reactContext = (currentActivity?.application as ReactApplication) - ?.reactNativeHost - ?.reactInstanceManager - ?.currentReactContext - - if (reactContext == null) { - promise.resolve(null) - return - } - - try { - val advertisingInfo = AdvertisingIdClient.getAdvertisingIdInfo(reactContext) - val isLimitAdTrackingEnabled = advertisingInfo.isLimitAdTrackingEnabled - - if (isLimitAdTrackingEnabled) { - promise.resolve(null) - } - - val id = advertisingInfo.id - val advertisingId = id.toString() - promise.resolve(advertisingId) - } - catch (e: GooglePlayServicesNotAvailableException) { - Log.d(name, e.toString()) - promise.resolve(null) - } - catch ( e: IOException) { - Log.d(name, e.toString()) - promise.resolve(null) - } + getAdvertisingIdInfo(promise) { advertisingInfo -> + val id = advertisingInfo.id + promise.resolve(id.toString()) + } + } + + @ReactMethod + fun getIsLimitAdTrackingEnableStatus(promise: Promise) { + getAdvertisingIdInfo(promise) { advertisingInfo -> + val isLimitAdTrackingEnabled = advertisingInfo.isLimitAdTrackingEnabled + promise.resolve(isLimitAdTrackingEnabled) + } } + + private fun getAdvertisingIdInfo(promise: Promise, callback: (AdvertisingIdClient.Info) -> Unit) { + if (currentActivity?.application == null) { + promise.resolve(null) + return + } + + val reactContext = (currentActivity?.application as ReactApplication) + ?.reactNativeHost + ?.reactInstanceManager + ?.currentReactContext + + if (reactContext == null) { + promise.resolve(null) + return + } + + try { + val advertisingInfo = AdvertisingIdClient.getAdvertisingIdInfo(reactContext) + callback(advertisingInfo) + } catch (e: GooglePlayServicesNotAvailableException) { + Log.d(name, e.toString()) + promise.resolve(null) + } catch (e: IOException) { + Log.d(name, e.toString()) + promise.resolve(null) + } + } } \ No newline at end of file diff --git a/packages/plugins/plugin-advertising-id/src/AdvertisingIdPlugin.ts b/packages/plugins/plugin-advertising-id/src/AdvertisingIdPlugin.ts index b16c28af..82bda03d 100644 --- a/packages/plugins/plugin-advertising-id/src/AdvertisingIdPlugin.ts +++ b/packages/plugins/plugin-advertising-id/src/AdvertisingIdPlugin.ts @@ -5,19 +5,21 @@ import { getNativeModule, ErrorType, SegmentError, - SegmentEvent + SegmentEvent, } from '@segment/analytics-react-native'; import { Platform, NativeModule } from 'react-native'; type AdvertisingIDNativeModule = NativeModule & { getAdvertisingId: () => Promise; + getIsLimitAdTrackingEnableStatus: () => Promise; }; export class AdvertisingIdPlugin extends Plugin { type = PluginType.enrichment; queuedEvents: SegmentEvent[] = []; advertisingId?: string = undefined; + isLimitAdTracking?: boolean = false; configure(analytics: SegmentClient): void { if (Platform.OS !== 'android') { @@ -25,49 +27,48 @@ export class AdvertisingIdPlugin extends Plugin { } this.analytics = analytics; - ( - getNativeModule( - 'AnalyticsReactNativePluginAdvertisingId' - ) as AdvertisingIDNativeModule - ) - ?.getAdvertisingId() - .then((id: string) => { + // Create an array of promises for fetching advertising ID and limit ad tracking status + const advertisingIdPromise = this.fetchAdvertisingId(); + const limitAdTrackingStatusPromise = this.fetchLimitAdTrackingStatus(); + // Wait for both promises to resolve + Promise.all([advertisingIdPromise, limitAdTrackingStatusPromise]) + .then(([id, status]) => { + //handle advertisingID if (id === null) { + // Need to check this condition void analytics.track( 'LimitAdTrackingEnabled (Google Play Services) is enabled' ); } else { - this.advertisingId = id - void this.setContext(id); + this.advertisingId = id; } + //handle isLimitAdTrackingEnableStatus + this.isLimitAdTracking = status; + + // Call setContext after both values are available + void this.setContext(this.advertisingId, status); }) - .catch((error) => { - this.analytics?.reportInternalError( - new SegmentError( - ErrorType.PluginError, - 'Error retrieving AdvertisingID', - error - ) - ); - }); + .catch((error) => this.handleError(error)); } - execute(event: SegmentEvent){ - + execute(event: SegmentEvent) { if (this.advertisingId === undefined) { this.queuedEvents.push(event); - }else{ + } else { return event; } return; } - async setContext(id: string): Promise { + async setContext( + id: string | undefined, + isLimitAdTrackingEnableStatus: boolean + ): Promise { try { await this.analytics?.context.set({ device: { advertisingId: id, - adTrackingEnabled: true, + adTrackingEnabled: !isLimitAdTrackingEnableStatus, }, }); this.sendQueued(); @@ -81,9 +82,35 @@ export class AdvertisingIdPlugin extends Plugin { } sendQueued() { - this.queuedEvents.forEach(event => { + this.queuedEvents.forEach((event) => { void this.analytics?.process(event); }); this.queuedEvents = []; } + + private fetchAdvertisingId(): Promise { + return ( + getNativeModule( + 'AnalyticsReactNativePluginAdvertisingId' + ) as AdvertisingIDNativeModule + )?.getAdvertisingId(); + } + + private fetchLimitAdTrackingStatus(): Promise { + return ( + getNativeModule( + 'AnalyticsReactNativePluginAdvertisingId' + ) as AdvertisingIDNativeModule + )?.getIsLimitAdTrackingEnableStatus(); + } + + private handleError(error: unknown): void { + this.analytics?.reportInternalError( + new SegmentError( + ErrorType.PluginError, + 'Error retrieving AdvertisingID', + error + ) + ); + } } From 2b1e1fce8962a060d102e123752f3a534e8f2932 Mon Sep 17 00:00:00 2001 From: Sunita Prajapati <> Date: Wed, 13 Nov 2024 15:12:30 +0530 Subject: [PATCH 5/6] fix: type mismatch inferred type is String? but String was expected --- .../segmentanalyticsreactnative/AnalyticsReactNativeModule.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/android/src/main/java/com/segmentanalyticsreactnative/AnalyticsReactNativeModule.kt b/packages/core/android/src/main/java/com/segmentanalyticsreactnative/AnalyticsReactNativeModule.kt index a6769bba..589ca782 100644 --- a/packages/core/android/src/main/java/com/segmentanalyticsreactnative/AnalyticsReactNativeModule.kt +++ b/packages/core/android/src/main/java/com/segmentanalyticsreactnative/AnalyticsReactNativeModule.kt @@ -125,7 +125,7 @@ class AnalyticsReactNativeModule : ReactContextBaseJavaModule, ActivityEventList @ReactMethod fun getContextInfo(config: ReadableMap, promise: Promise) { val appName: String = reactApplicationContext.applicationInfo.loadLabel(reactApplicationContext.packageManager).toString() - val appVersion: String = pInfo.versionName + val appVersion: String = pInfo.versionName.toString() val buildNumber = getBuildNumber() val bundleId = reactApplicationContext.packageName From 9ea4e9410b89e44341d0ec8b60d62a2c96b0293f Mon Sep 17 00:00:00 2001 From: Sunita Prajapati <> Date: Wed, 13 Nov 2024 15:33:25 +0530 Subject: [PATCH 6/6] Revert "fix: type mismatch inferred type is String? but String was expected" This reverts commit 2b1e1fce8962a060d102e123752f3a534e8f2932. --- .../segmentanalyticsreactnative/AnalyticsReactNativeModule.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/android/src/main/java/com/segmentanalyticsreactnative/AnalyticsReactNativeModule.kt b/packages/core/android/src/main/java/com/segmentanalyticsreactnative/AnalyticsReactNativeModule.kt index 589ca782..a6769bba 100644 --- a/packages/core/android/src/main/java/com/segmentanalyticsreactnative/AnalyticsReactNativeModule.kt +++ b/packages/core/android/src/main/java/com/segmentanalyticsreactnative/AnalyticsReactNativeModule.kt @@ -125,7 +125,7 @@ class AnalyticsReactNativeModule : ReactContextBaseJavaModule, ActivityEventList @ReactMethod fun getContextInfo(config: ReadableMap, promise: Promise) { val appName: String = reactApplicationContext.applicationInfo.loadLabel(reactApplicationContext.packageManager).toString() - val appVersion: String = pInfo.versionName.toString() + val appVersion: String = pInfo.versionName val buildNumber = getBuildNumber() val bundleId = reactApplicationContext.packageName