2

i've the following situation.

2 identical react-native apps (differs only for bundleId, app icon etc) structured like this:

-> project structure

My goal it's to emit an event from native side to the JS layer through the bridge when a push notification has been received or tapped by the user (assuming that the app is in foreground and app has finished launching). On the first App the following code works as expected when i trigger a push notification to my simulator with the command xcrun simctl push <device-id> <bundleId> <filename>.apns, the second app crash immediatly with the following error:

Thread 1: "Error when sending event: pushDelivered with body: <the string passed as body>. RCTCallableJSModules is not set. This is probably because you've explicitly synthesized the RCTCallableJSModules in CustomEventsEmitter, even though it's inherited from RCTEventEmitter."

-> xcode view

Here is the code implementation of RCTEventEmitter's sendEventWithName that provoke the assertion.

I don't know if it's a problem with my implementation. In 1 of the 2 apps works like a charm, in the other . Anyone can help me find the problem in the code ? Probably a problem with the bridge?

i've tried many times to reinstall pods, clean project and rebuild. The code works on the project A and not on the project B.. i cannot figure out the reason

AppDelegate.h

#import <React/RCTBridgeDelegate.h>
#import <React/RCTBridgeModule.h>
#import <UIKit/UIKit.h>
#import <UserNotifications/UserNotifications.h>
#import <UserNotifications/UNUserNotificationCenter.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, RCTBridgeModule, UNUserNotificationCenterDelegate>

@property (nonatomic, strong) UIWindow *window;
@property (nonatomic, strong) NSDictionary *receivedNotificationUserInfo;

@end

AppDelegate.mm

#import "AppDelegate.h"

#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>

#import <React/RCTAppSetupUtils.h>
#import <UserNotifications/UserNotifications.h>

#import "CustomEventsEmitter.h"

@implementation AppDelegate

bool hasFinishedLaunching = false;
CustomEventsEmitter *customEventsEmitter = NULL;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  hasFinishedLaunching = true;
  customEventsEmitter = [CustomEventsEmitter allocWithZone: nil];

  RCTAppSetupPrepareApp(application);

  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];


  NSDictionary *initProps = [self prepareInitialProps];
  UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"MyAppName", initProps);

  if (@available(iOS 13.0, *)) {
    rootView.backgroundColor = [UIColor systemBackgroundColor];
  } else {
    rootView.backgroundColor = [UIColor whiteColor];
  }

  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];


  // Define UNUserNotificationCenter
  UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
  center.delegate = self;


  return YES;
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}

-(void)applicationDidBecomeActive:(UIApplication *)application
{
  application.applicationIconBadgeNumber = 0;
}


// The method will be called on the delegate when the user responded to the notification by opening
// the application, dismissing the notification or choosing a UNNotificationAction. The delegate
// must be set before the application returns from applicationDidFinishLaunching:.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void (^)(void))completionHandler {

  NSLog(@"didReceiveNotificationResponse response: %@", response);
  NSDictionary *userInfo = response.notification.request.content.userInfo;
  if (userInfo[@"_od"]){

    // if no listeners has been registered yet, store the value
    // this is the case when the notification was clicked from closed app
    if(![customEventsEmitter hasListeners]) {
      // handle this case ...
    }
    // if listeners has been registered, emit an event
    // this is the case when the notification was clicked from foreground app
    else {
      [self emitPushTappedEvent:userInfo[@"_od"]];
    }

  }

  if (completionHandler != nil) {
    completionHandler();
  }
}


//Called when a notification is delivered to a foreground app.
-(void)userNotificationCenter:(UNUserNotificationCenter *)center
      willPresentNotification:(UNNotification *)notification
        withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
  NSDictionary *userInfo = notification.request.content.userInfo;
  NSLog(@"User Info : %@", userInfo);

  [self emitPushDeliveredEvent:userInfo[@"_od"]];

  completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge);
}


-(void)emitPushDeliveredEvent:(NSString*)value {
  NSLog(@"emitPushDeliveredEvent called");
  [customEventsEmitter sendEventWithName:@"pushDelivered" body:value];
}

-(void)emitPushTappedEvent:(NSString*)value {
  NSLog(@"emitPushTappedEvent called");
  [customEventsEmitter sendEventWithName:@"pushTapped" body:value];
}



@end

And this are the CustomEventsEmitter files:

CustomEventsEmitter.h

#ifndef CustomEventsEmitter_h
#define CustomEventsEmitter_h


#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

@interface CustomEventsEmitter : RCTEventEmitter <RCTBridgeModule>

- (void)sendEventName:(NSString *)eventName body:(id)body;
- (bool)hasListeners;

@end


#endif

CustomEventsEmitter.m


#import "CustomEventsEmitter.h"

@implementation CustomEventsEmitter
{
  bool hasListeners;
}

RCT_EXPORT_MODULE(CustomEventsEmitter);

+ (id)allocWithZone:(NSZone *)zone {
  static CustomEventsEmitter *sharedInstance = nil;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    sharedInstance = [super allocWithZone:zone];
  });
  return sharedInstance;
}


- (NSArray<NSString *> *)supportedEvents {
  return @[@"pushDelivered", @"pushTapped"];
}

// Will be called when this module's first listener is added.
-(void)startObserving {
  hasListeners = YES;
  // Set up any upstream listeners or background tasks as necessary
}

// Will be called when this module's last listener is removed, or on dealloc.
-(void)stopObserving {
  hasListeners = NO;
  // Remove upstream listeners, stop unnecessary background tasks
}

-(bool)hasListeners {
  return hasListeners;
}


- (void)sendEventName:(NSString *)eventName body:(id)body {
  if (hasListeners) {
    NSLog(@"CustomEventsEmitter sendEventName emitting event: %@", eventName);
    [self sendEventWithName:eventName   body:body];
  } else {
    NSLog(@"CustomEventsEmitter sendEventName called without listeners: %@", eventName);
  }
}

@end

HELP ME UNDERSTAND PLEASEEEE

se09deluca
  • 61
  • 7

1 Answers1

3

Oh i've solved it! It was a mistake of mine.

The AppModule didn't invoke the CustomEventsEmitter's methods correctly.. changing the code like below makes the events be emitted correctly through the RN bridge

-(void)emitPushDeliveredEvent:(NSString*)value {
  NSLog(@"emitPushDeliveredEvent called");
  [customEventsEmitter sendEventName:@"pushDelivered" body:value];
//[customEventsEmitter sendEventWithName:@"pushDelivered" body:value];
}

-(void)emitPushTappedEvent:(NSString*)value {
  NSLog(@"emitPushTappedEvent called");
  [customEventsEmitter sendEventName:@"pushTapped" body:value];
//[customEventsEmitter sendEventWithName:@"pushTapped" body:value];
}
se09deluca
  • 61
  • 7
  • I am also incurred by this kind of error pictures attached in link below https://docs.google.com/document/d/1JO0NXJpR6GekrRUhmKUmyJFvKnRfIofSfb0vn7A-Rv8/edit?usp=sharing – Hamza Kashif Aug 22 '23 at 10:11