From ea93151f21003df6f65dd173dd5dcb3135b0ae94 Mon Sep 17 00:00:00 2001 From: Lulu Wu Date: Wed, 14 Oct 2020 02:37:51 -0700 Subject: [PATCH] Make RCTEventDispatcher TurboModule-compatible Summary: This diff ended up being a bit more complicated than I anticipated, since the source files in `ReactInternal` were depending on `RCTEventDispatcher`. I made the following changes: 1. Make `RCTEventDispatcher` a `protocol`, keep it in `ReactInternal`. 2. Rename the `RCTEventDispatcher` NativeModule to `RCTEventDispatcherModule`, make it conform to the `RCTEventEmitter` `protocol`, and move it to `CoreModules`. 3. Where necessary, replace categories of `RCTEventDispatcher` with functions. Changelog: [iOS][Added] - Make RCTEventDispatcher TurboModule-comaptible Reviewed By: fkgozali Differential Revision: D18439488 fbshipit-source-id: b3da15c29459fddf884519f33b0c3b8c036b5539 --- Libraries/Image/RCTImageView.mm | 1 - Libraries/LinkingIOS/RCTLinkingManager.mm | 1 - .../Drivers/RCTEventAnimation.h | 2 +- .../NativeAnimation/RCTNativeAnimatedModule.h | 2 +- .../RCTNativeAnimatedNodesManager.h | 1 + .../RCTNativeAnimatedTurboModule.h | 2 +- Libraries/Network/RCTNetworking.mm | 1 - .../RCTPushNotificationManager.mm | 1 - Libraries/Settings/RCTSettingsManager.mm | 2 +- .../Text/TextInput/RCTBaseTextInputView.h | 1 - .../Text/TextInput/RCTBaseTextInputView.m | 4 +- React/Base/RCTBridge.h | 1 - React/Base/RCTBridge.m | 1 - React/Base/RCTComponentEvent.h | 2 +- React/Base/RCTEventDispatcher.m | 219 +---------------- ...patcher.h => RCTEventDispatcherProtocol.h} | 22 +- React/Base/RCTRootView.m | 1 - React/Base/RCTTouchEvent.h | 2 +- React/Base/RCTTouchHandler.m | 6 +- React/CoreModules/BUCK | 3 + React/CoreModules/CoreModulesPlugins.h | 1 + React/CoreModules/CoreModulesPlugins.mm | 1 + React/CoreModules/RCTAccessibilityManager.mm | 2 +- React/CoreModules/RCTAppState.mm | 2 +- React/CoreModules/RCTDevSettings.mm | 2 +- React/CoreModules/RCTDeviceInfo.mm | 2 +- React/CoreModules/RCTEventDispatcher.h | 17 ++ React/CoreModules/RCTEventDispatcher.mm | 232 ++++++++++++++++++ React/CoreModules/RCTKeyboardObserver.mm | 2 +- React/CoreModules/RCTRedBox.mm | 2 +- React/CoreModules/RCTStatusBarManager.mm | 2 +- React/Modules/RCTUIManager.m | 2 +- React/Views/RCTDatePickerManager.m | 1 - React/Views/RCTSegmentedControl.m | 1 - React/Views/RCTSliderManager.m | 1 - React/Views/RCTSwitch.m | 1 - React/Views/RCTSwitchManager.m | 1 - React/Views/RCTViewManager.h | 2 +- React/Views/RCTViewManager.m | 1 - React/Views/RCTWrapperViewController.m | 2 - React/Views/ScrollView/RCTScrollEvent.h | 2 +- React/Views/ScrollView/RCTScrollView.h | 14 +- React/Views/ScrollView/RCTScrollView.m | 12 +- .../RCTLegacyViewManagerInteropCoordinator.mm | 1 + packages/rn-tester/RCTTest/RCTTestModule.mm | 2 +- .../RCTRootViewIntegrationTests.m | 2 +- 46 files changed, 301 insertions(+), 284 deletions(-) rename React/Base/{RCTEventDispatcher.h => RCTEventDispatcherProtocol.h} (95%) create mode 100644 React/CoreModules/RCTEventDispatcher.h create mode 100644 React/CoreModules/RCTEventDispatcher.mm diff --git a/Libraries/Image/RCTImageView.mm b/Libraries/Image/RCTImageView.mm index 2b5fce147f6..15609120223 100644 --- a/Libraries/Image/RCTImageView.mm +++ b/Libraries/Image/RCTImageView.mm @@ -9,7 +9,6 @@ #import #import -#import #import #import #import diff --git a/Libraries/LinkingIOS/RCTLinkingManager.mm b/Libraries/LinkingIOS/RCTLinkingManager.mm index b1171f6ecae..355679b2824 100644 --- a/Libraries/LinkingIOS/RCTLinkingManager.mm +++ b/Libraries/LinkingIOS/RCTLinkingManager.mm @@ -9,7 +9,6 @@ #import #import -#import #import #import diff --git a/Libraries/NativeAnimation/Drivers/RCTEventAnimation.h b/Libraries/NativeAnimation/Drivers/RCTEventAnimation.h index cd06b1697ad..c63c1f4c643 100644 --- a/Libraries/NativeAnimation/Drivers/RCTEventAnimation.h +++ b/Libraries/NativeAnimation/Drivers/RCTEventAnimation.h @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#import +#import #import "RCTValueAnimatedNode.h" diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedModule.h b/Libraries/NativeAnimation/RCTNativeAnimatedModule.h index fde319b0eae..13b40fd8ae2 100644 --- a/Libraries/NativeAnimation/RCTNativeAnimatedModule.h +++ b/Libraries/NativeAnimation/RCTNativeAnimatedModule.h @@ -6,7 +6,7 @@ */ #import -#import +#import #import #import #import diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h index a3e35084268..be744568693 100644 --- a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h +++ b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h @@ -9,6 +9,7 @@ #import #import #import +#import @protocol RCTValueAnimatedNodeObserver; diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.h b/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.h index 71f40200c23..2da04a1277e 100644 --- a/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.h +++ b/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.h @@ -6,7 +6,7 @@ */ #import -#import +#import #import #import #import diff --git a/Libraries/Network/RCTNetworking.mm b/Libraries/Network/RCTNetworking.mm index c4dbf8a7bf3..c6fbe7092a7 100644 --- a/Libraries/Network/RCTNetworking.mm +++ b/Libraries/Network/RCTNetworking.mm @@ -11,7 +11,6 @@ #import #import #import -#import #import #import #import diff --git a/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm b/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm index 02fb7e4214b..aa075bb6bfa 100644 --- a/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm +++ b/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm @@ -12,7 +12,6 @@ #import #import #import -#import #import #import "RCTPushNotificationPlugins.h" diff --git a/Libraries/Settings/RCTSettingsManager.mm b/Libraries/Settings/RCTSettingsManager.mm index 2f6645f24d6..8628857bc8a 100644 --- a/Libraries/Settings/RCTSettingsManager.mm +++ b/Libraries/Settings/RCTSettingsManager.mm @@ -10,7 +10,7 @@ #import #import #import -#import +#import #import #import "RCTSettingsPlugins.h" diff --git a/Libraries/Text/TextInput/RCTBaseTextInputView.h b/Libraries/Text/TextInput/RCTBaseTextInputView.h index d7fc54bed85..d73f082cbd8 100644 --- a/Libraries/Text/TextInput/RCTBaseTextInputView.h +++ b/Libraries/Text/TextInput/RCTBaseTextInputView.h @@ -13,7 +13,6 @@ #import "RCTBackedTextInputViewProtocol.h" @class RCTBridge; -@class RCTEventDispatcher; @class RCTTextAttributes; @class RCTTextSelection; diff --git a/Libraries/Text/TextInput/RCTBaseTextInputView.m b/Libraries/Text/TextInput/RCTBaseTextInputView.m index 01ab7f19d12..d619e5b5640 100644 --- a/Libraries/Text/TextInput/RCTBaseTextInputView.m +++ b/Libraries/Text/TextInput/RCTBaseTextInputView.m @@ -9,7 +9,7 @@ #import #import -#import +#import #import #import #import @@ -21,7 +21,7 @@ @implementation RCTBaseTextInputView { __weak RCTBridge *_bridge; - __weak RCTEventDispatcher *_eventDispatcher; + __weak id _eventDispatcher; BOOL _hasInputAccesoryView; NSString *_Nullable _predictedText; BOOL _didMoveToWindow; diff --git a/React/Base/RCTBridge.h b/React/Base/RCTBridge.h index 55265010e09..1331f5009cb 100644 --- a/React/Base/RCTBridge.h +++ b/React/Base/RCTBridge.h @@ -15,7 +15,6 @@ @class JSValue; @class RCTBridge; -@class RCTEventDispatcher; @class RCTPerformanceLogger; /** diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 7d5c9cd414b..2429d58f86b 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -11,7 +11,6 @@ #import #import "RCTConvert.h" -#import "RCTEventDispatcher.h" #if RCT_ENABLE_INSPECTOR #import "RCTInspectorDevServerHelper.h" #endif diff --git a/React/Base/RCTComponentEvent.h b/React/Base/RCTComponentEvent.h index cad756234b2..0467f8c50f3 100644 --- a/React/Base/RCTComponentEvent.h +++ b/React/Base/RCTComponentEvent.h @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#import +#import NS_ASSUME_NONNULL_BEGIN diff --git a/React/Base/RCTEventDispatcher.m b/React/Base/RCTEventDispatcher.m index dfb61365760..9da63c3af11 100644 --- a/React/Base/RCTEventDispatcher.m +++ b/React/Base/RCTEventDispatcher.m @@ -5,14 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#import "RCTEventDispatcher.h" - -#import "RCTAssert.h" -#import "RCTBridge+Private.h" -#import "RCTBridge.h" -#import "RCTComponentEvent.h" -#import "RCTProfile.h" -#import "RCTUtils.h" +#import "RCTEventDispatcherProtocol.h" const NSInteger RCTTextUpdateLagWarningThreshold = 3; @@ -27,217 +20,11 @@ NSString *RCTNormalizeInputEventName(NSString *eventName) return eventName; } -static NSNumber *RCTGetEventID(NSNumber *viewTag, NSString *eventName, uint16_t coalescingKey) -{ - return @(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. - NSLock *_eventQueueLock; - // We have this id -> event mapping so we coalesce effectively. - NSMutableDictionary> *_events; - // This array contains ids of events in order they come in, so we can emit them to JS in the exact same order. - NSMutableArray *_eventQueue; - BOOL _eventsDispatchScheduled; - NSHashTable> *_observers; - NSLock *_observersLock; -} - -@synthesize bridge = _bridge; -@synthesize dispatchToJSThread = _dispatchToJSThread; -@synthesize invokeJS = _invokeJS; -@synthesize invokeJSWithModuleDotMethod = _invokeJSWithModuleDotMethod; - -RCT_EXPORT_MODULE() - -- (void)setBridge:(RCTBridge *)bridge -{ - _bridge = bridge; - _events = [NSMutableDictionary new]; - _eventQueue = [NSMutableArray new]; - _eventQueueLock = [NSLock new]; - _eventsDispatchScheduled = NO; - _observers = [NSHashTable weakObjectsHashTable]; - _observersLock = [NSLock new]; -} - -- (void)sendAppEventWithName:(NSString *)name body:(id)body -{ - if (_bridge) { - [_bridge enqueueJSCall:@"RCTNativeAppEventEmitter" - method:@"emit" - args:body ? @[ name, body ] : @[ name ] - completion:NULL]; - } else { - _invokeJS(@"RCTNativeAppEventEmitter", @"emit", body ? @[ name, body ] : @[ name ]); - } -} - -- (void)sendDeviceEventWithName:(NSString *)name body:(id)body -{ - if (_bridge) { - [_bridge enqueueJSCall:@"RCTDeviceEventEmitter" - method:@"emit" - args:body ? @[ name, body ] : @[ name ] - completion:NULL]; - } else { - _invokeJS(@"RCTDeviceEventEmitter", @"emit", body ? @[ name, body ] : @[ name ]); - } -} - -- (void)sendTextEventWithType:(RCTTextEventType)type - reactTag:(NSNumber *)reactTag - text:(NSString *)text - key:(NSString *)key - eventCount:(NSInteger)eventCount -{ - static NSString *events[] = {@"focus", @"blur", @"change", @"submitEditing", @"endEditing", @"keyPress"}; - - NSMutableDictionary *body = [[NSMutableDictionary alloc] initWithDictionary:@{ - @"eventCount" : @(eventCount), - }]; - - if (text) { - body[@"text"] = text; - } - - if (key) { - if (key.length == 0) { - key = @"Backspace"; // backspace - } else { - switch ([key characterAtIndex:0]) { - case '\t': - key = @"Tab"; - break; - case '\n': - key = @"Enter"; - default: - break; - } - } - body[@"key"] = key; - } - - RCTComponentEvent *event = [[RCTComponentEvent alloc] initWithName:events[type] viewTag:reactTag body:body]; - [self sendEvent:event]; -} - -- (void)sendEvent:(id)event -{ - [_observersLock lock]; - - for (id observer in _observers) { - [observer eventDispatcherWillDispatchEvent:event]; - } - - [_observersLock unlock]; - - [_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]; - } - } 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]; - } - - _events[eventID] = event; - - BOOL scheduleEventsDispatch = NO; - if (!_eventsDispatchScheduled) { - _eventsDispatchScheduled = YES; - scheduleEventsDispatch = YES; - } - - // 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) { - if (_bridge) { - [_bridge - dispatchBlock:^{ - [self flushEventsQueue]; - } - queue:RCTJSThread]; - } else if (_dispatchToJSThread) { - _dispatchToJSThread(^{ - [self flushEventsQueue]; - }); - } - } -} - -- (void)addDispatchObserver:(id)observer -{ - [_observersLock lock]; - [_observers addObject:observer]; - [_observersLock unlock]; -} - -- (void)removeDispatchObserver:(id)observer -{ - [_observersLock lock]; - [_observers removeObject:observer]; - [_observersLock unlock]; -} - -- (void)dispatchEvent:(id)event -{ - if (_bridge) { - [_bridge enqueueJSCall:[[event class] moduleDotMethod] args:[event arguments]]; - } else { - _invokeJSWithModuleDotMethod([[event class] moduleDotMethod], [event arguments]); - } -} - -- (dispatch_queue_t)methodQueue -{ - return RCTJSThread; -} - -// js thread only (which surprisingly can be the main thread, depends on used JS executor) -- (void)flushEventsQueue -{ - [_eventQueueLock lock]; - NSDictionary *events = _events; - _events = [NSMutableDictionary new]; - NSMutableArray *eventQueue = _eventQueue; - _eventQueue = [NSMutableArray new]; - _eventsDispatchScheduled = NO; - [_eventQueueLock unlock]; - - for (NSNumber *eventId in eventQueue) { - [self dispatchEvent:events[eventId]]; - } -} - -@end - @implementation RCTBridge (RCTEventDispatcher) -- (RCTEventDispatcher *)eventDispatcher +- (id)eventDispatcher { - return [self moduleForClass:[RCTEventDispatcher class]]; + return [self moduleForName:@"EventDispatcher" lazilyLoadIfNecessary:YES]; } @end diff --git a/React/Base/RCTEventDispatcher.h b/React/Base/RCTEventDispatcherProtocol.h similarity index 95% rename from React/Base/RCTEventDispatcher.h rename to React/Base/RCTEventDispatcherProtocol.h index dfaba98c2cf..0c703927726 100644 --- a/React/Base/RCTEventDispatcher.h +++ b/React/Base/RCTEventDispatcherProtocol.h @@ -10,15 +10,6 @@ #import #import -typedef NS_ENUM(NSInteger, RCTTextEventType) { - RCTTextEventTypeFocus, - RCTTextEventTypeBlur, - RCTTextEventTypeChange, - RCTTextEventTypeSubmit, - RCTTextEventTypeEnd, - RCTTextEventTypeKeyPress -}; - /** * The threshold at which text inputs will start warning that the JS thread * has fallen behind (resulting in poor input performance, missed keys, etc.) @@ -32,6 +23,15 @@ RCT_EXTERN const NSInteger RCTTextUpdateLagWarningThreshold; */ RCT_EXTERN NSString *RCTNormalizeInputEventName(NSString *eventName); +typedef NS_ENUM(NSInteger, RCTTextEventType) { + RCTTextEventTypeFocus, + RCTTextEventTypeBlur, + RCTTextEventTypeChange, + RCTTextEventTypeSubmit, + RCTTextEventTypeEnd, + RCTTextEventTypeKeyPress +}; + @protocol RCTEvent @required @@ -80,7 +80,7 @@ RCT_EXTERN NSString *RCTNormalizeInputEventName(NSString *eventName); * This class wraps the -[RCTBridge enqueueJSCall:args:] method, and * provides some convenience methods for generating event calls. */ -@interface RCTEventDispatcher : NSObject +@protocol RCTEventDispatcherProtocol /** * Deprecated, do not use. @@ -123,6 +123,6 @@ RCT_EXTERN NSString *RCTNormalizeInputEventName(NSString *eventName); @interface RCTBridge (RCTEventDispatcher) -- (RCTEventDispatcher *)eventDispatcher; +- (id)eventDispatcher; @end diff --git a/React/Base/RCTRootView.m b/React/Base/RCTRootView.m index d9fbc37b38d..62bdd758577 100644 --- a/React/Base/RCTRootView.m +++ b/React/Base/RCTRootView.m @@ -15,7 +15,6 @@ #import "RCTBridge+Private.h" #import "RCTBridge.h" #import "RCTConstants.h" -#import "RCTEventDispatcher.h" #import "RCTKeyCommands.h" #import "RCTLog.h" #import "RCTPerformanceLogger.h" diff --git a/React/Base/RCTTouchEvent.h b/React/Base/RCTTouchEvent.h index 99cc6d435a1..9f3cc4f9ff3 100644 --- a/React/Base/RCTTouchEvent.h +++ b/React/Base/RCTTouchEvent.h @@ -7,7 +7,7 @@ #import -#import +#import /** * Represents a touch event, which may be composed of several touches (one for every finger). diff --git a/React/Base/RCTTouchHandler.m b/React/Base/RCTTouchHandler.m index 8361fdf55c7..fc32afb0a79 100644 --- a/React/Base/RCTTouchHandler.m +++ b/React/Base/RCTTouchHandler.m @@ -11,7 +11,7 @@ #import "RCTAssert.h" #import "RCTBridge.h" -#import "RCTEventDispatcher.h" +#import "RCTEventDispatcherProtocol.h" #import "RCTLog.h" #import "RCTSurfaceView.h" #import "RCTTouchEvent.h" @@ -25,7 +25,7 @@ // TODO: this class behaves a lot like a module, and could be implemented as a // module if we were to assume that modules and RootViews had a 1:1 relationship @implementation RCTTouchHandler { - __weak RCTEventDispatcher *_eventDispatcher; + __weak id _eventDispatcher; /** * Arrays managed in parallel tracking native touch object along with the @@ -47,7 +47,7 @@ RCTAssertParam(bridge); if ((self = [super initWithTarget:nil action:NULL])) { - _eventDispatcher = [bridge moduleForClass:[RCTEventDispatcher class]]; + _eventDispatcher = bridge.eventDispatcher; _nativeTouches = [NSMutableOrderedSet new]; _reactTouches = [NSMutableArray new]; diff --git a/React/CoreModules/BUCK b/React/CoreModules/BUCK index 2170d80b1f9..d1bac10dab9 100644 --- a/React/CoreModules/BUCK +++ b/React/CoreModules/BUCK @@ -117,6 +117,9 @@ rn_apple_library( ) + react_module_plugin_providers( name = "DevSplitBundleLoader", native_class_func = "RCTDevSplitBundleLoaderCls", + ) + react_module_plugin_providers( + name = "EventDispatcher", + native_class_func = "RCTEventDispatcherCls", ), plugins_header = "FBCoreModulesPlugins.h", preprocessor_flags = OBJC_ARC_PREPROCESSOR_FLAGS + get_preprocessor_flags_for_build_mode() + rn_extra_build_flags() + [ diff --git a/React/CoreModules/CoreModulesPlugins.h b/React/CoreModules/CoreModulesPlugins.h index b8fa8e9f076..0dd21309bce 100644 --- a/React/CoreModules/CoreModulesPlugins.h +++ b/React/CoreModules/CoreModulesPlugins.h @@ -53,6 +53,7 @@ Class RCTWebSocketExecutorCls(void) __attribute__((used)); Class RCTWebSocketModuleCls(void) __attribute__((used)); Class RCTDevLoadingViewCls(void) __attribute__((used)); Class RCTDevSplitBundleLoaderCls(void) __attribute__((used)); +Class RCTEventDispatcherCls(void) __attribute__((used)); #ifdef __cplusplus } diff --git a/React/CoreModules/CoreModulesPlugins.mm b/React/CoreModules/CoreModulesPlugins.mm index de47e923f81..c2d1b268b4f 100644 --- a/React/CoreModules/CoreModulesPlugins.mm +++ b/React/CoreModules/CoreModulesPlugins.mm @@ -42,6 +42,7 @@ Class RCTCoreModulesClassProvider(const char *name) { {"WebSocketModule", RCTWebSocketModuleCls}, {"DevLoadingView", RCTDevLoadingViewCls}, {"DevSplitBundleLoader", RCTDevSplitBundleLoaderCls}, + {"EventDispatcher", RCTEventDispatcherCls}, }; auto p = sCoreModuleClassMap.find(name); diff --git a/React/CoreModules/RCTAccessibilityManager.mm b/React/CoreModules/RCTAccessibilityManager.mm index 3f6098e66ab..fc1741c21ee 100644 --- a/React/CoreModules/RCTAccessibilityManager.mm +++ b/React/CoreModules/RCTAccessibilityManager.mm @@ -10,7 +10,7 @@ #import #import #import -#import +#import #import #import diff --git a/React/CoreModules/RCTAppState.mm b/React/CoreModules/RCTAppState.mm index b0dc10bce5a..5fc28ef71c8 100644 --- a/React/CoreModules/RCTAppState.mm +++ b/React/CoreModules/RCTAppState.mm @@ -10,7 +10,7 @@ #import #import #import -#import +#import #import #import "CoreModulesPlugins.h" diff --git a/React/CoreModules/RCTDevSettings.mm b/React/CoreModules/RCTDevSettings.mm index 4bf03630201..4e11bd3c263 100644 --- a/React/CoreModules/RCTDevSettings.mm +++ b/React/CoreModules/RCTDevSettings.mm @@ -12,7 +12,7 @@ #import #import #import -#import +#import #import #import #import diff --git a/React/CoreModules/RCTDeviceInfo.mm b/React/CoreModules/RCTDeviceInfo.mm index 829821acc57..d70f6310362 100644 --- a/React/CoreModules/RCTDeviceInfo.mm +++ b/React/CoreModules/RCTDeviceInfo.mm @@ -11,7 +11,7 @@ #import #import #import -#import +#import #import #import diff --git a/React/CoreModules/RCTEventDispatcher.h b/React/CoreModules/RCTEventDispatcher.h new file mode 100644 index 00000000000..e15c2072325 --- /dev/null +++ b/React/CoreModules/RCTEventDispatcher.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import + +#import +#import +/** + * This class wraps the -[RCTBridge enqueueJSCall:args:] method, and + * provides some convenience methods for generating event calls. + */ +@interface RCTEventDispatcher : NSObject +@end diff --git a/React/CoreModules/RCTEventDispatcher.mm b/React/CoreModules/RCTEventDispatcher.mm new file mode 100644 index 00000000000..180815b964b --- /dev/null +++ b/React/CoreModules/RCTEventDispatcher.mm @@ -0,0 +1,232 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "RCTEventDispatcher.h" + +#import +#import +#import +#import +#import +#import +#import + +#import "CoreModulesPlugins.h" + +static NSNumber *RCTGetEventID(NSNumber *viewTag, NSString *eventName, uint16_t coalescingKey) +{ + return @(viewTag.intValue | (((uint64_t)eventName.hash & 0xFFFF) << 32) | (((uint64_t)coalescingKey) << 48)); +} + +static uint16_t RCTUniqueCoalescingKeyGenerator = 0; + +@interface RCTEventDispatcher () +@end + +@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. + NSLock *_eventQueueLock; + // We have this id -> event mapping so we coalesce effectively. + NSMutableDictionary> *_events; + // This array contains ids of events in order they come in, so we can emit them to JS in the exact same order. + NSMutableArray *_eventQueue; + BOOL _eventsDispatchScheduled; + NSHashTable> *_observers; + NSLock *_observersLock; +} + +@synthesize bridge = _bridge; +@synthesize dispatchToJSThread = _dispatchToJSThread; +@synthesize invokeJS = _invokeJS; +@synthesize invokeJSWithModuleDotMethod = _invokeJSWithModuleDotMethod; + +RCT_EXPORT_MODULE() + +- (void)setBridge:(RCTBridge *)bridge +{ + _bridge = bridge; + _events = [NSMutableDictionary new]; + _eventQueue = [NSMutableArray new]; + _eventQueueLock = [NSLock new]; + _eventsDispatchScheduled = NO; + _observers = [NSHashTable weakObjectsHashTable]; + _observersLock = [NSLock new]; +} + +- (void)sendAppEventWithName:(NSString *)name body:(id)body +{ + if (_bridge) { + [_bridge enqueueJSCall:@"RCTNativeAppEventEmitter" + method:@"emit" + args:body ? @[ name, body ] : @[ name ] + completion:NULL]; + } else { + _invokeJS(@"RCTNativeAppEventEmitter", @"emit", body ? @[ name, body ] : @[ name ]); + } +} + +- (void)sendDeviceEventWithName:(NSString *)name body:(id)body +{ + if (_bridge) { + [_bridge enqueueJSCall:@"RCTDeviceEventEmitter" + method:@"emit" + args:body ? @[ name, body ] : @[ name ] + completion:NULL]; + } else { + _invokeJS(@"RCTDeviceEventEmitter", @"emit", body ? @[ name, body ] : @[ name ]); + } +} + +- (void)sendTextEventWithType:(RCTTextEventType)type + reactTag:(NSNumber *)reactTag + text:(NSString *)text + key:(NSString *)key + eventCount:(NSInteger)eventCount +{ + static NSString *events[] = {@"focus", @"blur", @"change", @"submitEditing", @"endEditing", @"keyPress"}; + + NSMutableDictionary *body = [[NSMutableDictionary alloc] initWithDictionary:@{ + @"eventCount" : @(eventCount), + }]; + + if (text) { + body[@"text"] = text; + } + + if (key) { + if (key.length == 0) { + key = @"Backspace"; // backspace + } else { + switch ([key characterAtIndex:0]) { + case '\t': + key = @"Tab"; + break; + case '\n': + key = @"Enter"; + default: + break; + } + } + body[@"key"] = key; + } + + RCTComponentEvent *event = [[RCTComponentEvent alloc] initWithName:events[type] viewTag:reactTag body:body]; + [self sendEvent:event]; +} + +- (void)sendEvent:(id)event +{ + [_observersLock lock]; + + for (id observer in _observers) { + [observer eventDispatcherWillDispatchEvent:event]; + } + + [_observersLock unlock]; + + [_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]; + } + } 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]; + } + + _events[eventID] = event; + + BOOL scheduleEventsDispatch = NO; + if (!_eventsDispatchScheduled) { + _eventsDispatchScheduled = YES; + scheduleEventsDispatch = YES; + } + + // 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) { + if (_bridge) { + [_bridge + dispatchBlock:^{ + [self flushEventsQueue]; + } + queue:RCTJSThread]; + } else if (_dispatchToJSThread) { + _dispatchToJSThread(^{ + [self flushEventsQueue]; + }); + } + } +} + +- (void)addDispatchObserver:(id)observer +{ + [_observersLock lock]; + [_observers addObject:observer]; + [_observersLock unlock]; +} + +- (void)removeDispatchObserver:(id)observer +{ + [_observersLock lock]; + [_observers removeObject:observer]; + [_observersLock unlock]; +} + +- (void)dispatchEvent:(id)event +{ + if (_bridge) { + [_bridge enqueueJSCall:[[event class] moduleDotMethod] args:[event arguments]]; + } else { + _invokeJSWithModuleDotMethod([[event class] moduleDotMethod], [event arguments]); + } +} + +- (dispatch_queue_t)methodQueue +{ + return RCTJSThread; +} + +// js thread only (which surprisingly can be the main thread, depends on used JS executor) +- (void)flushEventsQueue +{ + [_eventQueueLock lock]; + NSDictionary *events = _events; + _events = [NSMutableDictionary new]; + NSMutableArray *eventQueue = _eventQueue; + _eventQueue = [NSMutableArray new]; + _eventsDispatchScheduled = NO; + [_eventQueueLock unlock]; + + for (NSNumber *eventId in eventQueue) { + [self dispatchEvent:events[eventId]]; + } +} + +@end + +Class RCTEventDispatcherCls(void) +{ + return RCTEventDispatcher.class; +} diff --git a/React/CoreModules/RCTKeyboardObserver.mm b/React/CoreModules/RCTKeyboardObserver.mm index 93c853f7acc..072df8ce3d7 100644 --- a/React/CoreModules/RCTKeyboardObserver.mm +++ b/React/CoreModules/RCTKeyboardObserver.mm @@ -8,7 +8,7 @@ #import "RCTKeyboardObserver.h" #import -#import +#import #import "CoreModulesPlugins.h" diff --git a/React/CoreModules/RCTRedBox.mm b/React/CoreModules/RCTRedBox.mm index 9fec829b015..64c9e332894 100644 --- a/React/CoreModules/RCTRedBox.mm +++ b/React/CoreModules/RCTRedBox.mm @@ -12,7 +12,7 @@ #import #import #import -#import +#import #import #import #import diff --git a/React/CoreModules/RCTStatusBarManager.mm b/React/CoreModules/RCTStatusBarManager.mm index 4104a708151..c40fdd4379e 100644 --- a/React/CoreModules/RCTStatusBarManager.mm +++ b/React/CoreModules/RCTStatusBarManager.mm @@ -8,7 +8,7 @@ #import "RCTStatusBarManager.h" #import "CoreModulesPlugins.h" -#import +#import #import #import diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 8f43fd3f3b1..b6dfefa3e60 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -17,7 +17,7 @@ #import "RCTComponentData.h" #import "RCTConvert.h" #import "RCTDefines.h" -#import "RCTEventDispatcher.h" +#import "RCTEventDispatcherProtocol.h" #import "RCTLayoutAnimation.h" #import "RCTLayoutAnimationGroup.h" #import "RCTLog.h" diff --git a/React/Views/RCTDatePickerManager.m b/React/Views/RCTDatePickerManager.m index 198a4ef77ef..38f662463f6 100644 --- a/React/Views/RCTDatePickerManager.m +++ b/React/Views/RCTDatePickerManager.m @@ -10,7 +10,6 @@ #import #import "RCTBridge.h" #import "RCTDatePicker.h" -#import "RCTEventDispatcher.h" #import "UIView+React.h" @implementation RCTConvert (UIDatePicker) diff --git a/React/Views/RCTSegmentedControl.m b/React/Views/RCTSegmentedControl.m index 10c1c745a59..1ffdb59921a 100644 --- a/React/Views/RCTSegmentedControl.m +++ b/React/Views/RCTSegmentedControl.m @@ -8,7 +8,6 @@ #import "RCTSegmentedControl.h" #import "RCTConvert.h" -#import "RCTEventDispatcher.h" #import "UIView+React.h" @implementation RCTSegmentedControl diff --git a/React/Views/RCTSliderManager.m b/React/Views/RCTSliderManager.m index a3a67959575..e2c28101102 100644 --- a/React/Views/RCTSliderManager.m +++ b/React/Views/RCTSliderManager.m @@ -8,7 +8,6 @@ #import "RCTSliderManager.h" #import "RCTBridge.h" -#import "RCTEventDispatcher.h" #import "RCTSlider.h" #import "UIView+React.h" diff --git a/React/Views/RCTSwitch.m b/React/Views/RCTSwitch.m index 80475480d13..8abd4d5afba 100644 --- a/React/Views/RCTSwitch.m +++ b/React/Views/RCTSwitch.m @@ -7,7 +7,6 @@ #import "RCTSwitch.h" -#import "RCTEventDispatcher.h" #import "UIView+React.h" @implementation RCTSwitch diff --git a/React/Views/RCTSwitchManager.m b/React/Views/RCTSwitchManager.m index 2ea446fac9f..f37df49c4df 100644 --- a/React/Views/RCTSwitchManager.m +++ b/React/Views/RCTSwitchManager.m @@ -9,7 +9,6 @@ #import #import "RCTBridge.h" -#import "RCTEventDispatcher.h" #import "RCTSwitch.h" #import "UIView+React.h" diff --git a/React/Views/RCTViewManager.h b/React/Views/RCTViewManager.h index b35ceb23f73..5c1eba4d80a 100644 --- a/React/Views/RCTViewManager.h +++ b/React/Views/RCTViewManager.h @@ -10,7 +10,7 @@ #import #import #import -#import +#import #import #import diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index ee6c41fee1c..48dcd0a06ce 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -11,7 +11,6 @@ #import "RCTBridge.h" #import "RCTConvert+Transform.h" #import "RCTConvert.h" -#import "RCTEventDispatcher.h" #import "RCTLog.h" #import "RCTShadowView.h" #import "RCTUIManager.h" diff --git a/React/Views/RCTWrapperViewController.m b/React/Views/RCTWrapperViewController.m index fc9eaa6c507..20dd2101e91 100644 --- a/React/Views/RCTWrapperViewController.m +++ b/React/Views/RCTWrapperViewController.m @@ -10,14 +10,12 @@ #import #import "RCTAutoInsetsProtocol.h" -#import "RCTEventDispatcher.h" #import "RCTUtils.h" #import "UIView+React.h" @implementation RCTWrapperViewController { UIView *_wrapperView; UIView *_contentView; - RCTEventDispatcher *_eventDispatcher; CGFloat _previousTopLayoutLength; CGFloat _previousBottomLayoutLength; diff --git a/React/Views/ScrollView/RCTScrollEvent.h b/React/Views/ScrollView/RCTScrollEvent.h index 8494f0e6e80..29a645d30c6 100644 --- a/React/Views/ScrollView/RCTScrollEvent.h +++ b/React/Views/ScrollView/RCTScrollEvent.h @@ -6,7 +6,7 @@ */ #import -#import +#import @interface RCTScrollEvent : NSObject diff --git a/React/Views/ScrollView/RCTScrollView.h b/React/Views/ScrollView/RCTScrollView.h index d5ac000284e..4a508ee1e9d 100644 --- a/React/Views/ScrollView/RCTScrollView.h +++ b/React/Views/ScrollView/RCTScrollView.h @@ -8,7 +8,8 @@ #import #import -#import +#import +#import #import #import @@ -16,7 +17,7 @@ @interface RCTScrollView : RCTView -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithEventDispatcher:(id)eventDispatcher NS_DESIGNATED_INITIALIZER; /** * The `RCTScrollView` may have at most one single subview. This will ensure @@ -71,11 +72,4 @@ @end -@interface RCTEventDispatcher (RCTScrollView) - -/** - * Send a fake scroll event. - */ -- (void)sendFakeScrollEvent:(NSNumber *)reactTag; - -@end +RCT_EXTERN void RCTSendFakeScrollEvent(id eventDispatcher, NSNumber *reactTag); diff --git a/React/Views/ScrollView/RCTScrollView.m b/React/Views/ScrollView/RCTScrollView.m index 8bdb3bb461d..c52f449c730 100644 --- a/React/Views/ScrollView/RCTScrollView.m +++ b/React/Views/ScrollView/RCTScrollView.m @@ -261,7 +261,7 @@ @end @implementation RCTScrollView { - RCTEventDispatcher *_eventDispatcher; + id _eventDispatcher; CGRect _prevFirstVisibleFrame; __weak UIView *_firstVisibleView; RCTCustomScrollView *_scrollView; @@ -275,7 +275,7 @@ NSHashTable *_scrollListeners; } -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher +- (instancetype)initWithEventDispatcher:(id)eventDispatcher { RCTAssertParam(eventDispatcher); @@ -1022,9 +1022,7 @@ RCT_SET_AND_PRESERVE_OFFSET(setScrollIndicatorInsets, scrollIndicatorInsets, UIE @end -@implementation RCTEventDispatcher (RCTScrollView) - -- (void)sendFakeScrollEvent:(NSNumber *)reactTag +void RCTSendFakeScrollEvent(id eventDispatcher, NSNumber *reactTag) { // Use the selector here in case the onScroll block property is ever renamed NSString *eventName = NSStringFromSelector(@selector(onScroll)); @@ -1037,7 +1035,5 @@ RCT_SET_AND_PRESERVE_OFFSET(setScrollIndicatorInsets, scrollIndicatorInsets, UIE scrollViewZoomScale:0 userData:nil coalescingKey:0]; - [self sendEvent:fakeScrollEvent]; + [eventDispatcher sendEvent:fakeScrollEvent]; } - -@end diff --git a/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm b/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm index d8f1b4abf4e..3e962334bf9 100644 --- a/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm +++ b/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/packages/rn-tester/RCTTest/RCTTestModule.mm b/packages/rn-tester/RCTTest/RCTTestModule.mm index a0bfe8e1390..33e5f090983 100644 --- a/packages/rn-tester/RCTTest/RCTTestModule.mm +++ b/packages/rn-tester/RCTTest/RCTTestModule.mm @@ -8,7 +8,7 @@ #import "RCTTestModule.h" #import -#import +#import #import #import #import diff --git a/packages/rn-tester/RNTesterIntegrationTests/RCTRootViewIntegrationTests.m b/packages/rn-tester/RNTesterIntegrationTests/RCTRootViewIntegrationTests.m index 690f86493a5..a6d374f935a 100644 --- a/packages/rn-tester/RNTesterIntegrationTests/RCTRootViewIntegrationTests.m +++ b/packages/rn-tester/RNTesterIntegrationTests/RCTRootViewIntegrationTests.m @@ -9,7 +9,7 @@ #import #import -#import +#import #import #import