diff --git a/ios/RNPurchases.m b/ios/RNPurchases.m index 52c957f78..d52113c83 100644 --- a/ios/RNPurchases.m +++ b/ios/RNPurchases.m @@ -53,6 +53,27 @@ - (void)sendEventWithName:(NSString *)name body:(id)body { RCT_EXPORT_MODULE(); +// Required for RN 0.65+ NativeEventEmitter (JavaScript class) support +// +// In JavaScript: new NativeEventEmitter(RNPurchases) +// NativeEventEmitter checks if the native module has addListener/removeListeners methods. +// Without these exported methods, construction throws in RN 0.79+. +// +// WHY export them here when our parent class RCTEventEmitter already has them? +// React Native's bridge only exposes methods that are EXPLICITLY exported with RCT_EXPORT_METHOD. +// Parent class methods are NOT automatically visible to JavaScript. We must re-export them here +// to make them callable from JS, then call [super] to use the parent's implementation. +// +// See: https://github.com/RevenueCat/react-native-purchases/issues/1298 +// See: https://github.com/facebook/react-native/blob/main/packages/react-native/React/Modules/RCTEventEmitter.m#L101-L125 +RCT_EXPORT_METHOD(addListener:(NSString *)eventName) { + [super addListener:eventName]; +} + +RCT_EXPORT_METHOD(removeListeners:(double)count) { + [super removeListeners:count]; +} + RCT_EXPORT_METHOD(setupPurchases:(NSString *)apiKey appUserID:(nullable NSString *)appUserID purchasesAreCompletedBy:(nullable NSString *)purchasesAreCompletedBy diff --git a/src/purchases.ts b/src/purchases.ts index d973d23a6..912d0bad0 100644 --- a/src/purchases.ts +++ b/src/purchases.ts @@ -95,7 +95,13 @@ const NATIVE_MODULE_ERROR = // Get the native module or use the browser implementation const usingBrowserMode = shouldUseBrowserMode(); const RNPurchases = usingBrowserMode ? browserNativeModuleRNPurchases : NativeModules.RNPurchases; + // Only create event emitter if native module is available to avoid crash on import +// +// React Native 0.79+ requires native modules to implement addListener() and removeListeners() +// methods for NativeEventEmitter to work. Both iOS and Android native modules now have these. +// See: https://github.com/RevenueCat/react-native-purchases/issues/1298 +// See: https://reactnative.dev/blog/2025/04/08/react-native-0.79 (Breaking Changes section) const eventEmitter = !usingBrowserMode && RNPurchases ? new NativeEventEmitter(RNPurchases) : null; // Helper function to check if native module is available - provides better error message than "Cannot read property X of null"