diff --git a/ios/RNIterableAPI/RNIterableAPI.mm b/ios/RNIterableAPI/RNIterableAPI.mm index 0fe92f4db..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,6 +304,30 @@ - (void)pauseEmbeddedImpression:(NSString *)messageId { [_swiftAPI pauseEmbeddedImpression:messageId]; } +- (void)trackEmbeddedClick:(JS::NativeRNIterableAPI::EmbeddedMessage &)message + buttonId:(NSString *_Nullable)buttonId + clickedUrl:(NSString *_Nullable)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 { // Placeholder function -- this method is only used in Android } @@ -557,6 +582,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))