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
10 changes: 4 additions & 6 deletions example/src/components/Embedded/Embedded.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -108,7 +106,7 @@ export const Embedded = () => {
<TouchableOpacity style={styles.button} onPress={endEmbeddedSession}>
<Text style={styles.buttonText}>End session</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={getEmbeddedMessages}>
<TouchableOpacity style={styles.button} onPress={() => getEmbeddedMessages(placementIds)}>
<Text style={styles.buttonText}>Get messages</Text>
</TouchableOpacity>
</View>
Expand Down
18 changes: 18 additions & 0 deletions ios/RNIterableAPI/RNIterableAPI.mm
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,16 @@ - (void)endEmbeddedSession {
[_swiftAPI endEmbeddedSession];
}

- (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
}
Expand Down Expand Up @@ -523,6 +533,14 @@ - (void)wakeApp {
[_swiftAPI endEmbeddedSession];
}

RCT_EXPORT_METHOD(syncEmbeddedMessages) {
[_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
}
Expand Down
30 changes: 30 additions & 0 deletions ios/RNIterableAPI/ReactIterableAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,36 @@ import React
EmbeddedSessionManager.shared.endSession()
}

@objc(syncEmbeddedMessages)
public func syncEmbeddedMessages() {
ITBInfo()
IterableAPI.embeddedManager.syncMessages { }
}

Choose a reason for hiding this comment

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

What happens when syncMessages fails.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately, it would swallow it. I would love to add success/error callbacks, but unfortunately Android does not seem to expose them, so I would only be able to do so for iOS. As RN needs to have similar capabilities for both, I kind of have to forgo the error handling here as I can't seem to figure it out properly in Android.

@Ayyanchira -- please let me know if I misunderstood this!


@objc(getEmbeddedMessages:resolver:rejecter:)
public func getEmbeddedMessages(
placementIds: [NSNumber]?, resolver: RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock
) {
ITBInfo()
var messages: [IterableEmbeddedMessage] = []

if let placementIds = placementIds, !placementIds.isEmpty {

Choose a reason for hiding this comment

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

need a comment here to explain why if placementIds is nil or empty we just do IterableAPI.embeddedManager.getMessages(). Would the messages array be coherent in both cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It would be coherent for both. Added a comment regarding this, but not 100% sure if this is what you meant. Please let me know if it is not.

// Get messages for specific placement IDs
for placementId in placementIds {
let placementMessages = IterableAPI.embeddedManager.getMessages(
for: placementId.intValue
)
messages.append(contentsOf: placementMessages)

Choose a reason for hiding this comment

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

what if placementMessages is nil

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It would return an empty array

}
} else {
// Get all messages from all placements
// getMessages() without parameters flattens all placement messages into a single array
messages = IterableAPI.embeddedManager.getMessages()
}

resolver(messages.map { $0.toDict() })
}

// MARK: Private
private var shouldEmit = false
private let _methodQueue = DispatchQueue(label: String(describing: ReactIterableAPI.self))
Expand Down
30 changes: 30 additions & 0 deletions ios/RNIterableAPI/Serialization.swift
Original file line number Diff line number Diff line change
Expand Up @@ -283,3 +283,33 @@ extension InboxImpressionTracker.RowInfo {
return rows.compactMap(InboxImpressionTracker.RowInfo.from(dict:))
}
}

extension IterableEmbeddedMessage {
func toDict() -> [AnyHashable: Any]? {
var dict = [AnyHashable: Any]()

// 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
}

Choose a reason for hiding this comment

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

What if serialization fails but the metadata is partially encodable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

JSONEnvoder is atomic -- If any property fails to encode, the entire encoding will fail. So there wouldn't be partial encoding. But the entire metadata would be silently omitted.

Updated this so that it would fail if the encoding fails so that the error isn't swallowed.

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
}
}

Choose a reason for hiding this comment

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

What is serialization fails but the elementsDict is partially encodable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated this -- let me know if this works


// Payload doesn't need serialization - it's already a dictionary
if let payload = payload {
dict["payload"] = payload
}

return dict
}
}