From 6d1eb352474d54dc9c671e354f048966e975667b Mon Sep 17 00:00:00 2001 From: Loren Posen Date: Sun, 11 Jan 2026 14:56:58 -0800 Subject: [PATCH 1/2] feat: add trackEmbeddedClick method to ReactIterableAPI --- ios/RNIterableAPI/RNIterableAPI.mm | 10 +++++++ ios/RNIterableAPI/ReactIterableAPI.swift | 33 ++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/ios/RNIterableAPI/RNIterableAPI.mm b/ios/RNIterableAPI/RNIterableAPI.mm index 0fe92f4db..be8cd6eb8 100644 --- a/ios/RNIterableAPI/RNIterableAPI.mm +++ b/ios/RNIterableAPI/RNIterableAPI.mm @@ -303,6 +303,12 @@ - (void)pauseEmbeddedImpression:(NSString *)messageId { [_swiftAPI pauseEmbeddedImpression:messageId]; } +- (void)trackEmbeddedClick:(NSDictionary *)message + buttonId:(NSString *_Nullable)buttonId + clickedUrl:(NSString *_Nullable)clickedUrl { + [_swiftAPI trackEmbeddedClick:message buttonId:buttonId clickedUrl:clickedUrl]; +} + - (void)wakeApp { // Placeholder function -- this method is only used in Android } @@ -557,6 +563,10 @@ - (void)wakeApp { [_swiftAPI pauseEmbeddedImpression:messageId]; } +RCT_EXPORT_METHOD(trackEmbeddedClick : (NSDictionary *)message buttonId : (NSString *_Nullable)buttonId clickedUrl : (NSString *_Nullable)clickedUrl) { + [_swiftAPI trackEmbeddedClick:message buttonId:buttonId clickedUrl:clickedUrl]; +} + 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 d929672ab..798f37850 100644 --- a/ios/RNIterableAPI/ReactIterableAPI.swift +++ b/ios/RNIterableAPI/ReactIterableAPI.swift @@ -548,6 +548,39 @@ import React EmbeddedSessionManager.shared.pauseImpression(messageId: messageId) } + @objc(trackEmbeddedClick:buttonId:clickedUrl:) + public func trackEmbeddedClick( + message: NSDictionary, buttonId: String?, clickedUrl: String? + ) { + ITBInfo() + + // Extract message ID from the dictionary + guard let messageDict = message as? [AnyHashable: Any], + let metadataDict = messageDict["metadata"] as? [AnyHashable: Any], + let messageId = metadataDict["messageId"] as? String else { + ITBError("Could not extract messageId from message dictionary") + return + } + + // Find the message in the embedded manager's cache + let messages = IterableAPI.embeddedManager.getMessages() + guard let embeddedMessage = messages.first(where: { $0.metadata.messageId == messageId }) else { + ITBError("Could not find embedded message with id: \(messageId)") + return + } + + guard let clickedUrl = clickedUrl else { + ITBError("clickedUrl is required for trackEmbeddedClick") + return + } + + IterableAPI.track( + embeddedMessageClick: embeddedMessage, + buttonIdentifier: buttonId, + clickedUrl: clickedUrl + ) + } + // MARK: Private private var shouldEmit = false private let _methodQueue = DispatchQueue(label: String(describing: ReactIterableAPI.self)) From 6dfc0814f201a09951d8e8f44d7de6de03523606 Mon Sep 17 00:00:00 2001 From: Loren Posen Date: Sun, 11 Jan 2026 15:17:21 -0800 Subject: [PATCH 2/2] feat: use C++ type and convert to NSDictionary for Swift integration --- ios/RNIterableAPI/RNIterableAPI.mm | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/ios/RNIterableAPI/RNIterableAPI.mm b/ios/RNIterableAPI/RNIterableAPI.mm index be8cd6eb8..131719043 100644 --- a/ios/RNIterableAPI/RNIterableAPI.mm +++ b/ios/RNIterableAPI/RNIterableAPI.mm @@ -2,6 +2,7 @@ #if RCT_NEW_ARCH_ENABLED #import "RNIterableAPISpec.h" + #import #endif #import @@ -303,10 +304,28 @@ - (void)pauseEmbeddedImpression:(NSString *)messageId { [_swiftAPI pauseEmbeddedImpression:messageId]; } -- (void)trackEmbeddedClick:(NSDictionary *)message +- (void)trackEmbeddedClick:(JS::NativeRNIterableAPI::EmbeddedMessage &)message buttonId:(NSString *_Nullable)buttonId clickedUrl:(NSString *_Nullable)clickedUrl { - [_swiftAPI trackEmbeddedClick:message buttonId:buttonId clickedUrl:clickedUrl]; + // The TurboModule bridge requires us to use the C++ type in the signature, + // but we need to convert it to NSDictionary to pass to Swift. + // The C++ struct wraps an NSDictionary, and the generated methods already + // return NSString*/NSNumber* types, so we just need to reconstruct the dict. + NSMutableDictionary *messageDict = [NSMutableDictionary new]; + + // Convert metadata (the accessor methods already return proper ObjC types) + NSMutableDictionary *metadataDict = [NSMutableDictionary new]; + metadataDict[@"messageId"] = message.metadata().messageId(); + metadataDict[@"placementId"] = @(message.metadata().placementId()); + if (message.metadata().campaignId().has_value()) { + metadataDict[@"campaignId"] = @(*message.metadata().campaignId()); + } + if (message.metadata().isProof().has_value()) { + metadataDict[@"isProof"] = @(*message.metadata().isProof()); + } + messageDict[@"metadata"] = metadataDict; + + [_swiftAPI trackEmbeddedClick:messageDict buttonId:buttonId clickedUrl:clickedUrl]; } - (void)wakeApp {