Skip to content

Commit d72712a

Browse files
sumchatteringclaude
andcommitted
fix: surface push registration errors instead of failing silently (#469)
When pushIntegrationName is set to an invalid value, the registerDeviceToken API call fails but the error was silently swallowed in the native layer. This change implements the onTokenRegistrationFailed callback on both iOS and Android native modules to emit an event to the React Native layer. A console.error is always logged, and developers can optionally provide an onTokenRegistrationFailed callback in IterableConfig for programmatic error handling. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e69c305 commit d72712a

5 files changed

Lines changed: 69 additions & 1 deletion

File tree

android/src/main/java/com/iterable/reactnative/RNIterableAPIModuleImpl.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,19 @@ public void onTokenRegistrationSuccessful(String authToken) {
681681
sendEvent(EventName.handleAuthSuccessCalled.name(), null);
682682
}
683683

684+
@Override
685+
public void onTokenRegistrationFailed(String reason) {
686+
IterableLogger.e(TAG, "Token registration failed: " + reason);
687+
JSONObject reasonJson = new JSONObject();
688+
try {
689+
reasonJson.put("reason", reason != null ? reason : "unknown");
690+
WritableMap eventData = Serialization.convertJsonToMap(reasonJson);
691+
sendEvent(EventName.handleTokenRegistrationFailedCalled.name(), eventData);
692+
} catch (JSONException e) {
693+
IterableLogger.e(TAG, "Failed to send token registration failure event");
694+
}
695+
}
696+
684697
public void addListener(String eventName) {
685698
// Keep: Required for RN built in Event Emitter Calls.
686699
}
@@ -811,5 +824,6 @@ enum EventName {
811824
handleInAppCalled,
812825
handleUrlCalled,
813826
receivedIterableEmbeddedMessagesChanged,
814-
receivedIterableInboxChanged
827+
receivedIterableInboxChanged,
828+
handleTokenRegistrationFailedCalled
815829
}

ios/RNIterableAPI/ReactIterableAPI.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import React
3535
case handleAuthFailureCalled
3636
case handleEmbeddedMessageUpdateCalled
3737
case handleEmbeddedMessagingDisabledCalled
38+
case handleTokenRegistrationFailedCalled
3839
}
3940

4041
@objc public static var supportedEvents: [String] {
@@ -818,6 +819,13 @@ extension ReactIterableAPI: IterableAuthDelegate {
818819
}
819820

820821
public func onTokenRegistrationFailed(_ reason: String?) {
822+
ITBError("Token registration failed: \(reason ?? "unknown reason")")
823+
guard shouldEmit else {
824+
return
825+
}
826+
delegate?.sendEvent(
827+
withName: EventName.handleTokenRegistrationFailedCalled.rawValue,
828+
body: ["reason": reason ?? "unknown"] as [String: Any])
821829
}
822830
}
823831

src/core/classes/Iterable.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,9 @@ export class Iterable {
956956
RNEventEmitter.removeAllListeners(
957957
IterableEventName.handleEmbeddedMessagingDisabledCalled
958958
);
959+
RNEventEmitter.removeAllListeners(
960+
IterableEventName.handleTokenRegistrationFailedCalled
961+
);
959962
}
960963

961964
/**
@@ -1109,6 +1112,25 @@ export class Iterable {
11091112
);
11101113
}
11111114
}
1115+
1116+
// Always listen for token registration failures so they are surfaced
1117+
// to developers. Without this, push registration errors (e.g. invalid
1118+
// pushIntegrationName) are silently swallowed.
1119+
RNEventEmitter.addListener(
1120+
IterableEventName.handleTokenRegistrationFailedCalled,
1121+
(dict: { reason: string }) => {
1122+
const reason = dict?.reason ?? 'unknown';
1123+
1124+
// Always log to console.error so developers can see the failure
1125+
// even without the callback configured
1126+
console.error(
1127+
`[Iterable] Push token registration failed: ${reason}. ` +
1128+
'Check that pushIntegrationName in your IterableConfig is valid.'
1129+
);
1130+
1131+
Iterable.savedConfig.onTokenRegistrationFailed?.(reason);
1132+
}
1133+
);
11121134
}
11131135

11141136
/**

src/core/classes/IterableConfig.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,28 @@ export class IterableConfig {
375375
*/
376376
onEmbeddedMessagingDisabled?: () => void;
377377

378+
/**
379+
* A callback function that is called when push token registration fails.
380+
*
381+
* This can happen when `pushIntegrationName` is set to an invalid value,
382+
* or when the `/api/users/registerDeviceToken` endpoint returns an error.
383+
*
384+
* Without this callback, push registration errors are silently ignored,
385+
* making it difficult to diagnose push notification setup issues.
386+
*
387+
* @param reason - A string describing why token registration failed.
388+
*
389+
* @example
390+
* ```typescript
391+
* const config = new IterableConfig();
392+
* config.onTokenRegistrationFailed = (reason) => {
393+
* console.error('Push token registration failed:', reason);
394+
* };
395+
* Iterable.initialize('<YOUR_API_KEY>', config);
396+
* ```
397+
*/
398+
onTokenRegistrationFailed?: (reason: string) => void;
399+
378400
/**
379401
* Converts the IterableConfig instance to a dictionary object.
380402
*

src/core/enums/IterableEventName.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,6 @@ export enum IterableEventName {
2323
handleEmbeddedMessageUpdateCalled = 'handleEmbeddedMessageUpdateCalled',
2424
/** Event that fires when embedded messaging is disabled */
2525
handleEmbeddedMessagingDisabledCalled = 'handleEmbeddedMessagingDisabledCalled',
26+
/** Event that fires when push token registration fails (e.g., invalid pushIntegrationName) */
27+
handleTokenRegistrationFailedCalled = 'handleTokenRegistrationFailedCalled',
2628
}

0 commit comments

Comments
 (0)