diff --git a/android/src/main/java/com/iterable/reactnative/RNIterableAPIModuleImpl.java b/android/src/main/java/com/iterable/reactnative/RNIterableAPIModuleImpl.java index caef783e6..94f5a0ab3 100644 --- a/android/src/main/java/com/iterable/reactnative/RNIterableAPIModuleImpl.java +++ b/android/src/main/java/com/iterable/reactnative/RNIterableAPIModuleImpl.java @@ -681,6 +681,19 @@ public void onTokenRegistrationSuccessful(String authToken) { sendEvent(EventName.handleAuthSuccessCalled.name(), null); } + @Override + public void onTokenRegistrationFailed(String reason) { + IterableLogger.e(TAG, "Token registration failed: " + reason); + JSONObject reasonJson = new JSONObject(); + try { + reasonJson.put("reason", reason != null ? reason : "unknown"); + WritableMap eventData = Serialization.convertJsonToMap(reasonJson); + sendEvent(EventName.handleTokenRegistrationFailedCalled.name(), eventData); + } catch (JSONException e) { + IterableLogger.e(TAG, "Failed to send token registration failure event"); + } + } + public void addListener(String eventName) { // Keep: Required for RN built in Event Emitter Calls. } @@ -811,5 +824,6 @@ enum EventName { handleInAppCalled, handleUrlCalled, receivedIterableEmbeddedMessagesChanged, - receivedIterableInboxChanged + receivedIterableInboxChanged, + handleTokenRegistrationFailedCalled } diff --git a/ios/RNIterableAPI/ReactIterableAPI.swift b/ios/RNIterableAPI/ReactIterableAPI.swift index c7b5fc745..52606bbcf 100644 --- a/ios/RNIterableAPI/ReactIterableAPI.swift +++ b/ios/RNIterableAPI/ReactIterableAPI.swift @@ -35,6 +35,7 @@ import React case handleAuthFailureCalled case handleEmbeddedMessageUpdateCalled case handleEmbeddedMessagingDisabledCalled + case handleTokenRegistrationFailedCalled } @objc public static var supportedEvents: [String] { @@ -818,6 +819,13 @@ extension ReactIterableAPI: IterableAuthDelegate { } public func onTokenRegistrationFailed(_ reason: String?) { + ITBError("Token registration failed: \(reason ?? "unknown reason")") + guard shouldEmit else { + return + } + delegate?.sendEvent( + withName: EventName.handleTokenRegistrationFailedCalled.rawValue, + body: ["reason": reason ?? "unknown"] as [String: Any]) } } diff --git a/src/core/classes/Iterable.ts b/src/core/classes/Iterable.ts index 983fab49b..c33762bd9 100644 --- a/src/core/classes/Iterable.ts +++ b/src/core/classes/Iterable.ts @@ -956,6 +956,9 @@ export class Iterable { RNEventEmitter.removeAllListeners( IterableEventName.handleEmbeddedMessagingDisabledCalled ); + RNEventEmitter.removeAllListeners( + IterableEventName.handleTokenRegistrationFailedCalled + ); } /** @@ -1109,6 +1112,25 @@ export class Iterable { ); } } + + // Always listen for token registration failures so they are surfaced + // to developers. Without this, push registration errors (e.g. invalid + // pushIntegrationName) are silently swallowed. + RNEventEmitter.addListener( + IterableEventName.handleTokenRegistrationFailedCalled, + (dict: { reason: string }) => { + const reason = dict?.reason ?? 'unknown'; + + // Always log to console.error so developers can see the failure + // even without the callback configured + console.error( + `[Iterable] Push token registration failed: ${reason}. ` + + 'Check that pushIntegrationName in your IterableConfig is valid.' + ); + + Iterable.savedConfig.onTokenRegistrationFailed?.(reason); + } + ); } /** diff --git a/src/core/classes/IterableConfig.ts b/src/core/classes/IterableConfig.ts index 34befbbc8..22d6cf2ca 100644 --- a/src/core/classes/IterableConfig.ts +++ b/src/core/classes/IterableConfig.ts @@ -375,6 +375,28 @@ export class IterableConfig { */ onEmbeddedMessagingDisabled?: () => void; + /** + * A callback function that is called when push token registration fails. + * + * This can happen when `pushIntegrationName` is set to an invalid value, + * or when the `/api/users/registerDeviceToken` endpoint returns an error. + * + * Without this callback, push registration errors are silently ignored, + * making it difficult to diagnose push notification setup issues. + * + * @param reason - A string describing why token registration failed. + * + * @example + * ```typescript + * const config = new IterableConfig(); + * config.onTokenRegistrationFailed = (reason) => { + * console.error('Push token registration failed:', reason); + * }; + * Iterable.initialize('', config); + * ``` + */ + onTokenRegistrationFailed?: (reason: string) => void; + /** * Converts the IterableConfig instance to a dictionary object. * diff --git a/src/core/enums/IterableEventName.ts b/src/core/enums/IterableEventName.ts index 6ea79c754..e3d2c90c8 100644 --- a/src/core/enums/IterableEventName.ts +++ b/src/core/enums/IterableEventName.ts @@ -23,4 +23,6 @@ export enum IterableEventName { handleEmbeddedMessageUpdateCalled = 'handleEmbeddedMessageUpdateCalled', /** Event that fires when embedded messaging is disabled */ handleEmbeddedMessagingDisabledCalled = 'handleEmbeddedMessagingDisabledCalled', + /** Event that fires when push token registration fails (e.g., invalid pushIntegrationName) */ + handleTokenRegistrationFailedCalled = 'handleTokenRegistrationFailedCalled', }