[FIXED] ReactNative iOS sendEventWithName verursacht „RCTCallableJSModules ist nicht gesetzt“

Ausgabe

ich habe folgende Situation.

2 identische React-Native-Apps (unterscheidet sich nur für BundleId, App-Symbol usw.), die wie folgt strukturiert sind:

-> Projektstruktur

Mein Ziel ist es, ein Ereignis von der nativen Seite über die Bridge an die JS-Schicht zu senden, wenn eine Push-Benachrichtigung vom Benutzer empfangen oder angetippt wurde (vorausgesetzt, die App befindet sich im Vordergrund und der App-Start ist abgeschlossen). Bei der ersten App funktioniert der folgende Code wie erwartet, wenn ich mit dem Befehl eine Push-Benachrichtigung an meinen Simulator auslöse xcrun simctl push <device-id> <bundleId> <filename>.apns, die zweite App stürzt sofort mit folgendem Fehler ab:

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

Hier ist die Codeimplementierung von sendEventWithName von RCTEventEmitter, die die Assertion provoziert.

Ich weiß nicht, ob es ein Problem mit meiner Implementierung ist. Bei 1 der 2 Apps klappt es wie am Schnürchen, bei der anderen 💥. Wer kann mir helfen, das Problem im Code zu finden? Wahrscheinlich ein Problem mit der Brücke?

Ich habe viele Male versucht, Pods neu zu installieren, das Projekt zu bereinigen und neu zu erstellen. Der Code funktioniert im Projekt A und nicht im Projekt B. Ich kann den Grund nicht herausfinden

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

Und das sind die CustomEventsEmitter-Dateien:

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

HELFEN SIE MIR, BITTE ZU VERSTEHEN

Lösung

Oh, ich habe es gelöst! Es war ein Fehler von mir.

Das AppModule hat die Methoden von CustomEventsEmitter nicht korrekt aufgerufen. Wenn Sie den Code wie unten ändern, werden die Ereignisse korrekt über die RN-Bridge ausgegeben

-(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];
}


Beantwortet von –
se09deluca


Antwort geprüft von –
Candace Johnson (FixError Volunteer)

0 Shares:
Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like