react-native/React/Base/RCTEventDispatcher.m
Tadeu Zagallo 777363fdd7 [ReactNative] Use NSDictionary + NSNumber for event coalescing
Summary:
@public

On D2099270 event coalescing was implemented and the event key on the RCTSparseArray
is an uint64_t, but it was declared as NSUInteger.  On a 32 bits architecture
it'll be clipped to 4 bits, meaning that just `reactTag` will be taken into
account, e.g. different types of events can coalesce with each other if they target the same view

Switching to use an NSMutableDictionary instead of RCTSparseArray and NSNumber
as keys instead of uint64_t

Test Plan: Fixed the previous tests and added a new test to RCTEventDispatcherTests
2015-06-02 03:06:06 -08:00

200 lines
4.2 KiB
Objective-C

/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTEventDispatcher.h"
#import "RCTAssert.h"
#import "RCTBridge.h"
static NSNumber *RCTGetEventID(id<RCTEvent> event)
{
return @(
[event.viewTag intValue] |
(((uint64_t)event.eventName.hash & 0xFFFF) << 32) |
(((uint64_t)event.coalescingKey) << 48)
);
}
@implementation RCTBaseEvent
@synthesize viewTag = _viewTag;
@synthesize eventName = _eventName;
@synthesize body = _body;
- (instancetype)initWithViewTag:(NSNumber *)viewTag
eventName:(NSString *)eventName
body:(NSDictionary *)body
{
if ((self = [super init])) {
_viewTag = viewTag;
_eventName = eventName;
_body = body;
}
return self;
}
- (uint16_t)coalescingKey
{
return 0;
}
- (BOOL)canCoalesce
{
return YES;
}
- (id<RCTEvent>)coalesceWithEvent:(id<RCTEvent>)newEvent
{
return newEvent;
}
+ (NSString *)moduleDotMethod
{
return nil;
}
@end
@interface RCTEventDispatcher() <RCTBridgeModule, RCTFrameUpdateObserver>
@end
@implementation RCTEventDispatcher
{
NSMutableDictionary *_eventQueue;
NSLock *_eventQueueLock;
}
@synthesize bridge = _bridge;
RCT_EXPORT_MODULE()
- (instancetype)init
{
if ((self = [super init])) {
_eventQueue = [[NSMutableDictionary alloc] init];
_eventQueueLock = [[NSLock alloc] init];
}
return self;
}
RCT_IMPORT_METHOD(RCTNativeAppEventEmitter, emit);
RCT_IMPORT_METHOD(RCTDeviceEventEmitter, emit);
RCT_IMPORT_METHOD(RCTEventEmitter, receiveEvent);
- (void)sendAppEventWithName:(NSString *)name body:(id)body
{
[_bridge enqueueJSCall:@"RCTNativeAppEventEmitter.emit"
args:body ? @[name, body] : @[name]];
}
- (void)sendDeviceEventWithName:(NSString *)name body:(id)body
{
[_bridge enqueueJSCall:@"RCTDeviceEventEmitter.emit"
args:body ? @[name, body] : @[name]];
}
- (void)sendInputEventWithName:(NSString *)name body:(NSDictionary *)body
{
RCTAssert([body[@"target"] isKindOfClass:[NSNumber class]],
@"Event body dictionary must include a 'target' property containing a React tag");
[_bridge enqueueJSCall:@"RCTEventEmitter.receiveEvent"
args:body ? @[body[@"target"], name, body] : @[body[@"target"], name]];
}
- (void)sendTextEventWithType:(RCTTextEventType)type
reactTag:(NSNumber *)reactTag
text:(NSString *)text
{
static NSString *events[] = {
@"topFocus",
@"topBlur",
@"topChange",
@"topSubmitEditing",
@"topEndEditing",
};
[self sendInputEventWithName:events[type] body:text ? @{
@"text": text,
@"target": reactTag
} : @{
@"target": reactTag
}];
}
- (void)sendEvent:(id<RCTEvent>)event
{
if (!event.canCoalesce) {
[self dispatchEvent:event];
return;
}
[_eventQueueLock lock];
NSNumber *eventID = RCTGetEventID(event);
id<RCTEvent> previousEvent = _eventQueue[eventID];
if (previousEvent) {
event = [previousEvent coalesceWithEvent:event];
}
_eventQueue[eventID] = event;
[_eventQueueLock unlock];
}
- (void)dispatchEvent:(id<RCTEvent>)event
{
NSMutableArray *arguments = [[NSMutableArray alloc] init];
if (event.viewTag) {
[arguments addObject:event.viewTag];
}
[arguments addObject:event.eventName];
if (event.body) {
[arguments addObject:event.body];
}
[_bridge enqueueJSCall:[[event class] moduleDotMethod]
args:arguments];
}
- (dispatch_queue_t)methodQueue
{
return RCTJSThread;
}
- (void)didUpdateFrame:(RCTFrameUpdate *)update
{
NSDictionary *eventQueue;
[_eventQueueLock lock];
eventQueue = _eventQueue;
_eventQueue = [[NSMutableDictionary alloc] init];
[_eventQueueLock unlock];
for (id<RCTEvent> event in eventQueue.allValues) {
[self dispatchEvent:event];
}
}
@end
@implementation RCTBridge (RCTEventDispatcher)
- (RCTEventDispatcher *)eventDispatcher
{
return self.modules[RCTBridgeModuleNameForClass([RCTEventDispatcher class])];
}
@end