7

How can I send event's to JavaScript in Swift?

There is examples of Objc code how to send event to JavaScript, but I need to do in swift?

#import "RCTBridge.h"
#import "RCTEventDispatcher.h"

@implementation CalendarManager

@synthesize bridge = _bridge;

- (void)calendarEventReminderReceived:(NSNotification *)notification
{
  NSString *eventName = notification.userInfo[@"name"];
  [self.bridge.eventDispatcher sendAppEventWithName:@"EventReminder"
                                               body:@{@"name": eventName}];
}

@end
Kostiantyn Koval
  • 8,407
  • 1
  • 45
  • 58

3 Answers3

12

I was just trying to figure this out myself. It was actually surprisingly easy. Heres how I did it:

EventTests.m

#import "RCTBridgeModule.h"

@interface RCT_EXTERN_MODULE(EventTests, NSObject)

RCT_EXTERN_METHOD( testEvent:(NSString *)eventName )

@end

EventTests.Swift

import UIKit

@objc( EventTests )
class EventTests: NSObject {
    // Swift doesn't have synthesize - just define the variable
    var bridge: RCTBridge!

    @objc func testEvent( eventName: String ) {
        self.bridge.eventDispatcher.sendAppEventWithName( eventName, body: "Woot!" )
    }
}

MyModule.js

var React      = require( 'react-native' );
var EventTests = require( 'NativeModules' ).EventTests;

var {
    Component,
    NativeAppEventEmitter
} = React;

var testEventName = 'test';

class MyModule extends Component {

    constructor( options ) {
        super( options );

        // Register for our test event
        NativeAppEventEmitter.addListener( testEventName, ( body ) => {
            console.log( body );
        });

        // Call objective c function, which will emit our test event
        EventTests.testEvent( testEventName );
    }
}

module.exports = MyModule;

Also make sure to include a few imports in your bridging header:

#import "RCTBridge.h"
#import "RCTEventDispatcher.h"
Stephen Donnell
  • 810
  • 9
  • 20
  • Wow, Sweet. I had to make it in Objc and sent call Objc code from Swift – Kostiantyn Koval Oct 26 '15 at 09:41
  • 1
    I have this working on a few Swift other classes but `self.bridge` is `nil` when used in my AppDelegate. Any ideas? – jamesfzhang Dec 15 '15 at 00:01
  • @jamesfzhang did you manage to fix self.bridge being nil in AppDelegate – coldbuffet Feb 04 '16 at 03:43
  • @coldbuffet no I did not, just ended up sending a notification to another swift class that sends out the event via the bridge – jamesfzhang Feb 04 '16 at 17:27
  • @jamesfzhang I asked the question on stack overflow and I got a great response from Nick Lockwood from the React Native team. Check it out here http://stackoverflow.com/questions/35192324/react-native-sending-events-from-native-to-javascript-in-appdelegate-ios/35194910#35194910 – coldbuffet Feb 04 '16 at 22:38
  • 7
    Using this implementation will now create a warning saying that "'sendAppEventWithName(_:body:)' is deprecated: Subclass RCTEventEmitter instead", I'm not sure what the replacement is, but I think there is a new way to do this. – CallMeNorm Jun 29 '16 at 20:29
  • What is the use of variable " bridge " here. Can anyone explain me? – Ramakrishna Sep 19 '16 at 13:51
2

Ok, things had already changed, so just to update what I did:

I have my swift module and I need to make it be a RCTEventEmitter

import React

@objc(MyModule)
class MyModule: RCTEventEmitter {

   var hasListener: Bool = false

   override func startObserving() {
     hasListener = true
   }

   override func stopObserving() {
     hasListener = false
   }

  @objc
  func sendMyEvent() {
     if hasListener {
       self.sendEvent(withName:"MyEvent", body:["name": eventName]];
     }
  }

  @objc
  override func supportedEvents() -> [String]! {
    return ["MyEvent","MyEvent2"];
  }

}

This is how my bridging header looks like:

#import <React/RCTBridgeModule.h>
#import <React/RCTBridge.h>
#import <React/RCTEventDispatcher.h>

And in the .m file I changed it to:

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

@interface RCT_EXTERN_MODULE(MyModule, RCTEventEmitter)
ggrana
  • 2,335
  • 2
  • 21
  • 31
1

It is insane. How to pass anything as an event to Javascript, if this event is generated by Swift?

I am stuck with the same issue here: "Subclass RCTEventEmitter instead" The following does not work anymore.

self.bridge.eventDispatcher.sendAppEventWithName( eventName, body: "Woot!" )

I tried:

var emitter: RCTEventEmitter = RCTEventEmitter()

Instead of:

var bridge: RCTBridge!

Conesquently:

self.emitter.sendEvent(withName: eventName, body: "Woot!")

Instead of

self.bridge.eventDispatcher.sendAppEventWithName( eventName, body: "Woot!" )

From bad to worst, I go from a warning to the following hard error:

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Error when sending event: Location with body: woot. Bridge is not set. This is probably because you've explicitly synthesized the bridge in RCTEventEmitter, even though it's inherited from RCTEventEmitter.'

The documentation is very poor and old. It looks that not many people is using this hot garbage. Very frustrating.

This is an old implementation and it no longer works:

example1

Another article, not very useful:

example2

Very thorough and complex implementation, no one knows if it still works. In my case it doesn't, I get the same error above:

example3

Then there are several objective-c swift cocktails, which I cannot drink at my age:

example4

1048576
  • 615
  • 9
  • 27
  • 2
    To us, solving this problem has been as finding water in the desert. The problem is not that complicated. Any bad API description would do the job, instead of a missing description. https://gist.github.com/byvalentino/782bcb06e5b04dfe1c638c77e7048c17 – user1641876 Nov 20 '20 at 05:29