diff --git a/CHANGELOG.md b/CHANGELOG.md index c38a090b0..17066b18b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,9 @@ ### Updates +- Added `IterableConfig.enableUnknownUserActivation` (default `false`) to opt in to Unknown User Activation on the native SDK (SDK-794). Visitor consent via `Iterable.setVisitorUsageTracked` is also required before the native SDK stores unknown-user events. - Added `IterableConfig.enableForegroundCriteriaFetch` (default `true`) to control whether the SDK fetches unknown-user targeting criteria when the app enters the foreground (SDK-554). -- Added `IterableConfig.eventThresholdLimit` (default `100`) to cap locally stored unknown-user events before identification (SDK-553). The value is forwarded to the native SDK; it has no runtime effect in React Native until `enableUnknownUserActivation` is exposed in a future release (that switch is not on the RN config surface yet). +- Added `IterableConfig.eventThresholdLimit` (default `100`) to cap locally stored unknown-user events before identification (SDK-553). The value is forwarded to the native SDK and applies when unknown-user activation is enabled. ## 3.2.1 diff --git a/android/src/main/java/com/iterable/reactnative/Serialization.java b/android/src/main/java/com/iterable/reactnative/Serialization.java index a1a1d0d50..08ac6c3f0 100644 --- a/android/src/main/java/com/iterable/reactnative/Serialization.java +++ b/android/src/main/java/com/iterable/reactnative/Serialization.java @@ -259,6 +259,11 @@ static IterableConfig.Builder getConfigFromReadableMap(ReadableMap iterableConte configBuilder.setEnableEmbeddedMessaging(iterableContextJSON.optBoolean("enableEmbeddedMessaging")); } + if (iterableContextJSON.has("enableUnknownUserActivation")) { + configBuilder.setEnableUnknownUserActivation( + iterableContextJSON.optBoolean("enableUnknownUserActivation")); + } + if (iterableContextJSON.has("retryPolicy")) { JSONObject retryPolicyJson = iterableContextJSON.getJSONObject("retryPolicy"); int maxRetry = retryPolicyJson.getInt("maxRetry"); diff --git a/ios/RNIterableAPI/Serialization.swift b/ios/RNIterableAPI/Serialization.swift index b7e5194e3..7dcb0b776 100644 --- a/ios/RNIterableAPI/Serialization.swift +++ b/ios/RNIterableAPI/Serialization.swift @@ -95,6 +95,10 @@ extension IterableConfig { config.enableEmbeddedMessaging = enableEmbeddedMessaging } + if let enableUnknownUserActivation = dict["enableUnknownUserActivation"] as? Bool { + config.enableUnknownUserActivation = enableUnknownUserActivation + } + if let dataRegion = dict["dataRegion"] as? NSNumber { switch dataRegion { case 0: diff --git a/src/core/classes/Iterable.test.ts b/src/core/classes/Iterable.test.ts index 9bd4ef811..f68b9db74 100644 --- a/src/core/classes/Iterable.test.ts +++ b/src/core/classes/Iterable.test.ts @@ -327,6 +327,7 @@ describe('Iterable', () => { expect(config.dataRegion).toBe(IterableDataRegion.US); expect(config.enableForegroundCriteriaFetch).toBe(true); expect(config.enableEmbeddedMessaging).toBe(false); + expect(config.enableUnknownUserActivation).toBe(false); expect(config.encryptionEnforced).toBe(false); expect(config.expiringAuthTokenRefreshPeriod).toBe(60.0); expect(config.eventThresholdLimit).toBe(100); @@ -348,6 +349,7 @@ describe('Iterable', () => { expect(configDict.dataRegion).toBe(IterableDataRegion.US); expect(configDict.enableForegroundCriteriaFetch).toBe(true); expect(configDict.enableEmbeddedMessaging).toBe(false); + expect(configDict.enableUnknownUserActivation).toBe(false); expect(configDict.encryptionEnforced).toBe(false); expect(configDict.expiringAuthTokenRefreshPeriod).toBe(60.0); expect(configDict.eventThresholdLimit).toBe(100); @@ -370,6 +372,13 @@ describe('Iterable', () => { expect(configDict.authCallbackTimeoutMs).toBe(2500); }); + it('should include enableUnknownUserActivation in toDict() when set to true', () => { + const config = new IterableConfig(); + config.enableUnknownUserActivation = true; + const configDict = config.toDict(); + expect(configDict.enableUnknownUserActivation).toBe(true); + }); + it('should include enableForegroundCriteriaFetch in toDict() when set to false', () => { const config = new IterableConfig(); config.enableForegroundCriteriaFetch = false; diff --git a/src/core/classes/IterableConfig.ts b/src/core/classes/IterableConfig.ts index a284e87af..6d88cba6e 100644 --- a/src/core/classes/IterableConfig.ts +++ b/src/core/classes/IterableConfig.ts @@ -267,10 +267,9 @@ export class IterableConfig { * identification. The native SDK drops the oldest events when the count * exceeds this limit. * - * The value is sent to the native SDK at init. React Native does not expose - * `enableUnknownUserActivation` yet, so this limit has no observable effect - * until that option is added; when it is, the limit applies only while - * unknown-user activation is enabled. + * The value is sent to the native SDK at init. The limit applies only while + * unknown-user activation is enabled (`enableUnknownUserActivation`) and + * visitor consent has been granted. */ eventThresholdLimit = 100; @@ -403,6 +402,15 @@ export class IterableConfig { */ authCallbackTimeoutMs = 6000; + /** + * When `true`, the native SDK may store and process events for unidentified + * (unknown) users. Defaults to `false`. + * + * Visitor consent via `Iterable.setVisitorUsageTracked` is also required + * before the native SDK stores unknown-user events or runs unknown-user flows. + */ + enableUnknownUserActivation = false; + /** * When `true` (the default), the SDK fetches unknown-user (anonymous) targeting * criteria when the app enters the foreground. @@ -505,6 +513,7 @@ export class IterableConfig { pushPlatform: this.pushPlatform, encryptionEnforced: this.encryptionEnforced, retryPolicy: this.retryPolicy, + enableUnknownUserActivation: this.enableUnknownUserActivation, enableForegroundCriteriaFetch: this.enableForegroundCriteriaFetch, enableEmbeddedMessaging: this.enableEmbeddedMessaging, androidWakeDelayMs: this.androidWakeDelayMs,