// Copyright 2018-present 650 Industries. All rights reserved. #import #import #import #import @interface UMReactNativeEventEmitter () @property (nonatomic, assign) int listenersCount; @property (nonatomic, weak) UMModuleRegistry *umModuleRegistry; @property (nonatomic, strong) NSMutableDictionary *modulesListenersCounts; @end @implementation UMReactNativeEventEmitter - (instancetype)init { if (self = [super init]) { _listenersCount = 0; _modulesListenersCounts = [NSMutableDictionary dictionary]; } return self; } UM_REGISTER_MODULE(); + (NSString *)moduleName { return @"UMReactNativeEventEmitter"; } + (const NSArray *)exportedInterfaces { return @[@protocol(UMEventEmitterService)]; } - (NSArray *)supportedEvents { NSMutableSet *eventsAccumulator = [NSMutableSet set]; for (UMExportedModule *exportedModule in [_umModuleRegistry getAllExportedModules]) { if ([exportedModule conformsToProtocol:@protocol(UMEventEmitter)]) { id eventEmitter = (id)exportedModule; [eventsAccumulator addObjectsFromArray:[eventEmitter supportedEvents]]; } } return [eventsAccumulator allObjects]; } RCT_EXPORT_METHOD(addProxiedListener:(NSString *)moduleName eventName:(NSString *)eventName) { [self addListener:eventName]; // Validate module UMExportedModule *module = [_umModuleRegistry getExportedModuleForName:moduleName]; if (RCT_DEBUG && module == nil) { UMLogError(@"Module for name `%@` has not been found.", moduleName); return; } else if (RCT_DEBUG && ![module conformsToProtocol:@protocol(UMEventEmitter)]) { UMLogError(@"Module `%@` is not an UMEventEmitter, thus it cannot be subscribed to.", moduleName); return; } // Validate eventEmitter id eventEmitter = (id)module; if (RCT_DEBUG && ![[eventEmitter supportedEvents] containsObject:eventName]) { UMLogError(@"`%@` is not a supported event type for %@. Supported events are: `%@`", eventName, moduleName, [[eventEmitter supportedEvents] componentsJoinedByString:@"`, `"]); } // Global observing state _listenersCount += 1; if (_listenersCount == 1) { [self startObserving]; } // Per-module observing state int newModuleListenersCount = [self moduleListenersCountFor:moduleName] + 1; if (newModuleListenersCount == 1) { [eventEmitter startObserving]; } _modulesListenersCounts[moduleName] = [NSNumber numberWithInt:newModuleListenersCount]; } RCT_EXPORT_METHOD(removeProxiedListeners:(NSString *)moduleName count:(double)count) { [self removeListeners:count]; // Validate module UMExportedModule *module = [_umModuleRegistry getExportedModuleForName:moduleName]; if (RCT_DEBUG && module == nil) { UMLogError(@"Module for name `%@` has not been found.", moduleName); return; } else if (RCT_DEBUG && ![module conformsToProtocol:@protocol(UMEventEmitter)]) { UMLogError(@"Module `%@` is not an UMEventEmitter, thus it cannot be subscribed to.", moduleName); return; } id eventEmitter = (id)module; // Per-module observing state int newModuleListenersCount = [self moduleListenersCountFor:moduleName] - count; if (newModuleListenersCount == 0) { [eventEmitter stopObserving]; } else if (newModuleListenersCount < 0) { UMLogError(@"Attempted to remove more `%@` listeners than added", moduleName); newModuleListenersCount = 0; } _modulesListenersCounts[moduleName] = [NSNumber numberWithInt:newModuleListenersCount]; // Global observing state if (_listenersCount - count < 0) { UMLogError(@"Attempted to remove more proxied event emitter listeners than added"); _listenersCount = 0; } else { _listenersCount -= count; } if (_listenersCount == 0) { [self stopObserving]; } } # pragma mark Utilities - (int)moduleListenersCountFor:(NSString *)moduleName { NSNumber *moduleListenersCountNumber = _modulesListenersCounts[moduleName]; int moduleListenersCount = 0; if (moduleListenersCountNumber != nil) { moduleListenersCount = [moduleListenersCountNumber intValue]; } return moduleListenersCount; } # pragma mark - UMModuleRegistryConsumer - (void)setModuleRegistry:(UMModuleRegistry *)moduleRegistry { _umModuleRegistry = moduleRegistry; } @end