Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions ios/RNIterableAPI/RNIterableAPI.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#if RCT_NEW_ARCH_ENABLED
#import "RNIterableAPISpec.h"
#import <string>
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The <string> header is imported but doesn't appear to be used in the visible code. If this import is not required by the generated RNIterableAPISpec.h header, consider removing it to keep the imports clean.

Suggested change
#import <string>

Copilot uses AI. Check for mistakes.
#endif

#import <IterableSDK/IterableSDK.h>
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down
33 changes: 33 additions & 0 deletions ios/RNIterableAPI/ReactIterableAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Comment on lines +572 to +575
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The clickedUrl validation should be moved earlier in the function, before extracting the messageId and looking up the message in the cache. This would avoid unnecessary work when clickedUrl is null. Consider moving this check right after the ITBInfo() call to fail fast.

Copilot uses AI. Check for mistakes.

IterableAPI.track(
embeddedMessageClick: embeddedMessage,
buttonIdentifier: buttonId,
clickedUrl: clickedUrl
)
}

// MARK: Private
private var shouldEmit = false
private let _methodQueue = DispatchQueue(label: String(describing: ReactIterableAPI.self))
Expand Down