Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

Expand All @@ -23,10 +24,14 @@
import com.iterable.iterableapi.IterableAction;
import com.iterable.iterableapi.IterableActionContext;
import com.iterable.iterableapi.IterableApi;
import com.iterable.iterableapi.IterableAttributionInfo;
import com.iterable.iterableapi.IterableAuthHandler;
import com.iterable.iterableapi.IterableConfig;
import com.iterable.iterableapi.IterableCustomActionHandler;
import com.iterable.iterableapi.IterableAttributionInfo;
// import com.iterable.iterableapi.IterableEmbeddedManager;
import com.iterable.iterableapi.IterableEmbeddedMessage;
// import com.iterable.iterableapi.IterableEmbeddedSession;
// import com.iterable.iterableapi.IterableEmbeddedUpdateHandler;
Comment on lines +31 to +34
Copy link

Copilot AI Oct 14, 2025

Choose a reason for hiding this comment

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

Remove commented-out import statements. These unused imports add clutter and should be cleaned up.

Suggested change
// import com.iterable.iterableapi.IterableEmbeddedManager;
import com.iterable.iterableapi.IterableEmbeddedMessage;
// import com.iterable.iterableapi.IterableEmbeddedSession;
// import com.iterable.iterableapi.IterableEmbeddedUpdateHandler;
import com.iterable.iterableapi.IterableEmbeddedMessage;

Copilot uses AI. Check for mistakes.
import com.iterable.iterableapi.IterableHelper;
import com.iterable.iterableapi.IterableInAppCloseAction;
import com.iterable.iterableapi.IterableInAppHandler;
Expand All @@ -45,6 +50,7 @@
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

Expand Down Expand Up @@ -88,10 +94,15 @@ public void initializeWithApiKey(String apiKey, ReadableMap configReadableMap, S
configBuilder.setAuthHandler(this);
}

if (configReadableMap.hasKey("enableEmbeddedMessaging")) {
configBuilder.setEnableEmbeddedMessaging(configReadableMap.getBoolean("enableEmbeddedMessaging"));
}

IterableApi.initialize(reactContext, apiKey, configBuilder.build());
IterableApi.getInstance().setDeviceAttribute("reactNativeSDKVersion", version);

IterableApi.getInstance().getInAppManager().addListener(this);
IterableApi.getInstance().getEmbeddedManager().syncMessages();

// MOB-10421: Figure out what the error cases are and handle them appropriately
// This is just here to match the TS types and let the JS thread know when we are done initializing
Expand All @@ -118,6 +129,10 @@ public void initialize2WithApiKey(String apiKey, ReadableMap configReadableMap,
configBuilder.setAuthHandler(this);
}

if (configReadableMap.hasKey("enableEmbeddedMessaging")) {
configBuilder.setEnableEmbeddedMessaging(configReadableMap.getBoolean("enableEmbeddedMessaging"));
}
Comment on lines +133 to +134
Copy link

Copilot AI Oct 14, 2025

Choose a reason for hiding this comment

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

Inconsistent indentation. The closing brace should align with the if statement (2 spaces instead of 6).

Suggested change
configBuilder.setEnableEmbeddedMessaging(configReadableMap.getBoolean("enableEmbeddedMessaging"));
}
configBuilder.setEnableEmbeddedMessaging(configReadableMap.getBoolean("enableEmbeddedMessaging"));
}

Copilot uses AI. Check for mistakes.

// NOTE: There does not seem to be a way to set the API endpoint
// override in the Android SDK. Check with @Ayyanchira and @evantk91 to
// see what the best approach is.
Expand All @@ -126,6 +141,7 @@ public void initialize2WithApiKey(String apiKey, ReadableMap configReadableMap,
IterableApi.getInstance().setDeviceAttribute("reactNativeSDKVersion", version);

IterableApi.getInstance().getInAppManager().addListener(this);
IterableApi.getInstance().getEmbeddedManager().syncMessages();

// MOB-10421: Figure out what the error cases are and handle them appropriately
// This is just here to match the TS types and let the JS thread know when we are done initializing
Expand Down Expand Up @@ -629,14 +645,80 @@ public void sendEvent(@NonNull String eventName, @Nullable Object eventData) {
public void onInboxUpdated() {
sendEvent(EventName.receivedIterableInboxChanged.name(), null);
}
// ---------------------------------------------------------------------------------------
// endregion

// ---------------------------------------------------------------------------------------
// region Embedded messaging

public void getEmbeddedMessages(@Nullable ReadableArray placementIds, Promise promise) {
IterableLogger.d(TAG, "getEmbeddedMessages for placements: " + placementIds);

try {
List<IterableEmbeddedMessage> allMessages = new ArrayList<>();

if (placementIds == null || placementIds.size() == 0) {
// If no placement IDs provided, we need to get messages for all possible placements
// Since the Android SDK requires a placement ID, we'll use 0 as a default
// This might need to be adjusted based on the actual SDK behavior
List<IterableEmbeddedMessage> messages = IterableApi.getInstance().getEmbeddedManager().getMessages(0L);
if (messages != null) {
allMessages.addAll(messages);
}
} else {
// Convert ReadableArray to individual placement IDs and get messages for each
for (int i = 0; i < placementIds.size(); i++) {
long placementId = placementIds.getInt(i);
List<IterableEmbeddedMessage> messages = IterableApi.getInstance().getEmbeddedManager().getMessages(placementId);
if (messages != null) {
allMessages.addAll(messages);
}
}
}

JSONArray embeddedMessageJsonArray = Serialization.serializeEmbeddedMessages(allMessages);
IterableLogger.d(TAG, "Messages for placements: " + embeddedMessageJsonArray);

promise.resolve(Serialization.convertJsonToArray(embeddedMessageJsonArray));
} catch (JSONException e) {
IterableLogger.e(TAG, e.getLocalizedMessage());
promise.reject("", "Failed to fetch messages with error " + e.getLocalizedMessage());
}
}

public void syncEmbeddedMessages() {
IterableLogger.d(TAG, "syncEmbeddedMessages");
IterableApi.getInstance().getEmbeddedManager().syncMessages();
}

public void getEmbeddedPlacementIds(Promise promise) {
IterableLogger.d(TAG, "getEmbeddedPlacementIds");
try {
List<Long> placementIds = IterableApi.getInstance().getEmbeddedManager().getPlacementIds();
WritableArray writableArray = Arguments.createArray();
if (placementIds != null) {
for (Long placementId : placementIds) {
writableArray.pushDouble(placementId.doubleValue());
Copy link

Copilot AI Oct 14, 2025

Choose a reason for hiding this comment

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

Converting Long placement IDs to double may cause precision loss for large values. Consider using pushString() or ensure the native interface expects numbers instead of longs.

Suggested change
writableArray.pushDouble(placementId.doubleValue());
writableArray.pushString(placementId.toString());

Copilot uses AI. Check for mistakes.
}
}
promise.resolve(writableArray);
} catch (Exception e) {
IterableLogger.e(TAG, "Error getting placement IDs: " + e.getLocalizedMessage());
promise.reject("", "Failed to get placement IDs: " + e.getLocalizedMessage());
}
}

// ---------------------------------------------------------------------------------------
// endregion
}

enum EventName {
handleUrlCalled,
handleCustomActionCalled,
handleInAppCalled,
handleAuthCalled,
receivedIterableInboxChanged,
handleAuthFailureCalled,
handleAuthSuccessCalled,
handleAuthFailureCalled
handleCustomActionCalled,
handleInAppCalled,
handleUrlCalled,
receivedIterableEmbeddedMessagesChanged,
receivedIterableInboxChanged
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,16 @@
import com.iterable.iterableapi.IterableActionContext;
import com.iterable.iterableapi.IterableConfig;
import com.iterable.iterableapi.IterableDataRegion;
import com.iterable.iterableapi.IterableEmbeddedMessage;
import com.iterable.iterableapi.IterableInAppCloseAction;
import com.iterable.iterableapi.IterableInAppDeleteActionType;
import com.iterable.iterableapi.IterableInAppHandler;
import com.iterable.iterableapi.IterableInAppLocation;
import com.iterable.iterableapi.IterableInAppMessage;
import com.iterable.iterableapi.IterableInboxSession;
import com.iterable.iterableapi.IterableLogger;
import com.iterable.iterableapi.RNIterableInternal;
import com.iterable.iterableapi.RetryPolicy;
import com.iterable.iterableapi.RNIterableInternal;

import org.json.JSONArray;
import org.json.JSONException;
Expand Down Expand Up @@ -137,6 +138,17 @@ static JSONArray serializeInAppMessages(List<IterableInAppMessage> inAppMessages
return inAppMessagesJson;
}

static JSONArray serializeEmbeddedMessages(List<IterableEmbeddedMessage> embeddedMessages) {
JSONArray embeddedMessagesJson = new JSONArray();
if (embeddedMessages != null) {
for (IterableEmbeddedMessage message : embeddedMessages) {
JSONObject messageJson = IterableEmbeddedMessage.Companion.toJSONObject(message);
embeddedMessagesJson.put(messageJson);
}
}
return embeddedMessagesJson;
}

static IterableConfig.Builder getConfigFromReadableMap(ReadableMap iterableContextMap) {
try {
JSONObject iterableContextJSON = convertMapToJson(iterableContextMap);
Expand Down Expand Up @@ -218,6 +230,10 @@ static IterableConfig.Builder getConfigFromReadableMap(ReadableMap iterableConte
configBuilder.setDataRegion(iterableDataRegion);
}

if (iterableContextJSON.has("enableEmbeddedMessaging")) {
configBuilder.setEnableEmbeddedMessaging(iterableContextJSON.optBoolean("enableEmbeddedMessaging"));
}

if (iterableContextJSON.has("retryPolicy")) {
JSONObject retryPolicyJson = iterableContextJSON.getJSONObject("retryPolicy");
int maxRetry = retryPolicyJson.getInt("maxRetry");
Expand Down
5 changes: 5 additions & 0 deletions android/src/newarch/java/com/RNIterableAPIModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,11 @@ public void pauseAuthRetries(boolean pauseRetry) {
moduleImpl.pauseAuthRetries(pauseRetry);
}

@Override
public void getEmbeddedPlacementIds(Promise promise) {
moduleImpl.getEmbeddedPlacementIds(promise);
}

public void sendEvent(@NonNull String eventName, @Nullable Object eventData) {
moduleImpl.sendEvent(eventName, eventData);
}
Expand Down
4 changes: 4 additions & 0 deletions android/src/oldarch/java/com/RNIterableAPIModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,10 @@ public void pauseAuthRetries(boolean pauseRetry) {
moduleImpl.pauseAuthRetries(pauseRetry);
}

@ReactMethod
public void getEmbeddedPlacementIds(Promise promise) {
moduleImpl.getEmbeddedPlacementIds(promise);
}

public void sendEvent(@NonNull String eventName, @Nullable Object eventData) {
moduleImpl.sendEvent(eventName, eventData);
Expand Down
20 changes: 19 additions & 1 deletion example/src/components/Embedded/Embedded.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
import { Text, View } from 'react-native';
import { Text, TouchableOpacity, View } from 'react-native';
import { useCallback, useState } from 'react';
import { Iterable } from '@iterable/react-native-sdk';

import styles from './Embedded.styles';

export const Embedded = () => {
const [placementIds, setPlacementIds] = useState<number[]>([]);
const getPlacementIds = useCallback(() => {
Iterable.embeddedManager.getPlacementIds().then((ids: unknown) => {
console.log(ids);
setPlacementIds(ids as number[]);
});
// .catch((error) => {
// console.error(error);
// });
}, []);

return (
<View style={styles.container}>
<Text style={styles.text}>EMBEDDED</Text>
Expand All @@ -14,6 +26,12 @@ export const Embedded = () => {
Is embedded manager enabled?
{Iterable.embeddedManager.isEnabled ? 'Yes' : 'No'}
</Text>
<Text style={styles.text}>
Placement ids: [{placementIds.join(', ')}]
</Text>
<TouchableOpacity style={styles.button} onPress={getPlacementIds}>
<Text style={styles.buttonText}>Get placement ids</Text>
</TouchableOpacity>
</View>
);
};
Expand Down
2 changes: 2 additions & 0 deletions example/src/hooks/useIterableApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ export const IterableAppProvider: FunctionComponent<
retryBackoff: IterableRetryBackoff.LINEAR,
};

config.enableEmbeddedMessaging = true;

config.onJWTError = (authFailure) => {
console.log('onJWTError', authFailure);

Expand Down
3 changes: 3 additions & 0 deletions src/api/NativeRNIterableAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ export interface Spec extends TurboModule {
passAlongAuthToken(authToken?: string | null): void;
pauseAuthRetries(pauseRetry: boolean): void;

// Embedded Messaging
getEmbeddedPlacementIds(): Promise<number[]>;

// Wake app -- android only
wakeApp(): void;

Expand Down
16 changes: 15 additions & 1 deletion src/core/classes/IterableApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,21 @@ export class IterableApi {
// ---- End IN-APP ---- //

// ====================================================== //
// ======================= MOSC ======================= //
// ======================= EMBEDDED ===================== //
// ====================================================== //

/**
* Get the embedded placement IDs.
*/
static getEmbeddedPlacementIds() {
IterableLogger.log('getEmbeddedPlacementIds');
return RNIterableAPI.getEmbeddedPlacementIds();
}

// ---- End EMBEDDED ---- //

// ====================================================== //
// ======================= MISCELLANEOUS ================ //
// ====================================================== //

/**
Expand Down
4 changes: 2 additions & 2 deletions src/core/classes/IterableConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ export class IterableConfig {
* - [Android Embedded Messaging](https://support.iterable.com/hc/en-us/articles/23061877893652-Embedded-Messages-with-Iterable-s-Android-SDK)
* - [iOS Embedded Messaging](https://support.iterable.com/hc/en-us/articles/23061840746900-Embedded-Messages-with-Iterable-s-iOS-SDK)
*/
embeddedMessagingEnabled = false;
enableEmbeddedMessaging = false;

/**
* Converts the IterableConfig instance to a dictionary object.
Expand Down Expand Up @@ -378,7 +378,7 @@ export class IterableConfig {
pushPlatform: this.pushPlatform,
encryptionEnforced: this.encryptionEnforced,
retryPolicy: this.retryPolicy,
embeddedMessagingEnabled: this.embeddedMessagingEnabled,
enableEmbeddedMessaging: this.enableEmbeddedMessaging,
};
}
}
11 changes: 11 additions & 0 deletions src/embedded/classes/IterableEmbeddedManager.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { IterableApi } from '../../core/classes/IterableApi';

/**
* Manages embedded messages from Iterable.
*
Expand All @@ -14,4 +16,13 @@ export class IterableEmbeddedManager {
* Whether the embedded manager is enabled.
*/
isEnabled = false;

/**
* Retrieves a list of placement IDs for the embedded manager.
*
* [Placement Documentation](https://support.iterable.com/hc/en-us/articles/23060529977364-Embedded-Messaging-Overview#placements-and-prioritization)
*/
getPlacementIds() {
return IterableApi.getEmbeddedPlacementIds();
}
}