From 217b712a47c4399519278fe566d7812d3be3fc38 Mon Sep 17 00:00:00 2001 From: Loren Posen Date: Sat, 10 Jan 2026 21:56:13 -0800 Subject: [PATCH 1/5] feat: add syncEmbeddedMessages method to ReactIterableAPI for message synchronization --- ios/RNIterableAPI/RNIterableAPI.mm | 8 ++++++++ ios/RNIterableAPI/ReactIterableAPI.swift | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/ios/RNIterableAPI/RNIterableAPI.mm b/ios/RNIterableAPI/RNIterableAPI.mm index 503e52fe2..74064a736 100644 --- a/ios/RNIterableAPI/RNIterableAPI.mm +++ b/ios/RNIterableAPI/RNIterableAPI.mm @@ -285,6 +285,10 @@ - (void)endEmbeddedSession { [_swiftAPI endEmbeddedSession]; } +- (void)syncEmbeddedMessages { + [_swiftAPI syncEmbeddedMessages]; +} + - (void)wakeApp { // Placeholder function -- this method is only used in Android } @@ -523,6 +527,10 @@ - (void)wakeApp { [_swiftAPI endEmbeddedSession]; } +RCT_EXPORT_METHOD(syncEmbeddedMessages) { + [_swiftAPI syncEmbeddedMessages]; +} + RCT_EXPORT_METHOD(wakeApp) { // Placeholder function -- this method is only used in Android } diff --git a/ios/RNIterableAPI/ReactIterableAPI.swift b/ios/RNIterableAPI/ReactIterableAPI.swift index fb4af214d..512d9359e 100644 --- a/ios/RNIterableAPI/ReactIterableAPI.swift +++ b/ios/RNIterableAPI/ReactIterableAPI.swift @@ -507,6 +507,12 @@ import React EmbeddedSessionManager.shared.endSession() } + @objc(syncEmbeddedMessages) + public func syncEmbeddedMessages() { + ITBInfo() + IterableAPI.embeddedManager.syncMessages { } + } + // MARK: Private private var shouldEmit = false private let _methodQueue = DispatchQueue(label: String(describing: ReactIterableAPI.self)) From a5cf6834559f1f15cea769f566ffa88cd458c892 Mon Sep 17 00:00:00 2001 From: Loren Posen Date: Sat, 10 Jan 2026 22:01:46 -0800 Subject: [PATCH 2/5] feat: add getEmbeddedMessages method to ReactIterableAPI for retrieving embedded messages --- ios/RNIterableAPI/RNIterableAPI.mm | 10 ++++++++++ ios/RNIterableAPI/ReactIterableAPI.swift | 23 +++++++++++++++++++++++ ios/RNIterableAPI/Serialization.swift | 24 ++++++++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/ios/RNIterableAPI/RNIterableAPI.mm b/ios/RNIterableAPI/RNIterableAPI.mm index 74064a736..4f05929de 100644 --- a/ios/RNIterableAPI/RNIterableAPI.mm +++ b/ios/RNIterableAPI/RNIterableAPI.mm @@ -289,6 +289,12 @@ - (void)syncEmbeddedMessages { [_swiftAPI syncEmbeddedMessages]; } +- (void)getEmbeddedMessages:(NSArray *_Nullable)placementIds + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject { + [_swiftAPI getEmbeddedMessages:placementIds resolver:resolve rejecter:reject]; +} + - (void)wakeApp { // Placeholder function -- this method is only used in Android } @@ -531,6 +537,10 @@ - (void)wakeApp { [_swiftAPI syncEmbeddedMessages]; } +RCT_EXPORT_METHOD(getEmbeddedMessages : (NSArray *_Nullable)placementIds resolve : (RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject) { + [_swiftAPI getEmbeddedMessages:placementIds resolver:resolve rejecter:reject]; +} + RCT_EXPORT_METHOD(wakeApp) { // Placeholder function -- this method is only used in Android } diff --git a/ios/RNIterableAPI/ReactIterableAPI.swift b/ios/RNIterableAPI/ReactIterableAPI.swift index 512d9359e..1529bace4 100644 --- a/ios/RNIterableAPI/ReactIterableAPI.swift +++ b/ios/RNIterableAPI/ReactIterableAPI.swift @@ -513,6 +513,29 @@ import React IterableAPI.embeddedManager.syncMessages { } } + @objc(getEmbeddedMessages:resolver:rejecter:) + public func getEmbeddedMessages( + placementIds: [NSNumber]?, resolver: RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock + ) { + ITBInfo() + var messages: [IterableEmbeddedMessage] = [] + + if let placementIds = placementIds, !placementIds.isEmpty { + // Get messages for specific placement IDs + for placementId in placementIds { + let placementMessages = IterableAPI.embeddedManager.getMessages( + for: placementId.intValue + ) + messages.append(contentsOf: placementMessages) + } + } else { + // Get all messages + messages = IterableAPI.embeddedManager.getMessages() + } + + resolver(messages.map { $0.toDict() }) + } + // MARK: Private private var shouldEmit = false private let _methodQueue = DispatchQueue(label: String(describing: ReactIterableAPI.self)) diff --git a/ios/RNIterableAPI/Serialization.swift b/ios/RNIterableAPI/Serialization.swift index 503427d5d..88bca45cb 100644 --- a/ios/RNIterableAPI/Serialization.swift +++ b/ios/RNIterableAPI/Serialization.swift @@ -283,3 +283,27 @@ extension InboxImpressionTracker.RowInfo { return rows.compactMap(InboxImpressionTracker.RowInfo.from(dict:)) } } + +extension IterableEmbeddedMessage { + func toDict() -> [AnyHashable: Any] { + var dict = [AnyHashable: Any]() + + // Serialize metadata (which is Codable) + if let metadataDict = SerializationUtil.encodableToDictionary(encodable: metadata) { + dict["metadata"] = metadataDict + } + + // Serialize elements if present (which is Codable) + if let elements = elements, + let elementsDict = SerializationUtil.encodableToDictionary(encodable: elements) { + dict["elements"] = elementsDict + } + + // Add payload directly + if let payload = payload { + dict["payload"] = payload + } + + return dict + } +} From a9d8198aea2fa7f844bcefb95c55717270697a62 Mon Sep 17 00:00:00 2001 From: Loren Posen Date: Sat, 10 Jan 2026 22:12:46 -0800 Subject: [PATCH 3/5] refactor: update getEmbeddedMessages to accept optional placement IDs and improve message retrieval --- example/src/components/Embedded/Embedded.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/example/src/components/Embedded/Embedded.tsx b/example/src/components/Embedded/Embedded.tsx index 785d722d6..9c67cb913 100644 --- a/example/src/components/Embedded/Embedded.tsx +++ b/example/src/components/Embedded/Embedded.tsx @@ -41,14 +41,12 @@ export const Embedded = () => { Iterable.embeddedManager.endSession(); }, []); - const getEmbeddedMessages = useCallback(() => { - getPlacementIds() - .then((ids: number[]) => Iterable.embeddedManager.getMessages(ids)) - .then((messages: IterableEmbeddedMessage[]) => { + const getEmbeddedMessages = useCallback((ids: number[] | null = null) => { + Iterable.embeddedManager.getMessages(ids).then((messages: IterableEmbeddedMessage[]) => { setEmbeddedMessages(messages); console.log(messages); }); - }, [getPlacementIds]); + }, []); const startEmbeddedImpression = useCallback( (message: IterableEmbeddedMessage) => { @@ -108,7 +106,7 @@ export const Embedded = () => { End session - + getEmbeddedMessages(placementIds)}> Get messages From c15a0573c4b6c35c9f650ff0c170223ab3f48fc8 Mon Sep 17 00:00:00 2001 From: Loren Posen Date: Mon, 12 Jan 2026 18:20:18 -0800 Subject: [PATCH 4/5] docs: clarify comments in ReactIterableAPI.swift regarding message retrieval from all placements --- ios/RNIterableAPI/ReactIterableAPI.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ios/RNIterableAPI/ReactIterableAPI.swift b/ios/RNIterableAPI/ReactIterableAPI.swift index 1529bace4..15ed7f14b 100644 --- a/ios/RNIterableAPI/ReactIterableAPI.swift +++ b/ios/RNIterableAPI/ReactIterableAPI.swift @@ -529,7 +529,8 @@ import React messages.append(contentsOf: placementMessages) } } else { - // Get all messages + // Get all messages from all placements + // getMessages() without parameters flattens all placement messages into a single array messages = IterableAPI.embeddedManager.getMessages() } From 41e080adedc33a1c850102d9e90611267b58c9e5 Mon Sep 17 00:00:00 2001 From: Loren Posen Date: Mon, 12 Jan 2026 18:33:07 -0800 Subject: [PATCH 5/5] fix: fail if encoding fails instead of silently omitting field --- ios/RNIterableAPI/Serialization.swift | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/ios/RNIterableAPI/Serialization.swift b/ios/RNIterableAPI/Serialization.swift index 88bca45cb..fee4c49b6 100644 --- a/ios/RNIterableAPI/Serialization.swift +++ b/ios/RNIterableAPI/Serialization.swift @@ -285,21 +285,27 @@ extension InboxImpressionTracker.RowInfo { } extension IterableEmbeddedMessage { - func toDict() -> [AnyHashable: Any] { + func toDict() -> [AnyHashable: Any]? { var dict = [AnyHashable: Any]() - // Serialize metadata (which is Codable) - if let metadataDict = SerializationUtil.encodableToDictionary(encodable: metadata) { - dict["metadata"] = metadataDict + // CRITICAL: Metadata is required - fail if missing + guard let metadataDict = SerializationUtil.encodableToDictionary(encodable: metadata) else { + ITBError("Failed to serialize embedded message metadata. Dropping invalid message.") + return nil } - - // Serialize elements if present (which is Codable) - if let elements = elements, - let elementsDict = SerializationUtil.encodableToDictionary(encodable: elements) { - dict["elements"] = elementsDict + dict["metadata"] = metadataDict + + // IMPORTANT: Elements are optional, but if present and fail to serialize, that's bad + if let elements = elements { + if let elementsDict = SerializationUtil.encodableToDictionary(encodable: elements) { + dict["elements"] = elementsDict + } else { + ITBError("Failed to serialize embedded message elements. Message will not be displayable.") + return nil + } } - // Add payload directly + // Payload doesn't need serialization - it's already a dictionary if let payload = payload { dict["payload"] = payload }