-
Notifications
You must be signed in to change notification settings - Fork 24k
/
RCTLegacyViewManagerInteropCoordinator.mm
215 lines (188 loc) · 7.73 KB
/
RCTLegacyViewManagerInteropCoordinator.mm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "RCTLegacyViewManagerInteropCoordinator.h"
#include <React/RCTBridge+Private.h>
#include <React/RCTBridgeMethod.h>
#include <React/RCTComponentData.h>
#include <React/RCTEventDispatcherProtocol.h>
#include <React/RCTFollyConvert.h>
#include <React/RCTModuleData.h>
#include <React/RCTModuleMethod.h>
#include <React/RCTUIManager.h>
#include <React/RCTUIManagerUtils.h>
#include <React/RCTUtils.h>
#include <folly/json.h>
#include <objc/runtime.h>
using namespace facebook::react;
@implementation RCTLegacyViewManagerInteropCoordinator {
RCTComponentData *_componentData;
__weak RCTBridge *_bridge;
__weak RCTBridgeModuleDecorator *_bridgelessInteropData;
/*
Each instance of `RCTLegacyViewManagerInteropComponentView` registers a block to which events are dispatched.
This is the container that maps unretained UIView pointer to a block to which the event is dispatched.
*/
NSMutableDictionary<NSNumber *, InterceptorBlock> *_eventInterceptors;
/*
* In bridgeless mode, instead of using the bridge to look up RCTModuleData,
* store that information locally.
*/
NSMutableArray<id<RCTBridgeMethod>> *_moduleMethods;
NSMutableDictionary<NSString *, id<RCTBridgeMethod>> *_moduleMethodsByName;
}
- (instancetype)initWithComponentData:(RCTComponentData *)componentData
bridge:(RCTBridge *)bridge
bridgelessInteropData:(RCTBridgeModuleDecorator *)bridgelessInteropData;
{
if (self = [super init]) {
_componentData = componentData;
_bridge = bridge;
_bridgelessInteropData = bridgelessInteropData;
if (bridgelessInteropData) {
// During bridge mode, RCTBridgeModules will be decorated with these APIs by the bridge.
RCTAssert(
_bridge == nil,
@"RCTLegacyViewManagerInteropCoordinator should not be initialized with RCTBridgeModuleDecorator in bridge mode.");
}
_eventInterceptors = [NSMutableDictionary new];
__weak __typeof(self) weakSelf = self;
_componentData.eventInterceptor = ^(NSString *eventName, NSDictionary *event, NSNumber *reactTag) {
__typeof(self) strongSelf = weakSelf;
if (strongSelf) {
InterceptorBlock block = [strongSelf->_eventInterceptors objectForKey:reactTag];
if (block) {
block(std::string([RCTNormalizeInputEventName(eventName) UTF8String]), convertIdToFollyDynamic(event ?: @{}));
}
}
};
}
return self;
}
- (void)addObserveForTag:(NSInteger)tag usingBlock:(InterceptorBlock)block
{
[_eventInterceptors setObject:block forKey:[NSNumber numberWithInteger:tag]];
}
- (void)removeObserveForTag:(NSInteger)tag
{
[_eventInterceptors removeObjectForKey:[NSNumber numberWithInteger:tag]];
}
- (UIView *)createPaperViewWithTag:(NSInteger)tag;
{
UIView *view = [_componentData createViewWithTag:[NSNumber numberWithInteger:tag] rootTag:NULL];
[_bridgelessInteropData attachInteropAPIsToModule:(id<RCTBridgeModule>)_componentData.bridgelessViewManager];
return view;
}
- (void)setProps:(folly::dynamic const &)props forView:(UIView *)view
{
if (props.isObject()) {
NSDictionary<NSString *, id> *convertedProps = convertFollyDynamicToId(props);
[_componentData setProps:convertedProps forView:view];
}
}
- (NSString *)componentViewName
{
return RCTDropReactPrefixes(_componentData.name);
}
- (void)handleCommand:(NSString *)commandName
args:(NSArray *)args
reactTag:(NSInteger)tag
paperView:(nonnull UIView *)paperView
{
Class managerClass = _componentData.managerClass;
[self _lookupModuleMethodsIfNecessary];
RCTModuleData *moduleData = [_bridge.batchedBridge moduleDataForName:RCTBridgeModuleNameForClass(managerClass)];
id<RCTBridgeMethod> method;
// We can't use `[NSString intValue]` as "0" is a valid command,
// but also a falsy value. [NSNumberFormatter numberFromString] returns a
// `NSNumber *` which is NULL when it's to be NULL
// and it points to 0 when the string is @"0" (not a falsy value).
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
if ([commandName isKindOfClass:[NSNumber class]] || [formatter numberFromString:commandName] != NULL) {
method = moduleData ? moduleData.methods[[commandName intValue]] : _moduleMethods[[commandName intValue]];
} else if ([commandName isKindOfClass:[NSString class]]) {
method = moduleData ? moduleData.methodsByName[commandName] : _moduleMethodsByName[commandName];
if (method == nil) {
RCTLogError(@"No command found with name \"%@\"", commandName);
}
} else {
RCTLogError(@"dispatchViewManagerCommand must be called with a string or integer command");
return;
}
NSArray *newArgs = [@[ [NSNumber numberWithInteger:tag] ] arrayByAddingObjectsFromArray:args];
if (_bridge) {
[_bridge.batchedBridge
dispatchBlock:^{
[method invokeWithBridge:self->_bridge module:self->_componentData.manager arguments:newArgs];
[self->_bridge.uiManager setNeedsLayout];
}
queue:RCTGetUIManagerQueue()];
} else {
// TODO T86826778 - Figure out which queue this should be dispatched to.
[method invokeWithBridge:nil module:self->_componentData.manager arguments:newArgs];
}
}
- (void)addViewToRegistry:(UIView *)view withTag:(NSInteger)tag
{
[self _addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
if ([viewRegistry objectForKey:@(tag)] != NULL) {
return;
}
NSMutableDictionary<NSNumber *, UIView *> *mutableViewRegistry =
(NSMutableDictionary<NSNumber *, UIView *> *)viewRegistry;
[mutableViewRegistry setObject:view forKey:@(tag)];
}];
}
- (void)removeViewFromRegistryWithTag:(NSInteger)tag
{
[self _addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
if ([viewRegistry objectForKey:@(tag)] == NULL) {
return;
}
NSMutableDictionary<NSNumber *, UIView *> *mutableViewRegistry =
(NSMutableDictionary<NSNumber *, UIView *> *)viewRegistry;
[mutableViewRegistry removeObjectForKey:@(tag)];
}];
}
#pragma mark - Private
- (void)_addUIBlock:(RCTViewManagerUIBlock)block
{
__weak __typeof__(self) weakSelf = self;
[_bridge.batchedBridge
dispatchBlock:^{
__typeof__(self) strongSelf = weakSelf;
[strongSelf->_bridge.uiManager addUIBlock:block];
}
queue:RCTGetUIManagerQueue()];
}
// This is copy-pasta from RCTModuleData.
- (void)_lookupModuleMethodsIfNecessary
{
if (!_bridge && !_moduleMethods) {
_moduleMethods = [NSMutableArray new];
_moduleMethodsByName = [NSMutableDictionary new];
unsigned int methodCount;
Class cls = _componentData.managerClass;
while (cls && cls != [NSObject class] && cls != [NSProxy class]) {
Method *methods = class_copyMethodList(object_getClass(cls), &methodCount);
for (unsigned int i = 0; i < methodCount; i++) {
Method method = methods[i];
SEL selector = method_getName(method);
if ([NSStringFromSelector(selector) hasPrefix:@"__rct_export__"]) {
IMP imp = method_getImplementation(method);
auto exportedMethod = ((const RCTMethodInfo *(*)(id, SEL))imp)(_componentData.managerClass, selector);
id<RCTBridgeMethod> moduleMethod =
[[RCTModuleMethod alloc] initWithExportedMethod:exportedMethod moduleClass:_componentData.managerClass];
[_moduleMethodsByName setValue:moduleMethod forKey:[NSString stringWithUTF8String:moduleMethod.JSMethodName]];
[_moduleMethods addObject:moduleMethod];
}
}
free(methods);
cls = class_getSuperclass(cls);
}
}
}
@end