Skip to content

Instantly share code, notes, and snippets.

@brennanMKE
Last active March 7, 2024 04:04
Show Gist options
  • Save brennanMKE/1ebba84a0fd7c2e8b481e4f8a5349b99 to your computer and use it in GitHub Desktop.
Save brennanMKE/1ebba84a0fd7c2e8b481e4f8a5349b99 to your computer and use it in GitHub Desktop.
React Native Event Emitter for RCTEventEmitter in Objective-C and Swift

React Native Event Emitter with Swift

You are building a React Native app and want to work in Swift as much as possible while minimizing Objective-C. This task can be a real challenge. Since the Objective-C runtime is leveraged for communicating with the JavaScript context and Swift does not support macros it will be necessary to use Objective-C to bridge React Native and Swift.

Read Exporting Swift to get an understanding of how it can be done. First you need an Objective-C implementation file. In this case it is called ReactNativeEventEmitter.m and it has what you need to make a module and method available to React Native. In ReactNativeEventEmitter.swift you will find the actual implementation with the class and function marked with objc so both are available to the Objective-C runtime.

When your app starts up the module and function will be made available to React Native which will create and instance of the module which is ReactNativeEventEmitter and sets the critical bridge property which allows for communicating between Swift and React Native.

Once React Native initializes ReactNativeEventEmitter it will be registered with EventEmitter so it can be used to send events. Be sure to put all of your events into the array of events returned by supportedEvents as it is used to check for valid events on the React Native side. Events which are not recognized will cause an error.


Brennan Stehling - 2017

class EventEmitter
/// Shared Instance.
public static var sharedInstance = EventEmitter()
// ReactNativeEventEmitter is instantiated by React Native with the bridge.
private static var eventEmitter: ReactNativeEventEmitter!
private init() {}
// When React Native instantiates the emitter it is registered here.
func registerEventEmitter(eventEmitter: ReactNativeEventEmitter) {
EventEmitter.eventEmitter = eventEmitter
}
func dispatch(name: String, body: Any?) {
eventEmitter.sendEvent(withName: name, body: body)
}
/// All Events which must be support by React Native.
lazy var allEvents: [String] = {
var allEventNames: [String] = []
// Append all events here
return allEventNames
}()
}
//
// ReactNativeEventEmitter.m
// See: http://facebook.github.io/react-native/releases/0.43/docs/native-modules-ios.html#exporting-swift
//
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
@interface RCT_EXTERN_MODULE(ReactNativeEventEmitter, RCTEventEmitter)
RCT_EXTERN_METHOD(supportedEvents)
@end
//
// ReactNativeEventEmitter.swift
//
import Foundation
@objc(ReactNativeEventEmitter)
open class ReactNativeEventEmitter: RCTEventEmitter {
override init() {
super.init()
EventEmitter.sharedInstance.registerEventEmitter(eventEmitter: self)
}
/// Base overide for RCTEventEmitter.
///
/// - Returns: all supported events
@objc open override func supportedEvents() -> [String] {
return EventEmitter.sharedInstance.allEvents
}
}
@ysfzrn
Copy link

ysfzrn commented Dec 28, 2023

Is there any argument against putting the singleton instance inside the RNEventEmitter itself?

Something like this:

/* RNEventEmitter.m */

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

@interface RCT_EXTERN_MODULE(RNEventEmitter, RCTEventEmitter)
  RCT_EXTERN_METHOD(supportedEvents)
@end
/* RNEventEmitter.swift*/

@objc(RNEventEmitter)
open class RNEventEmitter: RCTEventEmitter {

  public static var emitter: RCTEventEmitter!

  override init() {
    super.init()
    RNEventEmitter.emitter = self
  }

  open override func supportedEvents() -> [String] {
    ["onReady", "onPending", "onFailure"]      // etc. 
  }
}

Usage:

RNEventEmitter.emitter.sendEvent(withName: "onReady", body: [])

or if you have a protocol, say EventApi:

class RNEventApi: EventApi {

  func emitIsReady() {
    RNEventEmitter.emitter.sendEvent(withName: "onReady", body: [])
  }

}

This is perfect, thank you :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment