diff --git a/React/Base/RCTEventDispatcher.h b/React/Base/RCTEventDispatcher.h index 840e6b7020ff87..0243c7e512dfd4 100644 --- a/React/Base/RCTEventDispatcher.h +++ b/React/Base/RCTEventDispatcher.h @@ -35,6 +35,7 @@ RCT_EXTERN NSString *RCTNormalizeInputEventName(NSString *eventName); @protocol RCTEvent @required +@property (nonatomic, strong, readonly) NSNumber *viewTag; @property (nonatomic, copy, readonly) NSString *eventName; - (BOOL)canCoalesce; @@ -47,12 +48,6 @@ RCT_EXTERN NSString *RCTNormalizeInputEventName(NSString *eventName); @optional -/** - * Can be implemented for view based events that need to be coalesced - * by it's viewTag. - */ -@property (nonatomic, strong, readonly) NSNumber *viewTag; - /** * Coalescing related methods must only be implemented if canCoalesce * returns YES. diff --git a/React/Base/RCTEventDispatcher.m b/React/Base/RCTEventDispatcher.m index d6c396d815c792..a3229f37797139 100644 --- a/React/Base/RCTEventDispatcher.m +++ b/React/Base/RCTEventDispatcher.m @@ -27,15 +27,17 @@ return eventName; } -static NSNumber *RCTGetEventID(id event) +static NSNumber *RCTGetEventID(NSNumber *viewTag, NSString *eventName, uint16_t coalescingKey) { return @( - ([event respondsToSelector:@selector(viewTag)] ? event.viewTag.intValue : 0) | - (((uint64_t)event.eventName.hash & 0xFFFF) << 32) | - (((uint64_t)event.coalescingKey) << 48) + viewTag.intValue | + (((uint64_t)eventName.hash & 0xFFFF) << 32) | + (((uint64_t)coalescingKey) << 48) ); } +static uint16_t RCTUniqueCoalescingKeyGenerator = 0; + @implementation RCTEventDispatcher { // We need this lock to protect access to _events, _eventQueue and _eventsDispatchScheduled. It's filled in on main thread and consumed on js thread. @@ -136,38 +138,40 @@ - (void)sendEvent:(id)event [_observersLock unlock]; - if (event.canCoalesce) { - [_eventQueueLock lock]; - - NSNumber *eventID = RCTGetEventID(event); + [_eventQueueLock lock]; + NSNumber *eventID; + if (event.canCoalesce) { + eventID = RCTGetEventID(event.viewTag, event.eventName, event.coalescingKey); id previousEvent = _events[eventID]; if (previousEvent) { event = [previousEvent coalesceWithEvent:event]; } else { [_eventQueue addObject:eventID]; } - _events[eventID] = event; + } else { + id previousEvent = _events[eventID]; + eventID = RCTGetEventID(event.viewTag, event.eventName, RCTUniqueCoalescingKeyGenerator++); + RCTAssert(previousEvent == nil, @"Got event %@ which cannot be coalesced, but has the same eventID %@ as the previous event %@", event, eventID, previousEvent); + [_eventQueue addObject:eventID]; + } - BOOL scheduleEventsDispatch = NO; - if (!_eventsDispatchScheduled) { - _eventsDispatchScheduled = YES; - scheduleEventsDispatch = YES; - } + _events[eventID] = event; - // We have to release the lock before dispatching block with events, - // since dispatchBlock: can be executed synchronously on the same queue. - // (This is happening when chrome debugging is turned on.) - [_eventQueueLock unlock]; + BOOL scheduleEventsDispatch = NO; + if (!_eventsDispatchScheduled) { + _eventsDispatchScheduled = YES; + scheduleEventsDispatch = YES; + } - if (scheduleEventsDispatch) { - [_bridge dispatchBlock:^{ - [self flushEventsQueue]; - } queue:RCTJSThread]; - } - } else { + // We have to release the lock before dispatching block with events, + // since dispatchBlock: can be executed synchronously on the same queue. + // (This is happening when chrome debugging is turned on.) + [_eventQueueLock unlock]; + + if (scheduleEventsDispatch) { [_bridge dispatchBlock:^{ - [self dispatchEvent:event]; + [self flushEventsQueue]; } queue:RCTJSThread]; } }