├── .gitattributes ├── .gitignore ├── README.md ├── RNEventEmitter.podspec ├── android ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── lufinkey │ └── react │ └── eventemitter │ ├── RNEventCallback.java │ ├── RNEventConformer.java │ ├── RNEventEmitter.java │ ├── RNEventEmitterPackage.java │ └── RNModuleEvents.java ├── index.js ├── ios ├── RNEventBridge.h ├── RNEventBridge.m ├── RNEventCallback.h ├── RNEventCallback.m ├── RNEventConformer.h ├── RNEventEmitter.h ├── RNEventEmitter.m ├── RNEventEmitter.podspec ├── RNEventEmitter.xcodeproj │ └── project.pbxproj ├── RNEventEmitter.xcworkspace │ └── contents.xcworkspacedata ├── RNModuleEvents.h └── RNModuleEvents.m └── package.json /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # OSX 3 | # 4 | .DS_Store 5 | .goutputstream* 6 | 7 | # node.js 8 | # 9 | node_modules/ 10 | npm-debug.log 11 | yarn-error.log 12 | 13 | 14 | # Xcode 15 | # 16 | build/ 17 | *.pbxuser 18 | !default.pbxuser 19 | *.mode1v3 20 | !default.mode1v3 21 | *.mode2v3 22 | !default.mode2v3 23 | *.perspectivev3 24 | !default.perspectivev3 25 | xcuserdata 26 | *.xccheckout 27 | *.moved-aside 28 | DerivedData 29 | *.hmap 30 | *.ipa 31 | *.xcuserstate 32 | project.xcworkspace 33 | 34 | 35 | # Android/IntelliJ 36 | # 37 | /android/gradle/ 38 | gradlew 39 | gradlew.bat 40 | build/ 41 | .idea 42 | .gradle 43 | local.properties 44 | *.iml 45 | 46 | # BUCK 47 | buck-out/ 48 | \.buckd/ 49 | *.keystore 50 | 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # react-native-events 3 | 4 | A full EventEmitter implementation for react-native modules 5 | 6 | The *documented* way to send and receive events from a native module requires you to use different native classes *and* different javascript classes on iOS vs Android, *and* the events are global and could easily collide with another module's events. This module unifies the native events process by allowing native modules to conform to node's [EventEmitter](https://nodejs.org/dist/latest-v9.x/docs/api/events.html#events_class_eventemitter) class, meaning all of the methods from EventEmitter are callable from your module instance. 7 | 8 | 9 | 10 | 11 | ## Setup 12 | 13 | Since this module is only meant to be used with other native modules, you have to add this module as a dependency inside of your native module (NOT inside of your main project): 14 | 15 | ```bash 16 | npm install --save react-native-events 17 | ``` 18 | 19 | **note:** Inside your main project (NOT inside your native module), after setting up your native module and adding it to your `package.json` file, you must run `npm install` to install your module and its dependencies, and `react-native link` to actually link the native code to your app project. 20 | 21 | In order to set up your native module to conform to an EventEmitter on each platform, you must perform the following steps: 22 | 23 | #### iOS 24 | 25 | Add `$(SRCROOT)/../../react-native-events/ios` to *Header Search Paths* in the project settings of your native module. (If your module is a scoped package, you'll need to add one more `../` to the path) 26 | 27 | Then make your native module conform to `RNEventConformer`: 28 | 29 | ```objc 30 | // MyNativeModule.h 31 | 32 | #import 33 | 34 | #import 35 | 36 | @interface MyNativeModule : NSObject 37 | 38 | ... 39 | 40 | @end 41 | ``` 42 | 43 | ```objc 44 | // MyNativeModule.m 45 | 46 | #import "MyNativeModule.h" 47 | 48 | @implementation MyNativeModule 49 | 50 | @synthesize bridge = _bridge; 51 | 52 | ... 53 | 54 | RCT_EXPORT_METHOD(__registerAsJSEventEmitter:(int)moduleId) 55 | { 56 | [RNEventEmitter registerEventEmitterModule:self withID:moduleId bridge:_bridge]; 57 | } 58 | 59 | -(void)onNativeEvent:(NSString*)eventName params:(NSArray*)params 60 | { 61 | // Called when an event for this module is emitted from native code 62 | } 63 | 64 | -(void)onJSEvent:(NSString*)eventName params:(NSArray*)params 65 | { 66 | // Called when an event for this module is emitted from javascript 67 | } 68 | 69 | -(void)onEvent:(NSString*)eventName params:(NSArray*)params 70 | { 71 | // Called when any event for this module is emitted 72 | } 73 | 74 | ... 75 | 76 | @end 77 | 78 | ``` 79 | 80 | #### Android 81 | 82 | Edit `android/build.gradle` and add the `react-native-events` project to `dependencies` 83 | 84 | ``` 85 | ... 86 | dependencies { 87 | compile 'com.facebook.react:react-native:+' 88 | compile project(path: ':react-native-events') 89 | } 90 | ... 91 | ``` 92 | 93 | Then make your native module conform to `RNEventConformer`: 94 | 95 | ```java 96 | package com.reactlibrary.mynativemodule; 97 | 98 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 99 | import com.facebook.react.bridge.ReactMethod; 100 | 101 | import com.lufinkey.react.eventemitter.RNEventConformer; 102 | import com.lufinkey.react.eventemitter.RNEventEmitter; 103 | 104 | public class MyNativeModule extends ReactContextBaseJavaModule implements RNEventConformer 105 | { 106 | ... 107 | 108 | @Override 109 | @ReactMethod 110 | public void __registerAsJSEventEmitter(int moduleId) 111 | { 112 | RNEventEmitter.registerEventEmitterModule(this.reactContext, moduleId, this); 113 | } 114 | 115 | @Override 116 | public void onNativeEvent(String eventName, Object... args) 117 | { 118 | // Called when an event for this module is emitted from native code 119 | } 120 | 121 | @Override 122 | public void onJSEvent(String eventName, Object... args) 123 | { 124 | // Called when an event for this module is emitted from javascript 125 | } 126 | 127 | @Override 128 | public void onEvent(String eventName, Object... args) 129 | { 130 | // Called when any event for this module is emitted 131 | } 132 | 133 | ... 134 | } 135 | ``` 136 | 137 | 138 | 139 | 140 | ## Usage 141 | 142 | In order for your native module to conform to node's EventEmitter class, you must first register your module as an event emitter inside your native module's `index.js` using the `register` method, and then conform it using the `conform` method: 143 | 144 | ```javascript 145 | import { NativeModules } from 'react-native'; 146 | import RNEvents from 'react-native-events'; 147 | 148 | var MyNativeModule = NativeModules.MyNativeModule; 149 | 150 | // register your event emitter to be able to send events 151 | RNEvents.register(MyNativeModule); 152 | // conform your native module to EventEmitter 153 | RNEvents.conform(MyNativeModule); 154 | 155 | 156 | export default MyNativeModule; 157 | ``` 158 | 159 | #### Sending and receiving events in javascript 160 | 161 | Once your native module has been registered and conformed, you may use any method available in the [EventEmitter](https://nodejs.org/dist/latest-v9.x/docs/api/events.html#events_class_eventemitter) class. 162 | 163 | To receive events, you can just use the `addListener` method from EventEmitter. To send events, you can just use the `emit` method from EventEmitter. 164 | 165 | ```javascript 166 | import MyNativeModule from 'my-native-module-package'; 167 | 168 | // add a listener to listen for the "somethingHappened" event 169 | MyNativeModule.addListener("somethingHappened", (arg1, arg2) => { 170 | console.log("my event was called, and I received some data!"); 171 | console.log("arg1: ", arg1); 172 | console.log("arg2: ", arg2); 173 | }); 174 | 175 | // send the "somethingHappened" event with 2 arguments 176 | MyNativeModule.emit("somethingHappened", {a:"ayy", b:"lmao"}, {c:"It's 4:20 somewhere"}); 177 | ``` 178 | 179 | #### Sending and receiving events in native code 180 | 181 | Any event sent from your native code will be received in your native modules's javascript event listeners for that event. Likewise, any event sent from javascript will be received in the `onEvent` and `onJSEvent` methods in your native code. 182 | 183 | The following code shows how to send the same `somethingHappened` event from your native code's module class: 184 | 185 | **Objective-C** 186 | 187 | ```objc 188 | 189 | // create event arguments 190 | NSDictionary* arg1 = @{ @"a":@"ayy", @"b":@"lmao" }; 191 | NSDictionary* arg2 = @{ @"c":@"It's 4:20 somewhere" }; 192 | 193 | // send the event 194 | [RNEventEmitter emitEvent:@"somethingHappened" withParams:@[ arg1, arg2 ] module:self bridge:_bridge]; 195 | ``` 196 | 197 | **Java** 198 | 199 | ```objc 200 | 201 | // create event arguments 202 | WritableMap arg1 = Arguments.createMap(); 203 | data.putString("a", "ayy"); 204 | data.putString("b", "lmao"); 205 | WritableMap arg2 = Arguments.createMap(); 206 | data.putString("c", "It's 4:20 somewhere"); 207 | 208 | // send the events 209 | RNEventEmitter.emitEvent(this.reactContext, this, "somethingHappened", arg1, arg2); 210 | ``` 211 | 212 | 213 | --- 214 | 215 | 216 | ## API Reference 217 | 218 | ```javascript 219 | import RNEvents from 'react-native-events'; 220 | ``` 221 | 222 | ### Methods 223 | 224 | - **register**( *nativeModule* ) 225 | 226 | Registers a native module to be able to send / receive events between native code and javascript. 227 | 228 | - *Parameters* 229 | 230 | - **nativeModule** - a react native module 231 | 232 | - *Returns* 233 | 234 | - The registered native module 235 | 236 | 237 | - **conform**( *nativeModule* ) 238 | 239 | Conforms a registered native module to the EventEmitter prototype. This creates an EventEmitter instance and adds all of its methods to the native module object. The native module will not inherit from EventEmitter and `nativeModule instanceof EventEmitter` will still return false. Calling the `emit` method will call `emitNativeEvent` and `emitJSEvent` on the module, in that order 240 | 241 | - *Parameters* 242 | 243 | - **nativeModule** - a react native module 244 | 245 | - *Returns* 246 | 247 | - The conformed native module 248 | 249 | 250 | - **emitNativeEvent**( *nativeModule*, *eventName*, ...*args* ) 251 | 252 | Sends an event to the module's native code. This will trigger the native module's `onEvent` and `onJSEvent` methods, in that order. Note that this only triggers the native code events. No javascript events will be emitted. 253 | 254 | - *Parameters* 255 | 256 | - **nativeModule** - a react native module 257 | - **eventName** - the name of the event 258 | - **args** - the arguments to be passed to the event 259 | 260 | 261 | - **emitJSEvent**( *nativeModule*, *eventName*, ...*args* ) 262 | 263 | Sends an event to the module's javascript event subscribers. This will trigger on EventEmitters subscribed with `addPreSubscriber`, on the conformed module, and on EventEmitters subscribed with `addSubscriber`, in that order. Note that this only triggers the javascript events. No native code events will be emitted. 264 | 265 | - *Parameters* 266 | 267 | - **nativeModule** - a react native module 268 | - **eventName** - the name of the event 269 | - **args** - the arguments to be passed to the event 270 | 271 | 272 | - **addSubscriber**( *nativeModule*, *subscriber* ) 273 | 274 | Subscribes an EventEmitter to the events sent by the native module. 275 | 276 | - *Parameters* 277 | 278 | - **nativeModule** - a react native module 279 | - **subscriber** - an EventEmitter to be subscribed to the module's events 280 | 281 | 282 | - **removeSubscriber**( *nativeModule*, *subscriber* ) 283 | 284 | Unsubscribes an EventEmitter from the events sent by the native module. 285 | 286 | - *Parameters* 287 | 288 | - **nativeModule** - a react native module 289 | - **subscriber** - an EventEmitter to be unsubscribed from the module's events 290 | 291 | 292 | - **addPreSubscriber**( *nativeModule*, *subscriber* ) 293 | 294 | Subscribes an EventEmitter to the events sent by the native module. Subscribers added using this method receive events before the conformed native module and before subscribers added using `addSubscriber`. 295 | 296 | - *Parameters* 297 | 298 | - **nativeModule** - a react native module 299 | - **subscriber** - an EventEmitter to be subscribed to the module's events 300 | 301 | 302 | - **removePreSubscriber**( *nativeModule*, *subscriber* ) 303 | 304 | Unsubscribes an EventEmitter from the events sent by the native module. This method will only unsubscribe subscribers added using the `addPreSubscriber` method. 305 | 306 | - *Parameters* 307 | 308 | - **nativeModule** - a react native module 309 | - **subscriber** - an EventEmitter to be unsubscribed from the module's events 310 | -------------------------------------------------------------------------------- /RNEventEmitter.podspec: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) 4 | 5 | Pod::Spec.new do |s| 6 | s.name = "RNEventEmitter" 7 | s.version = package['version'] 8 | s.summary = package['description'] 9 | s.license = package['license'] 10 | 11 | s.authors = package['author'] 12 | s.homepage = package['repository']['url'] 13 | s.platform = :ios, "9.0" 14 | 15 | s.source = { :git => package['repository']['url'], :tag => "v#{s.version}" } 16 | s.source_files = "ios/**/*.{h,m}" 17 | 18 | s.dependency 'React' 19 | end 20 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | buildscript { 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.2.1' 10 | } 11 | } 12 | 13 | apply plugin: 'com.android.library' 14 | 15 | android { 16 | compileSdkVersion 26 17 | buildToolsVersion '28.0.3' 18 | 19 | defaultConfig { 20 | targetSdkVersion 26 21 | versionCode 1 22 | versionName "1.0" 23 | } 24 | lintOptions { 25 | abortOnError false 26 | } 27 | } 28 | 29 | repositories { 30 | mavenCentral() 31 | } 32 | 33 | dependencies { 34 | api 'com.facebook.react:react-native:+' 35 | } 36 | 37 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /android/src/main/java/com/lufinkey/react/eventemitter/RNEventCallback.java: -------------------------------------------------------------------------------- 1 | package com.lufinkey.react.eventemitter; 2 | 3 | import com.facebook.react.bridge.Callback; 4 | 5 | public class RNEventCallback { 6 | private Callback callback; 7 | private boolean onlyOnce; 8 | 9 | public RNEventCallback(Callback callback, boolean onlyOnce) { 10 | this.callback = callback; 11 | this.onlyOnce = onlyOnce; 12 | } 13 | 14 | public Callback getCallback() { 15 | return callback; 16 | } 17 | 18 | public boolean isCalledOnlyOnce() { 19 | return onlyOnce; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /android/src/main/java/com/lufinkey/react/eventemitter/RNEventConformer.java: -------------------------------------------------------------------------------- 1 | package com.lufinkey.react.eventemitter; 2 | 3 | public interface RNEventConformer { 4 | void __registerAsJSEventEmitter(int moduleId); 5 | 6 | void onNativeEvent(String eventName, Object... args); 7 | void onJSEvent(String eventName, Object... args); 8 | void onEvent(String eventName, Object... args); 9 | } 10 | -------------------------------------------------------------------------------- /android/src/main/java/com/lufinkey/react/eventemitter/RNEventEmitter.java: -------------------------------------------------------------------------------- 1 | 2 | package com.lufinkey.react.eventemitter; 3 | 4 | import com.facebook.react.bridge.Arguments; 5 | import com.facebook.react.bridge.ReactApplicationContext; 6 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 7 | import com.facebook.react.bridge.ReactMethod; 8 | import com.facebook.react.bridge.Callback; 9 | import com.facebook.react.bridge.ReadableArray; 10 | import com.facebook.react.bridge.WritableArray; 11 | import com.facebook.react.bridge.WritableMap; 12 | import com.facebook.react.module.annotations.ReactModule; 13 | import com.facebook.react.modules.core.DeviceEventManagerModule; 14 | 15 | import java.util.HashMap; 16 | 17 | @ReactModule(name = "RNEventEmitter") 18 | public class RNEventEmitter extends ReactContextBaseJavaModule { 19 | private final ReactApplicationContext reactContext; 20 | 21 | public static final String EVENT_NAME = "ayylmao_dicksnshit_nobodyUsethisevent PLS OK THANKS"; 22 | 23 | private final HashMap registeredModules = new HashMap<>(); 24 | private final HashMap modules = new HashMap<>(); 25 | 26 | public RNEventEmitter(ReactApplicationContext reactContext) { 27 | super(reactContext); 28 | this.reactContext = reactContext; 29 | } 30 | 31 | @Override 32 | public String getName() { 33 | return "RNEventEmitter"; 34 | } 35 | 36 | 37 | 38 | public static RNEventEmitter getMainEventEmitter(ReactApplicationContext context) { 39 | return context.getNativeModule(RNEventEmitter.class); 40 | } 41 | 42 | 43 | 44 | public static void registerEventEmitterModule(ReactApplicationContext context, int moduleId, RNEventConformer module) { 45 | RNEventEmitter eventEmitter = getMainEventEmitter(context); 46 | if(eventEmitter == null) { 47 | System.out.println("Error: No RNEventEmitter is available to register to"); 48 | return; 49 | } 50 | eventEmitter.registerModule(moduleId, module); 51 | } 52 | 53 | private void registerModule(int moduleId, RNEventConformer module) { 54 | synchronized (registeredModules) { 55 | if(registeredModules.containsValue(module)) { 56 | throw new IllegalArgumentException("Module "+module+" has already been registered"); 57 | } 58 | if(registeredModules.containsKey(moduleId)) { 59 | throw new IllegalArgumentException("moduleId "+moduleId+" has already been registered to a module"); 60 | } 61 | registeredModules.put(moduleId, module); 62 | } 63 | } 64 | 65 | 66 | 67 | private RNModuleEvents getModuleEvents(int moduleId) { 68 | synchronized (modules) { 69 | RNModuleEvents moduleEvents = modules.get(moduleId); 70 | if (moduleEvents == null) { 71 | moduleEvents = new RNModuleEvents(); 72 | modules.put(moduleId, moduleEvents); 73 | } 74 | return moduleEvents; 75 | } 76 | } 77 | 78 | private RNEventConformer getRegisteredModule(int moduleId) { 79 | synchronized (registeredModules) { 80 | return registeredModules.get(moduleId); 81 | } 82 | } 83 | 84 | private Integer getRegisteredModuleID(RNEventConformer module) { 85 | synchronized (registeredModules) { 86 | for (HashMap.Entry entry : registeredModules.entrySet()) { 87 | if(entry.getValue() == module) { 88 | return entry.getKey(); 89 | } 90 | } 91 | return null; 92 | } 93 | } 94 | 95 | private WritableArray fromObjectArray(Object[] args) { 96 | WritableArray array = Arguments.createArray(); 97 | for(Object arg : args) { 98 | if(arg == null) { 99 | array.pushNull(); 100 | } 101 | else if(arg instanceof Boolean) { 102 | array.pushBoolean((Boolean)arg); 103 | } 104 | else if(arg instanceof Integer) { 105 | array.pushInt((Integer)arg); 106 | } 107 | else if(arg instanceof Double) { 108 | array.pushDouble((Double)arg); 109 | } 110 | else if(arg instanceof Float) { 111 | array.pushDouble((double)((Float)arg)); 112 | } 113 | else if(arg instanceof String) { 114 | array.pushString((String)arg); 115 | } 116 | else if(arg instanceof WritableArray) { 117 | array.pushArray((WritableArray)arg); 118 | } 119 | else if(arg instanceof WritableMap) { 120 | array.pushMap((WritableMap)arg); 121 | } 122 | else { 123 | throw new IllegalArgumentException("Illegal object type"); 124 | } 125 | } 126 | return array; 127 | } 128 | 129 | 130 | 131 | public static void emitEvent(ReactApplicationContext context, RNEventConformer module, String eventName, Object... args) { 132 | RNEventEmitter eventEmitter = getMainEventEmitter(context); 133 | if(eventEmitter == null) { 134 | System.out.println("Error: No RNEventEmitter is available to emit "+eventName+" event"); 135 | return; 136 | } 137 | eventEmitter.emit(module, eventName, args); 138 | } 139 | 140 | public void emit(RNEventConformer module, String eventName, Object... args) { 141 | Integer moduleId = getRegisteredModuleID(module); 142 | if(moduleId == null) { 143 | System.out.println("Error: Cannot emit "+eventName+" event before "+module+" module has been registered"); 144 | return; 145 | } 146 | getModuleEvents(moduleId).emit(eventName, args); 147 | 148 | WritableMap jsEvent = Arguments.createMap(); 149 | jsEvent.putInt("moduleId", moduleId); 150 | jsEvent.putString("eventName", eventName); 151 | jsEvent.putArray("args", fromObjectArray(args)); 152 | 153 | reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(EVENT_NAME, jsEvent); 154 | 155 | module.onEvent(eventName, args); 156 | module.onNativeEvent(eventName, args); 157 | } 158 | 159 | @ReactMethod 160 | private void emit(int moduleId, String eventName, ReadableArray args) { 161 | RNEventConformer module = getRegisteredModule(moduleId); 162 | if(module == null) { 163 | throw new IllegalArgumentException("No module registered with ID "+moduleId); 164 | } 165 | 166 | Object[] argsArray = args.toArrayList().toArray(); 167 | getModuleEvents(moduleId).emit(eventName, argsArray); 168 | 169 | module.onEvent(eventName, argsArray); 170 | module.onJSEvent(eventName, argsArray); 171 | } 172 | 173 | 174 | 175 | public void addListener(RNEventConformer module, String eventName, Callback callback, boolean once) { 176 | Integer moduleId = getRegisteredModuleID(module); 177 | if(moduleId == null) { 178 | throw new IllegalArgumentException("Module "+module+" has not been registered"); 179 | } 180 | addListener(moduleId, eventName, callback, once); 181 | } 182 | 183 | public void addListener(RNEventConformer module, String eventName, Callback callback) { 184 | addListener(module, eventName, callback, false); 185 | } 186 | 187 | public void addListener(int moduleId, String eventName, Callback callback) { 188 | addListener(moduleId, eventName, callback, false); 189 | } 190 | 191 | public void addListener(int moduleId, String eventName, Callback callback, boolean once) { 192 | getModuleEvents(moduleId).addListener(eventName, callback, once); 193 | } 194 | 195 | 196 | 197 | public void prependListener(RNEventConformer module, String eventName, Callback callback, boolean once) { 198 | Integer moduleId = getRegisteredModuleID(module); 199 | if(moduleId == null) { 200 | throw new IllegalArgumentException("Module "+module+" has not been registered"); 201 | } 202 | prependListener(moduleId, eventName, callback, once); 203 | } 204 | 205 | public void prependListener(RNEventConformer module, String eventName, Callback callback) { 206 | prependListener(module, eventName, callback, false); 207 | } 208 | 209 | public void prependListener(int moduleId, String eventName, Callback callback) { 210 | prependListener(moduleId, eventName, callback, false); 211 | } 212 | 213 | public void prependListener(int moduleId, String eventName, Callback callback, boolean once) { 214 | getModuleEvents(moduleId).prependListener(eventName, callback, once); 215 | } 216 | 217 | 218 | 219 | public void removeListener(RNEventConformer module, String eventName, Callback callback) { 220 | Integer moduleId = getRegisteredModuleID(module); 221 | if(moduleId == null) { 222 | throw new IllegalArgumentException("Module "+module+" has not been registered"); 223 | } 224 | removeListener(moduleId, eventName, callback); 225 | } 226 | 227 | public void removeListener(int moduleId, String eventName, Callback callback) { 228 | getModuleEvents(moduleId).removeListener(eventName, callback); 229 | } 230 | 231 | 232 | 233 | public void removeAllListeners(RNEventConformer module, String eventName) { 234 | Integer moduleId = getRegisteredModuleID(module); 235 | if(moduleId == null) { 236 | throw new IllegalArgumentException("Module "+module+" has not been registered"); 237 | } 238 | removeAllListeners(moduleId, eventName); 239 | } 240 | 241 | public void removeAllListeners(int moduleId, String eventName) { 242 | getModuleEvents(moduleId).removeAllListeners(eventName); 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /android/src/main/java/com/lufinkey/react/eventemitter/RNEventEmitterPackage.java: -------------------------------------------------------------------------------- 1 | 2 | package com.lufinkey.react.eventemitter; 3 | 4 | import java.util.Arrays; 5 | import java.util.Collections; 6 | import java.util.List; 7 | 8 | import com.facebook.react.ReactPackage; 9 | import com.facebook.react.bridge.NativeModule; 10 | import com.facebook.react.bridge.ReactApplicationContext; 11 | import com.facebook.react.uimanager.ViewManager; 12 | import com.facebook.react.bridge.JavaScriptModule; 13 | public class RNEventEmitterPackage implements ReactPackage { 14 | @Override 15 | public List createNativeModules(ReactApplicationContext reactContext) { 16 | return Arrays.asList(new RNEventEmitter(reactContext)); 17 | } 18 | 19 | // Deprecated from RN 0.47 20 | public List> createJSModules() { 21 | return Collections.emptyList(); 22 | } 23 | 24 | @Override 25 | public List createViewManagers(ReactApplicationContext reactContext) { 26 | return Collections.emptyList(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /android/src/main/java/com/lufinkey/react/eventemitter/RNModuleEvents.java: -------------------------------------------------------------------------------- 1 | package com.lufinkey.react.eventemitter; 2 | 3 | import com.facebook.react.bridge.Callback; 4 | 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | 8 | public class RNModuleEvents { 9 | HashMap> eventListeners = new HashMap<>(); 10 | 11 | public void addListener(String eventName, Callback callback, boolean once) { 12 | synchronized (eventListeners) { 13 | ArrayList listeners = eventListeners.get(eventName); 14 | if (listeners == null) { 15 | listeners = new ArrayList<>(); 16 | eventListeners.put(eventName, listeners); 17 | } 18 | listeners.add(new RNEventCallback(callback, once)); 19 | } 20 | } 21 | 22 | public void prependListener(String eventName, Callback callback, boolean once) { 23 | synchronized (eventListeners) { 24 | ArrayList listeners = eventListeners.get(eventName); 25 | if (listeners == null) { 26 | listeners = new ArrayList<>(); 27 | eventListeners.put(eventName, listeners); 28 | } 29 | listeners.add(0, new RNEventCallback(callback, once)); 30 | } 31 | } 32 | 33 | public void removeListener(String eventName, Callback callback) { 34 | synchronized (eventListeners) { 35 | ArrayList listeners = eventListeners.get(eventName); 36 | for (int i=0; i listeners = eventListeners.get(eventName); 60 | if (listeners == null) { 61 | return 0; 62 | } 63 | return listeners.size(); 64 | } 65 | } 66 | 67 | public boolean emit(String eventName, Object... args) { 68 | ArrayList tmpListeners = null; 69 | 70 | synchronized (eventListeners) { 71 | ArrayList listeners = eventListeners.get(eventName); 72 | if(listeners != null) { 73 | tmpListeners = new ArrayList<>(listeners); 74 | } 75 | 76 | // remove "once" event listeners 77 | if(listeners != null) { 78 | for (int i = 0; i < listeners.size(); i++) { 79 | RNEventCallback listener = listeners.get(i); 80 | if (listener.isCalledOnlyOnce()) { 81 | listeners.remove(i); 82 | i--; 83 | } 84 | } 85 | } 86 | } 87 | 88 | if (tmpListeners != null && tmpListeners.size() > 0) { 89 | //invoke events 90 | for (int i = 0; i < tmpListeners.size(); i++) { 91 | RNEventCallback listener = tmpListeners.get(i); 92 | listener.getCallback().invoke(args); 93 | } 94 | return true; 95 | } 96 | return false; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | import { 3 | DeviceEventEmitter, 4 | NativeModules, 5 | Platform 6 | } from 'react-native'; 7 | import EventEmitter from 'events'; 8 | 9 | const { RNEventEmitter } = NativeModules; 10 | 11 | 12 | 13 | //========= INTERNAL FUNCTIONS =========// 14 | 15 | const EVENT_NAME = "ayylmao_dicksnshit_nobodyUsethisevent PLS OK THANKS"; 16 | 17 | let moduleIdCounter = 1; 18 | function getNewModuleId() { 19 | let moduleId = moduleIdCounter; 20 | moduleIdCounter++; 21 | return moduleId; 22 | } 23 | 24 | const registeredModules = {}; 25 | 26 | function sendJSEvents(moduleInfo, eventName, args) { 27 | for(const subscriber of moduleInfo.preSubscribers) { 28 | subscriber.emit(eventName, ...args); 29 | } 30 | if(moduleInfo.mainEmitter) { 31 | moduleInfo.mainEmitter.emit(eventName, ...args); 32 | } 33 | for(const subscriber of moduleInfo.subscribers) { 34 | subscriber.emit(eventName, ...args); 35 | } 36 | } 37 | 38 | // function called when an event is emitted from native code 39 | function onNativeModuleEvent(event) { 40 | var moduleInfo = registeredModules[event.moduleId]; 41 | if(moduleInfo == null) { 42 | console.error("Received event for unregistered module with id "+event.moduleId); 43 | return; 44 | } 45 | sendJSEvents(moduleInfo, event.eventName, event.args); 46 | } 47 | 48 | DeviceEventEmitter.addListener(EVENT_NAME, onNativeModuleEvent); 49 | 50 | 51 | 52 | 53 | 54 | //========= EXPORTED FUNCTIONS =========// 55 | 56 | const RNEvents = {}; 57 | module.exports = RNEvents; 58 | 59 | 60 | 61 | RNEvents.register = (nativeModule) => { 62 | if(!nativeModule.__registerAsJSEventEmitter) { 63 | throw new Error("Native module does not conform to RNEventConformer"); 64 | } 65 | else if(nativeModule.__rnEventsId != null) { 66 | throw new Error("Native module has already been registered"); 67 | } 68 | 69 | // register native module 70 | const moduleId = getNewModuleId(); 71 | nativeModule.__registerAsJSEventEmitter(moduleId); 72 | registeredModules[moduleId] = { 73 | nativeModule: nativeModule, 74 | mainEmitter: null, 75 | preSubscribers: [], 76 | subscribers: [] 77 | }; 78 | 79 | // define __rnEventsId property 80 | Object.defineProperty(nativeModule, '__rnEventsId', { 81 | value: moduleId, 82 | writable: false 83 | }); 84 | 85 | return nativeModule; 86 | } 87 | 88 | 89 | 90 | RNEvents.conform = (nativeModule) => { 91 | if(!nativeModule.__registerAsJSEventEmitter) { 92 | throw new Error("Native module does not conform to RNEventConformer"); 93 | } 94 | else if(nativeModule.__rnEventsId == null) { 95 | throw new Error("Native module has not been registered"); 96 | } 97 | const moduleId = nativeModule.__rnEventsId; 98 | const moduleInfo = registeredModules[moduleId]; 99 | if(moduleInfo == null) { 100 | throw new Error("No module info found for native module"); 101 | } 102 | else if(moduleInfo.mainEmitter) { 103 | throw new Error("Native module has already been conformed"); 104 | } 105 | 106 | // apply event emitter methods 107 | var eventEmitter = new EventEmitter(); 108 | var emitterKeys = Object.keys(EventEmitter.prototype); 109 | for(var i=0; i { 122 | // send native event 123 | RNEventEmitter.emit(moduleId, eventName, args); 124 | // send js events 125 | sendJSEvents(moduleInfo, eventName, args); 126 | }; 127 | // update module info 128 | moduleInfo.mainEmitter = eventEmitter; 129 | 130 | return nativeModule; 131 | } 132 | 133 | 134 | 135 | RNEvents.emitNativeEvent = (nativeModule, eventName, ...args) => { 136 | if(!nativeModule.__registerAsJSEventEmitter) { 137 | throw new Error("Native module does not conform to RNEventConformer"); 138 | } 139 | else if(nativeModule.__rnEventsId == null) { 140 | throw new Error("Native module has not been registered"); 141 | } 142 | const moduleId = nativeModule.__rnEventsId; 143 | const moduleInfo = registeredModules[moduleId]; 144 | if(moduleInfo == null) { 145 | throw new Error("No module info found for native module"); 146 | } 147 | 148 | // send native event 149 | RNEventEmitter.emit(moduleId, eventName, args); 150 | } 151 | 152 | 153 | 154 | RNEvents.emitJSEvent = (nativeModule, eventName, ...args) => { 155 | if(!nativeModule.__registerAsJSEventEmitter) { 156 | throw new Error("Native module does not conform to RNEventConformer"); 157 | } 158 | else if(nativeModule.__rnEventsId == null) { 159 | throw new Error("Native module has not been registered"); 160 | } 161 | const moduleId = nativeModule.__rnEventsId; 162 | const moduleInfo = registeredModules[moduleId]; 163 | if(moduleInfo == null) { 164 | throw new Error("No module info found for native module"); 165 | } 166 | 167 | // send js events 168 | sendJSEvents(moduleInfo, eventName, args); 169 | } 170 | 171 | 172 | 173 | RNEvents.addSubscriber = (nativeModule, subscriber) => { 174 | if(!nativeModule.__registerAsJSEventEmitter) { 175 | throw new Error("Native module does not conform to RNEventConformer"); 176 | } 177 | else if(nativeModule.__rnEventsId == null) { 178 | throw new Error("Native module has not been registered"); 179 | } 180 | else if(!(subscriber instanceof EventEmitter)) { 181 | throw new Error("subscriber must be an EventEmitter"); 182 | } 183 | const moduleId = nativeModule.__rnEventsId; 184 | const moduleInfo = registeredModules[moduleId]; 185 | if(moduleInfo == null) { 186 | throw new Error("No module info found for native module"); 187 | } 188 | 189 | // add subscriber 190 | moduleInfo.subscribers.push(subscriber); 191 | } 192 | 193 | 194 | 195 | RNEvents.removeSubscriber = (nativeModule, subscriber) => { 196 | if(!nativeModule.__registerAsJSEventEmitter) { 197 | throw new Error("Native module does not conform to RNEventConformer"); 198 | } 199 | else if(nativeModule.__rnEventsId == null) { 200 | throw new Error("Native module has not been registered"); 201 | } 202 | else if(!(subscriber instanceof EventEmitter)) { 203 | throw new Error("subscriber must be an EventEmitter"); 204 | } 205 | const moduleId = nativeModule.__rnEventsId; 206 | const moduleInfo = registeredModules[moduleId]; 207 | if(moduleInfo == null) { 208 | throw new Error("No module info found for native module"); 209 | } 210 | 211 | // remove subscriber 212 | const index = moduleInfo.subscribers.indexOf(subscriber); 213 | if(index != -1) { 214 | moduleInfo.subscribers.splice(index, 1); 215 | } 216 | } 217 | 218 | 219 | 220 | RNEvents.addPreSubscriber = (nativeModule, subscriber) => { 221 | if(!nativeModule.__registerAsJSEventEmitter) { 222 | throw new Error("Native module does not conform to RNEventConformer"); 223 | } 224 | else if(nativeModule.__rnEventsId == null) { 225 | throw new Error("Native module has not been registered"); 226 | } 227 | else if(!(subscriber instanceof EventEmitter)) { 228 | throw new Error("subscriber must be an EventEmitter"); 229 | } 230 | const moduleId = nativeModule.__rnEventsId; 231 | const moduleInfo = registeredModules[moduleId]; 232 | if(moduleInfo == null) { 233 | throw new Error("No module info found for native module"); 234 | } 235 | 236 | // add subscriber 237 | moduleInfo.preSubscribers.push(subscriber); 238 | } 239 | 240 | 241 | 242 | RNEvents.removePreSubscriber = (nativeModule, subscriber) => { 243 | if(!nativeModule.__registerAsJSEventEmitter) { 244 | throw new Error("Native module does not conform to RNEventConformer"); 245 | } 246 | else if(nativeModule.__rnEventsId == null) { 247 | throw new Error("Native module has not been registered"); 248 | } 249 | else if(!(subscriber instanceof EventEmitter)) { 250 | throw new Error("subscriber must be an EventEmitter"); 251 | } 252 | const moduleId = nativeModule.__rnEventsId; 253 | const moduleInfo = registeredModules[moduleId]; 254 | if(moduleInfo == null) { 255 | throw new Error("No module info found for native module"); 256 | } 257 | 258 | // remove subscriber 259 | const index = moduleInfo.preSubscribers.indexOf(subscriber); 260 | if(index != -1) { 261 | moduleInfo.preSubscribers.splice(index, 1); 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /ios/RNEventBridge.h: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | 4 | @class RCTBridge; 5 | 6 | @interface RNEventBridge : NSObject 7 | 8 | +(id)moduleForClass:(Class)classObj bridge:(RCTBridge*)bridge; 9 | +(void)sendEvent:(NSString*)event body:(id)body bridge:(RCTBridge*)bridge; 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /ios/RNEventBridge.m: -------------------------------------------------------------------------------- 1 | 2 | #import "RNEventBridge.h" 3 | 4 | #import 5 | 6 | @implementation RNEventBridge 7 | 8 | +(id)moduleForClass:(Class)classObj bridge:(RCTBridge*)bridge { 9 | return [bridge moduleForClass:classObj]; 10 | } 11 | 12 | +(void)sendEvent:(NSString*)event body:(id)body bridge:(RCTBridge*)bridge { 13 | [bridge enqueueJSCall:@"RCTDeviceEventEmitter" 14 | method:@"emit" 15 | args:body ? @[event, body] : @[event] 16 | completion:nil]; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /ios/RNEventCallback.h: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | 4 | @interface RNEventCallback : NSObject 5 | 6 | -(id)initWithBlock:(void(^)())block callOnlyOnce:(BOOL)once; 7 | 8 | @property (readonly) void(^block)(); 9 | @property (readonly, getter=isCalledOnlyOnce) BOOL calledOnlyOnce; 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /ios/RNEventCallback.m: -------------------------------------------------------------------------------- 1 | 2 | #import "RNEventCallback.h" 3 | 4 | @implementation RNEventCallback 5 | 6 | -(id)initWithBlock:(void(^)())block callOnlyOnce:(BOOL)once { 7 | if(self = [super init]) { 8 | _block = block; 9 | _calledOnlyOnce = once; 10 | } 11 | return self; 12 | } 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /ios/RNEventConformer.h: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | 4 | @protocol RNEventConformer 5 | 6 | -(void)__registerAsJSEventEmitter:(int)moduleId; 7 | 8 | @optional 9 | -(void)onNativeEvent:(NSString*)eventName params:(NSArray*)params; 10 | -(void)onJSEvent:(NSString*)eventName params:(NSArray*)params; 11 | -(void)onEvent:(NSString*)eventName params:(NSArray*)params; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ios/RNEventEmitter.h: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | 4 | #import "RNEventConformer.h" 5 | #import "RNEventBridge.h" 6 | 7 | @interface RNEventEmitter : NSObject 8 | 9 | +(RNEventEmitter*)eventEmitterForBridge:(RCTBridge*)bridge; 10 | +(void)registerEventEmitterModule:(id)module withID:(int)moduleId bridge:(RCTBridge*)bridge; 11 | +(void)emitEvent:(NSString*)event withParams:(NSArray*)params module:(id)module bridge:(RCTBridge*)bridge; 12 | 13 | -(void)registerModule:(id)module withID:(int)moduleId; 14 | -(id)registeredModuleForID:(int)moduleId; 15 | 16 | -(void)emitEvent:(NSString*)event withParams:(NSArray*)params forModule:(id)module; 17 | 18 | -(void)addListener:(void(^)(NSArray*))listener forEvent:(NSString*)event fromModule:(id)module runOnce:(BOOL)once; 19 | -(void)addListener:(void(^)(NSArray*))listener forEvent:(NSString*)event fromModule:(id)module; 20 | -(void)addListener:(void(^)(NSArray*))listener forEvent:(NSString*)event fromModuleWithID:(int)moduleId runOnce:(BOOL)once; 21 | -(void)addListener:(void(^)(NSArray*))listener forEvent:(NSString*)event fromModuleWithID:(int)moduleId; 22 | 23 | -(void)prependListener:(void(^)(NSArray*))listener forEvent:(NSString*)event fromModule:(id)module runOnce:(BOOL)once; 24 | -(void)prependListener:(void(^)(NSArray*))listener forEvent:(NSString*)event fromModule:(id)module; 25 | -(void)prependListener:(void(^)(NSArray*))listener forEvent:(NSString*)event fromModuleWithID:(int) moduleId runOnce:(BOOL)once; 26 | -(void)prependListener:(void(^)(NSArray*))listener forEvent:(NSString*)event fromModuleWithID:(int)moduleId; 27 | 28 | -(void)removeListener:(void(^)(NSArray*))listener forEvent:(NSString*)event fromModule:(id)module; 29 | -(void)removeListener:(void(^)(NSArray*))listener forEvent:(NSString*)event fromModuleWithID:(int)moduleId; 30 | 31 | -(void)removeAllListenersFromModule:(id)module forEvent:(NSString*)event; 32 | -(void)removeAllListenersFromModuleWithID:(int)moduleId forEvent:(NSString*)event; 33 | -(void)removeAllListenersFromModule:(id)module; 34 | -(void)removeAllListenersFromModuleWithID:(int)moduleId; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /ios/RNEventEmitter.m: -------------------------------------------------------------------------------- 1 | 2 | #import "RNEventEmitter.h" 3 | #import "RNEventConformer.h" 4 | #import "RNModuleEvents.h" 5 | #import "RNEventBridge.h" 6 | 7 | @interface RNEventEmitter() { 8 | NSMutableDictionary>* _registeredModules; 9 | NSMutableDictionary* _modules; 10 | } 11 | -(RNModuleEvents*)moduleEventsForID:(int)moduleId; 12 | -(NSNumber*)registeredModuleIDForModule:(id)module; 13 | @end 14 | 15 | 16 | @implementation RNEventEmitter 17 | 18 | @synthesize bridge = _bridge; 19 | 20 | static NSString* EVENT_NAME = @"ayylmao_dicksnshit_nobodyUsethisevent PLS OK THANKS"; 21 | 22 | RCT_EXPORT_MODULE() 23 | 24 | -(id)init { 25 | if(self = [super init]) { 26 | _registeredModules = [NSMutableDictionary dictionary]; 27 | _modules = [NSMutableDictionary dictionary]; 28 | } 29 | return self; 30 | } 31 | 32 | +(BOOL)requiresMainQueueSetup { 33 | return NO; 34 | } 35 | 36 | +(RNEventEmitter*)eventEmitterForBridge:(RCTBridge*)bridge { 37 | return [RNEventBridge moduleForClass:[RNEventEmitter class] bridge:bridge]; 38 | } 39 | 40 | 41 | 42 | +(void)registerEventEmitterModule:(id)module withID:(int)moduleId bridge:(RCTBridge*)bridge { 43 | RNEventEmitter* eventEmitter = [self eventEmitterForBridge:bridge]; 44 | if(eventEmitter == nil) { 45 | NSLog(@"Error: No RNEventEmitter is available to register to"); 46 | return; 47 | } 48 | [eventEmitter registerModule:module withID:moduleId]; 49 | } 50 | 51 | -(void)registerModule:(id)module withID:(int)moduleId { 52 | @synchronized (_registeredModules) { 53 | if([[_registeredModules allValues] indexOfObjectIdenticalTo:module] != NSNotFound) { 54 | @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Module has already been registered" userInfo:nil]; 55 | } 56 | if(_registeredModules[@(moduleId)] != nil) { 57 | @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"moduleId has already been registered to a module" userInfo:nil]; 58 | } 59 | _registeredModules[@(moduleId)] = module; 60 | } 61 | } 62 | 63 | -(RNModuleEvents*)moduleEventsForID:(int)moduleId { 64 | @synchronized (_modules) { 65 | RNModuleEvents* moduleEvents = _modules[@(moduleId)]; 66 | if (moduleEvents == nil) { 67 | moduleEvents = [[RNModuleEvents alloc] init]; 68 | _modules[@(moduleId)] = moduleEvents; 69 | } 70 | return moduleEvents; 71 | } 72 | } 73 | 74 | -(id)registeredModuleForID:(int)moduleId { 75 | @synchronized (_registeredModules) { 76 | return _registeredModules[@(moduleId)]; 77 | } 78 | } 79 | 80 | -(NSNumber*)registeredModuleIDForModule:(id)module { 81 | @synchronized (_registeredModules) { 82 | for (NSNumber* moduleId in [_registeredModules allKeys]) { 83 | id cmpModule = _registeredModules[moduleId]; 84 | if(cmpModule == module) { 85 | return moduleId; 86 | } 87 | } 88 | return nil; 89 | } 90 | } 91 | 92 | 93 | 94 | +(void)emitEvent:(NSString*)event withParams:(NSArray*)params module:(id)module bridge:(RCTBridge*)bridge { 95 | RNEventEmitter* eventEmitter = [self eventEmitterForBridge:bridge]; 96 | if(eventEmitter == nil) { 97 | NSLog(@"Error: No RNEventEmitterModule is available to emit %@ event", event); 98 | return; 99 | } 100 | [eventEmitter emitEvent:event withParams:params forModule:module]; 101 | } 102 | 103 | -(void)emitEvent:(NSString*)event withParams:(NSArray*)params forModule:(id)module { 104 | NSNumber* moduleId = [self registeredModuleIDForModule:module]; 105 | if(moduleId == nil) { 106 | NSLog(@"Error: Cannot emit %@ event before %@ module has been registered", event, module); 107 | return; 108 | } 109 | [[self moduleEventsForID:moduleId.intValue] emitEvent:event withParams:params]; 110 | 111 | NSMutableDictionary* jsEvent = [NSMutableDictionary dictionary]; 112 | jsEvent[@"moduleId"] = moduleId; 113 | jsEvent[@"eventName"] = event; 114 | jsEvent[@"args"] = params; 115 | 116 | [RNEventBridge sendEvent:EVENT_NAME body:jsEvent bridge:_bridge]; 117 | 118 | if([module respondsToSelector:@selector(onEvent:params:)]) { 119 | [module onEvent:event params:params]; 120 | } 121 | if([module respondsToSelector:@selector(onNativeEvent:params:)]) { 122 | [module onNativeEvent:event params:params]; 123 | } 124 | } 125 | 126 | RCT_EXPORT_METHOD(emit:(int)moduleId event:(NSString*)event params:(NSArray*)params) { 127 | id module = [self registeredModuleForID:moduleId]; 128 | if(module == nil) { 129 | @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"No module registered with that ID" userInfo:nil]; 130 | } 131 | 132 | [[self moduleEventsForID:moduleId] emitEvent:event withParams:params]; 133 | 134 | if([module respondsToSelector:@selector(onEvent:params:)]) { 135 | [module onEvent:event params:params]; 136 | } 137 | if([module respondsToSelector:@selector(onJSEvent:params:)]) { 138 | [module onJSEvent:event params:params]; 139 | } 140 | } 141 | 142 | 143 | 144 | -(void)addListener:(void(^)(NSArray*))listener forEvent:(NSString*)event fromModule:(id)module runOnce:(BOOL)once { 145 | NSNumber* moduleId = [self registeredModuleIDForModule:module]; 146 | if(moduleId == nil) { 147 | @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Module has not been registered" userInfo:nil]; 148 | } 149 | [self addListener:listener forEvent:event fromModuleWithID:moduleId.intValue runOnce:once]; 150 | } 151 | 152 | -(void)addListener:(void(^)(NSArray*))listener forEvent:(NSString*)event fromModule:(id)module { 153 | [self addListener:listener forEvent:event fromModule:module runOnce:NO]; 154 | } 155 | 156 | -(void)addListener:(void(^)(NSArray*))listener forEvent:(NSString*)event fromModuleWithID:(int) moduleId runOnce:(BOOL)once { 157 | [[self moduleEventsForID:moduleId] addListener:listener forEvent:event onlyOnce:once]; 158 | } 159 | 160 | -(void)addListener:(void(^)(NSArray*))listener forEvent:(NSString*)event fromModuleWithID:(int)moduleId { 161 | [self addListener:listener forEvent:event fromModuleWithID:moduleId runOnce:NO]; 162 | } 163 | 164 | 165 | 166 | -(void)prependListener:(void(^)(NSArray*))listener forEvent:(NSString*)event fromModule:(id)module runOnce:(BOOL)once { 167 | NSNumber* moduleId = [self registeredModuleIDForModule:module]; 168 | if(moduleId == nil) { 169 | @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Module has not been registered" userInfo:nil]; 170 | } 171 | [self prependListener:listener forEvent:event fromModuleWithID:moduleId.intValue runOnce:once]; 172 | } 173 | 174 | -(void)prependListener:(void(^)(NSArray*))listener forEvent:(NSString*)event fromModule:(id)module { 175 | [self prependListener:listener forEvent:event fromModule:module runOnce:NO]; 176 | } 177 | 178 | -(void)prependListener:(void(^)(NSArray*))listener forEvent:(NSString*)event fromModuleWithID:(int) moduleId runOnce:(BOOL)once { 179 | [[self moduleEventsForID:moduleId] addListener:listener forEvent:event onlyOnce:once]; 180 | } 181 | 182 | -(void)prependListener:(void(^)(NSArray*))listener forEvent:(NSString*)event fromModuleWithID:(int)moduleId { 183 | [self addListener:listener forEvent:event fromModuleWithID:moduleId runOnce:NO]; 184 | } 185 | 186 | 187 | 188 | -(void)removeListener:(void(^)(NSArray*))listener forEvent:(NSString*)event fromModule:(id)module { 189 | NSNumber* moduleId = [self registeredModuleIDForModule:module]; 190 | if(moduleId == nil) 191 | { 192 | @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Module has not been registered" userInfo:nil]; 193 | } 194 | [self removeListener:listener forEvent:event fromModuleWithID:moduleId.intValue]; 195 | } 196 | 197 | -(void)removeListener:(void(^)(NSArray*))listener forEvent:(NSString*)event fromModuleWithID:(int)moduleId { 198 | [[self moduleEventsForID:moduleId] removeListener:listener forEvent:event]; 199 | } 200 | 201 | 202 | 203 | -(void)removeAllListenersFromModule:(id)module forEvent:(NSString*)event { 204 | NSNumber* moduleId = [self registeredModuleIDForModule:module]; 205 | if(moduleId == nil) 206 | { 207 | @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Module has not been registered" userInfo:nil]; 208 | } 209 | [self removeAllListenersFromModuleWithID:moduleId.intValue forEvent:event]; 210 | } 211 | 212 | -(void)removeAllListenersFromModuleWithID:(int)moduleId forEvent:(NSString*)event { 213 | [[self moduleEventsForID:moduleId] removeAllListenersForEvent:event]; 214 | } 215 | 216 | -(void)removeAllListenersFromModule:(id)module { 217 | NSNumber* moduleId = [self registeredModuleIDForModule:module]; 218 | if(moduleId == nil) 219 | { 220 | @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Module has not been registered" userInfo:nil]; 221 | } 222 | [self removeAllListenersFromModuleWithID:moduleId.intValue]; 223 | } 224 | 225 | -(void)removeAllListenersFromModuleWithID:(int)moduleId { 226 | [[self moduleEventsForID:moduleId] removeAllListeners]; 227 | } 228 | 229 | @end 230 | -------------------------------------------------------------------------------- /ios/RNEventEmitter.podspec: -------------------------------------------------------------------------------- 1 | 2 | Pod::Spec.new do |s| 3 | s.name = "RNEventEmitter" 4 | s.version = "1.0.0" 5 | s.summary = "RNEventEmitter" 6 | s.description = <<-DESC 7 | RNEventEmitter 8 | DESC 9 | s.homepage = "" 10 | s.license = "MIT" 11 | # s.license = { :type => "MIT", :file => "FILE_LICENSE" } 12 | s.author = { "author" => "author@domain.cn" } 13 | s.platform = :ios, "7.0" 14 | s.source = { :git => "https://github.com/author/RNEventEmitter.git", :tag => "master" } 15 | s.source_files = "RNEventEmitter/**/*.{h,m}" 16 | s.requires_arc = true 17 | 18 | 19 | s.dependency "React" 20 | #s.dependency "others" 21 | 22 | end 23 | 24 | -------------------------------------------------------------------------------- /ios/RNEventEmitter.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | A038EA6D2010585800132979 /* RNEventCallback.m in Sources */ = {isa = PBXBuildFile; fileRef = A038EA6C2010585800132979 /* RNEventCallback.m */; }; 11 | A038EA7020105AF200132979 /* RNModuleEvents.m in Sources */ = {isa = PBXBuildFile; fileRef = A038EA6F20105AF200132979 /* RNModuleEvents.m */; }; 12 | A0538A452011069200D185A8 /* RNEventCallback.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = A038EA6B2010585800132979 /* RNEventCallback.h */; }; 13 | A0538A462011069200D185A8 /* RNEventConformer.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = A038EA6A2010572400132979 /* RNEventConformer.h */; }; 14 | A0538A472011069200D185A8 /* RNEventEmitter.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = B3E7B5881CC2AC0600A0062D /* RNEventEmitter.h */; }; 15 | A0538A482011069200D185A8 /* RNModuleEvents.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = A038EA6E20105AF200132979 /* RNModuleEvents.h */; }; 16 | A0538A4B2011094600D185A8 /* RNEventBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = A0538A4A2011094600D185A8 /* RNEventBridge.m */; }; 17 | A553AEEA223B075B004C373D /* RNEventBridge.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = A0538A492011094600D185A8 /* RNEventBridge.h */; }; 18 | B3E7B58A1CC2AC0600A0062D /* RNEventEmitter.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNEventEmitter.m */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXCopyFilesBuildPhase section */ 22 | 58B511D91A9E6C8500147676 /* Copy Headers */ = { 23 | isa = PBXCopyFilesBuildPhase; 24 | buildActionMask = 2147483647; 25 | dstPath = include/RNEventEmitter; 26 | dstSubfolderSpec = 16; 27 | files = ( 28 | A553AEEA223B075B004C373D /* RNEventBridge.h in Copy Headers */, 29 | A0538A452011069200D185A8 /* RNEventCallback.h in Copy Headers */, 30 | A0538A462011069200D185A8 /* RNEventConformer.h in Copy Headers */, 31 | A0538A472011069200D185A8 /* RNEventEmitter.h in Copy Headers */, 32 | A0538A482011069200D185A8 /* RNModuleEvents.h in Copy Headers */, 33 | ); 34 | name = "Copy Headers"; 35 | runOnlyForDeploymentPostprocessing = 0; 36 | }; 37 | /* End PBXCopyFilesBuildPhase section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 134814201AA4EA6300B7C361 /* libRNEventEmitter.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNEventEmitter.a; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | A038EA6A2010572400132979 /* RNEventConformer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNEventConformer.h; sourceTree = ""; }; 42 | A038EA6B2010585800132979 /* RNEventCallback.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNEventCallback.h; sourceTree = ""; }; 43 | A038EA6C2010585800132979 /* RNEventCallback.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNEventCallback.m; sourceTree = ""; }; 44 | A038EA6E20105AF200132979 /* RNModuleEvents.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNModuleEvents.h; sourceTree = ""; }; 45 | A038EA6F20105AF200132979 /* RNModuleEvents.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNModuleEvents.m; sourceTree = ""; }; 46 | A0538A492011094600D185A8 /* RNEventBridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNEventBridge.h; sourceTree = ""; }; 47 | A0538A4A2011094600D185A8 /* RNEventBridge.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNEventBridge.m; sourceTree = ""; }; 48 | B3E7B5881CC2AC0600A0062D /* RNEventEmitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNEventEmitter.h; sourceTree = ""; }; 49 | B3E7B5891CC2AC0600A0062D /* RNEventEmitter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNEventEmitter.m; sourceTree = ""; }; 50 | /* End PBXFileReference section */ 51 | 52 | /* Begin PBXFrameworksBuildPhase section */ 53 | 58B511D81A9E6C8500147676 /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | ); 58 | runOnlyForDeploymentPostprocessing = 0; 59 | }; 60 | /* End PBXFrameworksBuildPhase section */ 61 | 62 | /* Begin PBXGroup section */ 63 | 134814211AA4EA7D00B7C361 /* Products */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 134814201AA4EA6300B7C361 /* libRNEventEmitter.a */, 67 | ); 68 | name = Products; 69 | sourceTree = ""; 70 | }; 71 | 58B511D21A9E6C8500147676 = { 72 | isa = PBXGroup; 73 | children = ( 74 | A0538A492011094600D185A8 /* RNEventBridge.h */, 75 | A0538A4A2011094600D185A8 /* RNEventBridge.m */, 76 | A038EA6B2010585800132979 /* RNEventCallback.h */, 77 | A038EA6C2010585800132979 /* RNEventCallback.m */, 78 | A038EA6A2010572400132979 /* RNEventConformer.h */, 79 | B3E7B5881CC2AC0600A0062D /* RNEventEmitter.h */, 80 | B3E7B5891CC2AC0600A0062D /* RNEventEmitter.m */, 81 | A038EA6E20105AF200132979 /* RNModuleEvents.h */, 82 | A038EA6F20105AF200132979 /* RNModuleEvents.m */, 83 | 134814211AA4EA7D00B7C361 /* Products */, 84 | ); 85 | sourceTree = ""; 86 | }; 87 | /* End PBXGroup section */ 88 | 89 | /* Begin PBXNativeTarget section */ 90 | 58B511DA1A9E6C8500147676 /* RNEventEmitter */ = { 91 | isa = PBXNativeTarget; 92 | buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNEventEmitter" */; 93 | buildPhases = ( 94 | 58B511D71A9E6C8500147676 /* Sources */, 95 | 58B511D81A9E6C8500147676 /* Frameworks */, 96 | 58B511D91A9E6C8500147676 /* Copy Headers */, 97 | ); 98 | buildRules = ( 99 | ); 100 | dependencies = ( 101 | ); 102 | name = RNEventEmitter; 103 | productName = RCTDataManager; 104 | productReference = 134814201AA4EA6300B7C361 /* libRNEventEmitter.a */; 105 | productType = "com.apple.product-type.library.static"; 106 | }; 107 | /* End PBXNativeTarget section */ 108 | 109 | /* Begin PBXProject section */ 110 | 58B511D31A9E6C8500147676 /* Project object */ = { 111 | isa = PBXProject; 112 | attributes = { 113 | LastUpgradeCheck = 0830; 114 | ORGANIZATIONNAME = Facebook; 115 | TargetAttributes = { 116 | 58B511DA1A9E6C8500147676 = { 117 | CreatedOnToolsVersion = 6.1.1; 118 | }; 119 | }; 120 | }; 121 | buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNEventEmitter" */; 122 | compatibilityVersion = "Xcode 3.2"; 123 | developmentRegion = English; 124 | hasScannedForEncodings = 0; 125 | knownRegions = ( 126 | en, 127 | ); 128 | mainGroup = 58B511D21A9E6C8500147676; 129 | productRefGroup = 58B511D21A9E6C8500147676; 130 | projectDirPath = ""; 131 | projectRoot = ""; 132 | targets = ( 133 | 58B511DA1A9E6C8500147676 /* RNEventEmitter */, 134 | ); 135 | }; 136 | /* End PBXProject section */ 137 | 138 | /* Begin PBXSourcesBuildPhase section */ 139 | 58B511D71A9E6C8500147676 /* Sources */ = { 140 | isa = PBXSourcesBuildPhase; 141 | buildActionMask = 2147483647; 142 | files = ( 143 | B3E7B58A1CC2AC0600A0062D /* RNEventEmitter.m in Sources */, 144 | A038EA6D2010585800132979 /* RNEventCallback.m in Sources */, 145 | A0538A4B2011094600D185A8 /* RNEventBridge.m in Sources */, 146 | A038EA7020105AF200132979 /* RNModuleEvents.m in Sources */, 147 | ); 148 | runOnlyForDeploymentPostprocessing = 0; 149 | }; 150 | /* End PBXSourcesBuildPhase section */ 151 | 152 | /* Begin XCBuildConfiguration section */ 153 | 58B511ED1A9E6C8500147676 /* Debug */ = { 154 | isa = XCBuildConfiguration; 155 | buildSettings = { 156 | ALWAYS_SEARCH_USER_PATHS = NO; 157 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 158 | CLANG_CXX_LIBRARY = "libc++"; 159 | CLANG_ENABLE_MODULES = YES; 160 | CLANG_ENABLE_OBJC_ARC = YES; 161 | CLANG_WARN_BOOL_CONVERSION = YES; 162 | CLANG_WARN_CONSTANT_CONVERSION = YES; 163 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 164 | CLANG_WARN_EMPTY_BODY = YES; 165 | CLANG_WARN_ENUM_CONVERSION = YES; 166 | CLANG_WARN_INFINITE_RECURSION = YES; 167 | CLANG_WARN_INT_CONVERSION = YES; 168 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 169 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 170 | CLANG_WARN_UNREACHABLE_CODE = YES; 171 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 172 | COPY_PHASE_STRIP = NO; 173 | ENABLE_STRICT_OBJC_MSGSEND = YES; 174 | ENABLE_TESTABILITY = YES; 175 | GCC_C_LANGUAGE_STANDARD = gnu99; 176 | GCC_DYNAMIC_NO_PIC = NO; 177 | GCC_NO_COMMON_BLOCKS = YES; 178 | GCC_OPTIMIZATION_LEVEL = 0; 179 | GCC_PREPROCESSOR_DEFINITIONS = ( 180 | "DEBUG=1", 181 | "$(inherited)", 182 | ); 183 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 184 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 185 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 186 | GCC_WARN_UNDECLARED_SELECTOR = YES; 187 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 188 | GCC_WARN_UNUSED_FUNCTION = YES; 189 | GCC_WARN_UNUSED_VARIABLE = YES; 190 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 191 | MTL_ENABLE_DEBUG_INFO = YES; 192 | ONLY_ACTIVE_ARCH = YES; 193 | SDKROOT = iphoneos; 194 | }; 195 | name = Debug; 196 | }; 197 | 58B511EE1A9E6C8500147676 /* Release */ = { 198 | isa = XCBuildConfiguration; 199 | buildSettings = { 200 | ALWAYS_SEARCH_USER_PATHS = NO; 201 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 202 | CLANG_CXX_LIBRARY = "libc++"; 203 | CLANG_ENABLE_MODULES = YES; 204 | CLANG_ENABLE_OBJC_ARC = YES; 205 | CLANG_WARN_BOOL_CONVERSION = YES; 206 | CLANG_WARN_CONSTANT_CONVERSION = YES; 207 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 208 | CLANG_WARN_EMPTY_BODY = YES; 209 | CLANG_WARN_ENUM_CONVERSION = YES; 210 | CLANG_WARN_INFINITE_RECURSION = YES; 211 | CLANG_WARN_INT_CONVERSION = YES; 212 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 213 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 214 | CLANG_WARN_UNREACHABLE_CODE = YES; 215 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 216 | COPY_PHASE_STRIP = YES; 217 | ENABLE_NS_ASSERTIONS = NO; 218 | ENABLE_STRICT_OBJC_MSGSEND = YES; 219 | GCC_C_LANGUAGE_STANDARD = gnu99; 220 | GCC_NO_COMMON_BLOCKS = YES; 221 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 222 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 223 | GCC_WARN_UNDECLARED_SELECTOR = YES; 224 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 225 | GCC_WARN_UNUSED_FUNCTION = YES; 226 | GCC_WARN_UNUSED_VARIABLE = YES; 227 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 228 | MTL_ENABLE_DEBUG_INFO = NO; 229 | SDKROOT = iphoneos; 230 | VALIDATE_PRODUCT = YES; 231 | }; 232 | name = Release; 233 | }; 234 | 58B511F01A9E6C8500147676 /* Debug */ = { 235 | isa = XCBuildConfiguration; 236 | buildSettings = { 237 | HEADER_SEARCH_PATHS = ( 238 | "$(inherited)", 239 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 240 | "$(SRCROOT)/../../../React/**", 241 | "$(SRCROOT)/../../react-native/React/**", 242 | ); 243 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 244 | OTHER_LDFLAGS = "-ObjC"; 245 | PRODUCT_NAME = RNEventEmitter; 246 | SKIP_INSTALL = YES; 247 | }; 248 | name = Debug; 249 | }; 250 | 58B511F11A9E6C8500147676 /* Release */ = { 251 | isa = XCBuildConfiguration; 252 | buildSettings = { 253 | HEADER_SEARCH_PATHS = ( 254 | "$(inherited)", 255 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 256 | "$(SRCROOT)/../../../React/**", 257 | "$(SRCROOT)/../../react-native/React/**", 258 | ); 259 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 260 | OTHER_LDFLAGS = "-ObjC"; 261 | PRODUCT_NAME = RNEventEmitter; 262 | SKIP_INSTALL = YES; 263 | }; 264 | name = Release; 265 | }; 266 | /* End XCBuildConfiguration section */ 267 | 268 | /* Begin XCConfigurationList section */ 269 | 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNEventEmitter" */ = { 270 | isa = XCConfigurationList; 271 | buildConfigurations = ( 272 | 58B511ED1A9E6C8500147676 /* Debug */, 273 | 58B511EE1A9E6C8500147676 /* Release */, 274 | ); 275 | defaultConfigurationIsVisible = 0; 276 | defaultConfigurationName = Release; 277 | }; 278 | 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNEventEmitter" */ = { 279 | isa = XCConfigurationList; 280 | buildConfigurations = ( 281 | 58B511F01A9E6C8500147676 /* Debug */, 282 | 58B511F11A9E6C8500147676 /* Release */, 283 | ); 284 | defaultConfigurationIsVisible = 0; 285 | defaultConfigurationName = Release; 286 | }; 287 | /* End XCConfigurationList section */ 288 | }; 289 | rootObject = 58B511D31A9E6C8500147676 /* Project object */; 290 | } 291 | -------------------------------------------------------------------------------- /ios/RNEventEmitter.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/RNModuleEvents.h: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | 4 | @interface RNModuleEvents : NSObject 5 | 6 | -(void)addListener:(void(^)(NSArray*))listener forEvent:(NSString*)event onlyOnce:(BOOL)once; 7 | -(void)prependListener:(void(^)(NSArray*))listener forEvent:(NSString*)event onlyOnce:(BOOL)once; 8 | -(void)removeListener:(void(^)(NSArray*))listener forEvent:(NSString*)event; 9 | -(void)removeAllListenersForEvent:(NSString*)event; 10 | -(void)removeAllListeners; 11 | 12 | -(NSUInteger)listenerCountForEvent:(NSString*)event; 13 | 14 | -(BOOL)emitEvent:(NSString*)event withParams:(NSArray*)params; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /ios/RNModuleEvents.m: -------------------------------------------------------------------------------- 1 | 2 | #import "RNModuleEvents.h" 3 | #import "RNEventCallback.h" 4 | 5 | @interface RNModuleEvents() { 6 | NSMutableDictionary*>* _eventListeners; 7 | } 8 | @end 9 | 10 | 11 | @implementation RNModuleEvents 12 | 13 | -(id)init { 14 | if(self = [super init]) { 15 | _eventListeners = [NSMutableDictionary dictionary]; 16 | } 17 | return self; 18 | } 19 | 20 | -(void)addListener:(void(^)(NSArray*))listener forEvent:(NSString*)event onlyOnce:(BOOL)once { 21 | @synchronized (_eventListeners) { 22 | NSMutableArray* listeners = _eventListeners[event]; 23 | if (listeners == nil) { 24 | listeners = [[NSMutableArray alloc] init]; 25 | _eventListeners[event] = listeners; 26 | } 27 | [listeners addObject:[[RNEventCallback alloc] initWithBlock:listener callOnlyOnce:once]]; 28 | } 29 | } 30 | 31 | -(void)prependListener:(void(^)(NSArray*))listener forEvent:(NSString*)event onlyOnce:(BOOL)once { 32 | @synchronized (_eventListeners) { 33 | NSMutableArray* listeners = _eventListeners[event]; 34 | if (listeners == nil) { 35 | listeners = [[NSMutableArray alloc] init]; 36 | _eventListeners[event] = listeners; 37 | } 38 | [listeners insertObject:[[RNEventCallback alloc] initWithBlock:listener callOnlyOnce:once] atIndex:0]; 39 | } 40 | } 41 | 42 | -(void)removeListener:(void(^)(NSArray*))listener forEvent:(NSString*)event { 43 | @synchronized (_eventListeners) { 44 | NSMutableArray* listeners = _eventListeners[event]; 45 | for (NSUInteger i=0; i* listeners = _eventListeners[event]; 75 | if (listeners == nil) { 76 | return 0; 77 | } 78 | return listeners.count; 79 | } 80 | } 81 | 82 | -(BOOL)emitEvent:(NSString*)event withParams:(NSArray*)params { 83 | NSArray* tmpListeners = nil; 84 | 85 | @synchronized (_eventListeners) { 86 | NSMutableArray* listeners = _eventListeners[event]; 87 | if(listeners != nil) { 88 | tmpListeners = [NSMutableArray arrayWithArray:listeners]; 89 | } 90 | 91 | // remove "once" event listeners 92 | if(listeners != nil) { 93 | for (NSUInteger i=0; i 0) { 104 | //invoke events 105 | for(NSUInteger i=0; i