mirror of
https://github.com/status-im/react-native.git
synced 2025-01-12 02:24:23 +00:00
2015-02-02 updates
- Removed special-case treatment of RCTTiming and RCTUIManager modules | Nick Lockwood
This commit is contained in:
parent
00f0ebccdf
commit
ccd8f184af
18
README.md
18
README.md
@ -92,27 +92,27 @@ the responder system.
|
||||
|
||||
# FAQ
|
||||
|
||||
Q. How does debugging work? Can I set breakpoints in my JS?
|
||||
Q. How does debugging work? Can I set breakpoints in my JS?
|
||||
A. We are going to add the ability to use the Chrome developer tools soon. We
|
||||
are very passionate about building the best possible developer experience.
|
||||
|
||||
Q. When is this coming to Android/Windows/OS X/etc?
|
||||
Q. When is this coming to Android/Windows/OS X/etc?
|
||||
A. We're working on Android, and we are excited to release it as soon as we can.
|
||||
We are looking forward to the community helping us target other platforms as
|
||||
well :)
|
||||
|
||||
Q. How do I create my own app?
|
||||
Q. How do I create my own app?
|
||||
A. Copy the entire `Examples/TicTacToe` folder, rename stuff in Xcode, and
|
||||
replace the `TicTacToeApp.js` with your own. Then, in `AppDelegate.m`, update
|
||||
`moduleName` to match your call to
|
||||
`Bundler.registerComponent(<moduleName>, <componentName>)` at the bottom of your
|
||||
JS file, and update `jsCodeLocation` to match your JS file name and location.
|
||||
|
||||
Q. Can I submit my own React Native app to the App Store?
|
||||
Q. Can I submit my own React Native app to the App Store?
|
||||
A. Not yet, but you will be able to soon. If you build something you want to
|
||||
submit to the App Store, come talk to us ASAP.
|
||||
|
||||
Q. How do I deploy to my device?
|
||||
Q. How do I deploy to my device?
|
||||
A. You can change `localhost` in `AppDelegate.m` to your laptop's IP address and
|
||||
grab the bundle over the same Wi-Fi network. You can also download the bundle
|
||||
that the React packager generates, save it to the file `main.jsbundle`, and add it
|
||||
@ -120,20 +120,20 @@ as a static resource in your Xcode project. Then set the `jsCodeLocation` in
|
||||
`AppDelegate.m` to point to that file and deploy to your device like you would
|
||||
any other app.
|
||||
|
||||
Q. What's up with this private repo? Why aren't you just open sourcing it now?
|
||||
Q. What's up with this private repo? Why aren't you just open sourcing it now?
|
||||
A. We want input from the React community before we open the floodgates so we
|
||||
can incorporate your feedback, and we also have a bunch more features we want to
|
||||
add to make a more complete offering before we open source.
|
||||
|
||||
Q. Do you have to ship a JS runtime with your apps?
|
||||
Q. Do you have to ship a JS runtime with your apps?
|
||||
A. No, we just use the JavaScriptCore public API that is part of iOS 7 and
|
||||
later.
|
||||
|
||||
Q. How do I add more native capabilities?
|
||||
Q. How do I add more native capabilities?
|
||||
A. React Native is designed to be extensible - come talk to us, we would love to
|
||||
work with you.
|
||||
|
||||
Q. Can I reuse existing iOS code?
|
||||
Q. Can I reuse existing iOS code?
|
||||
A. Yes, React Native is designed to be extensible and allow integration of all
|
||||
sorts of native components, such as `UINavigationController` (available as
|
||||
`<NavigatorIOS>`), `MKMapView` (not available yet), or your own custom
|
||||
|
@ -6,33 +6,8 @@
|
||||
|
||||
@protocol RCTNativeModule;
|
||||
|
||||
@class RCTUIManager;
|
||||
@class RCTEventDispatcher;
|
||||
|
||||
/**
|
||||
* Functions are the one thing that aren't automatically converted to OBJC
|
||||
* blocks, according to this revert: http://trac.webkit.org/changeset/144489
|
||||
* They must be expressed as `JSValue`s.
|
||||
*
|
||||
* But storing callbacks causes reference cycles!
|
||||
* http://stackoverflow.com/questions/19202248/how-can-i-use-jsmanagedvalue-to-avoid-a-reference-cycle-without-the-jsvalue-gett
|
||||
* We'll live with the leak for now, but need to clean this up asap:
|
||||
* Passing a reference to the `context` to the bridge would make it easy to
|
||||
* execute JS. We can add `JSManagedValue`s to protect against this. The same
|
||||
* needs to be done in `RCTTiming` and friends.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Must be kept in sync with `MessageQueue.js`.
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, RCTBridgeFields) {
|
||||
RCTBridgeFieldRequestModuleIDs = 0,
|
||||
RCTBridgeFieldMethodIDs,
|
||||
RCTBridgeFieldParamss,
|
||||
RCTBridgeFieldResponseCBIDs,
|
||||
RCTBridgeFieldResponseReturnValues,
|
||||
RCTBridgeFieldFlushDateMillis
|
||||
};
|
||||
@class RCTRootView;
|
||||
|
||||
/**
|
||||
* Utilities for constructing common response objects. When sending a
|
||||
@ -59,19 +34,19 @@ static inline NSDictionary *RCTAPIErrorObject(NSString *msg)
|
||||
@interface RCTBridge : NSObject <RCTInvalidating>
|
||||
|
||||
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor
|
||||
shadowQueue:(dispatch_queue_t)shadowQueue
|
||||
javaScriptModulesConfig:(NSDictionary *)javaScriptModulesConfig;
|
||||
|
||||
- (void)enqueueJSCall:(NSUInteger)moduleID methodID:(NSUInteger)methodID args:(NSArray *)args;
|
||||
- (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete:(RCTJavaScriptCompleteBlock)onComplete;
|
||||
- (void)enqueueUpdateTimers;
|
||||
|
||||
@property (nonatomic, readonly) RCTUIManager *uiManager;
|
||||
@property (nonatomic, readonly) RCTEventDispatcher *eventDispatcher;
|
||||
@property (nonatomic, readonly) dispatch_queue_t shadowQueue;
|
||||
|
||||
// For use in implementing delegates, which may need to queue responses.
|
||||
- (RCTResponseSenderBlock)createResponseSenderBlock:(NSInteger)callbackID;
|
||||
|
||||
- (void)registerRootView:(RCTRootView *)rootView;
|
||||
|
||||
/**
|
||||
* Global logging function will print to both xcode and js debugger consoles.
|
||||
*
|
||||
|
@ -9,11 +9,34 @@
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTModuleIDs.h"
|
||||
#import "RCTTiming.h"
|
||||
#import "RCTUIManager.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
NSString *RCTModuleName(Class moduleClass)
|
||||
/**
|
||||
* Functions are the one thing that aren't automatically converted to OBJC
|
||||
* blocks, according to this revert: http://trac.webkit.org/changeset/144489
|
||||
* They must be expressed as `JSValue`s.
|
||||
*
|
||||
* But storing callbacks causes reference cycles!
|
||||
* http://stackoverflow.com/questions/19202248/how-can-i-use-jsmanagedvalue-to-avoid-a-reference-cycle-without-the-jsvalue-gett
|
||||
* We'll live with the leak for now, but need to clean this up asap:
|
||||
* Passing a reference to the `context` to the bridge would make it easy to
|
||||
* execute JS. We can add `JSManagedValue`s to protect against this. The same
|
||||
* needs to be done in `RCTTiming` and friends.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Must be kept in sync with `MessageQueue.js`.
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, RCTBridgeFields) {
|
||||
RCTBridgeFieldRequestModuleIDs = 0,
|
||||
RCTBridgeFieldMethodIDs,
|
||||
RCTBridgeFieldParamss,
|
||||
RCTBridgeFieldResponseCBIDs,
|
||||
RCTBridgeFieldResponseReturnValues,
|
||||
RCTBridgeFieldFlushDateMillis
|
||||
};
|
||||
|
||||
static NSString *RCTModuleName(Class moduleClass)
|
||||
{
|
||||
if ([moduleClass respondsToSelector:@selector(moduleName)]) {
|
||||
|
||||
@ -22,22 +45,11 @@ NSString *RCTModuleName(Class moduleClass)
|
||||
} else {
|
||||
|
||||
// Default implementation, works in most cases
|
||||
NSString *className = NSStringFromClass(moduleClass);
|
||||
|
||||
// TODO: be more consistent with naming so that this check isn't needed
|
||||
if ([moduleClass conformsToProtocol:@protocol(RCTNativeViewModule)]) {
|
||||
if ([className hasPrefix:@"RCTUI"]) {
|
||||
className = [className substringFromIndex:@"RCT".length];
|
||||
}
|
||||
if ([className hasSuffix:@"Manager"]) {
|
||||
className = [className substringToIndex:className.length - @"Manager".length];
|
||||
}
|
||||
}
|
||||
return className;
|
||||
return NSStringFromClass(moduleClass);
|
||||
}
|
||||
}
|
||||
|
||||
NSDictionary *RCTNativeModuleClasses(void)
|
||||
static NSDictionary *RCTNativeModuleClasses(void)
|
||||
{
|
||||
static NSMutableDictionary *modules;
|
||||
static dispatch_once_t onceToken;
|
||||
@ -79,46 +91,30 @@ NSDictionary *RCTNativeModuleClasses(void)
|
||||
{
|
||||
NSMutableDictionary *_moduleInstances;
|
||||
NSDictionary *_javaScriptModulesConfig;
|
||||
dispatch_queue_t _shadowQueue;
|
||||
RCTTiming *_timing;
|
||||
id<RCTJavaScriptExecutor> _javaScriptExecutor;
|
||||
}
|
||||
|
||||
static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
|
||||
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor
|
||||
shadowQueue:(dispatch_queue_t)shadowQueue
|
||||
javaScriptModulesConfig:(NSDictionary *)javaScriptModulesConfig
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_javaScriptExecutor = javaScriptExecutor;
|
||||
_latestJSExecutor = _javaScriptExecutor;
|
||||
_shadowQueue = shadowQueue;
|
||||
_eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
|
||||
|
||||
_moduleInstances = [[NSMutableDictionary alloc] init];
|
||||
|
||||
// TODO (#5906496): Remove special case
|
||||
_timing = [[RCTTiming alloc] initWithBridge:self];
|
||||
_javaScriptModulesConfig = javaScriptModulesConfig;
|
||||
_moduleInstances[RCTModuleName([RCTTiming class])] = _timing;
|
||||
|
||||
// TODO (#5906496): Remove special case
|
||||
NSMutableDictionary *viewManagers = [[NSMutableDictionary alloc] init];
|
||||
[RCTNativeModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, Class moduleClass, BOOL *stop) {
|
||||
if ([moduleClass conformsToProtocol:@protocol(RCTNativeViewModule)]) {
|
||||
viewManagers[moduleName] = [[moduleClass alloc] init];
|
||||
}
|
||||
}];
|
||||
_uiManager = [[RCTUIManager alloc] initWithShadowQueue:_shadowQueue viewManagers:viewManagers];
|
||||
_uiManager.eventDispatcher = _eventDispatcher;
|
||||
_moduleInstances[RCTModuleName([RCTUIManager class])] = _uiManager;
|
||||
[_moduleInstances addEntriesFromDictionary:viewManagers];
|
||||
|
||||
// Register remaining modules
|
||||
_shadowQueue = dispatch_queue_create("com.facebook.ReactKit.ShadowQueue", DISPATCH_QUEUE_SERIAL);
|
||||
|
||||
// Register modules
|
||||
_moduleInstances = [[NSMutableDictionary alloc] init];
|
||||
[RCTNativeModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, Class moduleClass, BOOL *stop) {
|
||||
if (_moduleInstances[moduleName] == nil) {
|
||||
_moduleInstances[moduleName] = [[moduleClass alloc] init];
|
||||
if ([moduleClass instancesRespondToSelector:@selector(initWithBridge:)]) {
|
||||
_moduleInstances[moduleName] = [[moduleClass alloc] initWithBridge:self];
|
||||
} else {
|
||||
_moduleInstances[moduleName] = [[moduleClass alloc] init];
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
@ -148,18 +144,16 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
_javaScriptExecutor = nil;
|
||||
|
||||
dispatch_sync(_shadowQueue, ^{
|
||||
// Make sure all dispatchers have been executed before
|
||||
// freeing up memory from _asyncHookMapByModuleID
|
||||
// Make sure all dispatchers have been executed before continuing
|
||||
// TODO: is this still needed?
|
||||
});
|
||||
|
||||
|
||||
for (id target in _moduleInstances.objectEnumerator) {
|
||||
if ([target respondsToSelector:@selector(invalidate)]) {
|
||||
[(id<RCTInvalidating>)target invalidate];
|
||||
}
|
||||
}
|
||||
[_moduleInstances removeAllObjects];
|
||||
|
||||
_timing = nil;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -199,11 +193,6 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)enqueueUpdateTimers
|
||||
{
|
||||
[_timing enqueueUpdateTimers];
|
||||
}
|
||||
|
||||
#pragma mark - Payload Generation
|
||||
|
||||
- (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args
|
||||
@ -274,12 +263,12 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
}
|
||||
}
|
||||
|
||||
NSArray *moduleIDs = [requestsArray objectAtIndex:RCTBridgeFieldRequestModuleIDs];
|
||||
NSArray *methodIDs = [requestsArray objectAtIndex:RCTBridgeFieldMethodIDs];
|
||||
NSArray *paramss = [requestsArray objectAtIndex:RCTBridgeFieldParamss];
|
||||
NSArray *moduleIDs = requestsArray[RCTBridgeFieldRequestModuleIDs];
|
||||
NSArray *methodIDs = requestsArray[RCTBridgeFieldMethodIDs];
|
||||
NSArray *paramsArrays = requestsArray[RCTBridgeFieldParamss];
|
||||
|
||||
NSUInteger numRequests = [moduleIDs count];
|
||||
BOOL allSame = numRequests == [methodIDs count] && numRequests == [paramss count];
|
||||
BOOL allSame = numRequests == [methodIDs count] && numRequests == [paramsArrays count];
|
||||
if (!allSame) {
|
||||
RCTLogMustFix(@"Invalid data message - all must be length: %zd", numRequests);
|
||||
return;
|
||||
@ -288,31 +277,102 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
for (NSUInteger i = 0; i < numRequests; i++) {
|
||||
@autoreleasepool {
|
||||
[self _handleRequestNumber:i
|
||||
moduleID:[moduleIDs objectAtIndex:i]
|
||||
methodID:[methodIDs objectAtIndex:i]
|
||||
params:[paramss objectAtIndex:i]];
|
||||
moduleID:[moduleIDs[i] integerValue]
|
||||
methodID:[methodIDs[i] integerValue]
|
||||
params:paramsArrays[i]];
|
||||
}
|
||||
}
|
||||
|
||||
// Update modules
|
||||
for (id target in _moduleInstances.objectEnumerator) {
|
||||
if ([target respondsToSelector:@selector(batchDidComplete)]) {
|
||||
dispatch_async(_shadowQueue, ^{
|
||||
// TODO: only used by RCTUIManager - can we eliminate this special case?
|
||||
dispatch_async(_shadowQueue, ^{
|
||||
for (id target in _moduleInstances.objectEnumerator) {
|
||||
if ([target respondsToSelector:@selector(batchDidComplete)]) {
|
||||
[target batchDidComplete];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)_handleRequestNumber:(NSUInteger)i moduleID:(id)moduleID methodID:(id)methodID params:(id)params
|
||||
- (BOOL)_handleRequestNumber:(NSUInteger)i
|
||||
moduleID:(NSInteger)moduleID
|
||||
methodID:(NSInteger)methodID
|
||||
params:(NSArray *)params
|
||||
{
|
||||
if (![moduleID isKindOfClass:[NSNumber class]] || ![methodID isKindOfClass:[NSNumber class]] || ![params isKindOfClass:[NSArray class]]) {
|
||||
if (![params isKindOfClass:[NSArray class]]) {
|
||||
RCTLogMustFix(@"Invalid module/method/params tuple for request #%zd", i);
|
||||
return;
|
||||
return NO;
|
||||
}
|
||||
[self _dispatchUsingAsyncHookMapWithModuleID:[moduleID integerValue]
|
||||
methodID:[methodID integerValue]
|
||||
params:params];
|
||||
|
||||
if (moduleID < 0 || moduleID >= RCTExportedMethodsByModule().count) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSString *moduleName = RCTExportedModuleNameAtSortedIndex(moduleID);
|
||||
NSArray *methods = RCTExportedMethodsByModule()[moduleName];
|
||||
if (methodID < 0 || methodID >= methods.count) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
RCTModuleMethod *method = methods[methodID];
|
||||
NSUInteger methodArity = method.arity;
|
||||
if (params.count != methodArity) {
|
||||
RCTLogMustFix(@"Expected %tu arguments but got %tu invoking %@.%@",
|
||||
methodArity,
|
||||
params.count,
|
||||
moduleName,
|
||||
method.JSMethodName);
|
||||
return NO;
|
||||
}
|
||||
|
||||
__weak RCTBridge *weakSelf = self;
|
||||
dispatch_async(_shadowQueue, ^{
|
||||
__strong RCTBridge *strongSelf = weakSelf;
|
||||
|
||||
if (!strongSelf.isValid) {
|
||||
// strongSelf has been invalidated since the dispatch_async call and this
|
||||
// invocation should not continue.
|
||||
return;
|
||||
}
|
||||
|
||||
NSInvocation *invocation = [RCTBridge invocationForAdditionalArguments:methodArity];
|
||||
|
||||
// TODO: we should just store module instances by index, since that's how we look them up anyway
|
||||
id target = strongSelf->_moduleInstances[moduleName];
|
||||
RCTAssert(target != nil, @"No module found for name '%@'", moduleName);
|
||||
|
||||
[invocation setArgument:&target atIndex:0];
|
||||
|
||||
SEL selector = method.selector;
|
||||
[invocation setArgument:&selector atIndex:1];
|
||||
|
||||
// Retain used blocks until after invocation completes.
|
||||
NSMutableArray *blocks = [NSMutableArray array];
|
||||
|
||||
[params enumerateObjectsUsingBlock:^(id param, NSUInteger idx, BOOL *stop) {
|
||||
if ([param isEqual:[NSNull null]]) {
|
||||
param = nil;
|
||||
} else if ([method.blockArgumentIndexes containsIndex:idx]) {
|
||||
id block = [strongSelf createResponseSenderBlock:[param integerValue]];
|
||||
[blocks addObject:block];
|
||||
param = block;
|
||||
}
|
||||
|
||||
[invocation setArgument:¶m atIndex:idx + 2];
|
||||
}];
|
||||
|
||||
@try {
|
||||
[invocation invoke];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
RCTLogMustFix(@"Exception thrown while invoking %@ on target %@ with params %@: %@", method.JSMethodName, target, params, exception);
|
||||
}
|
||||
@finally {
|
||||
// Force `blocks` to remain alive until here.
|
||||
blocks = nil;
|
||||
}
|
||||
});
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -352,84 +412,6 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
return invocation;
|
||||
}
|
||||
|
||||
- (BOOL)_dispatchUsingAsyncHookMapWithModuleID:(NSInteger)moduleID
|
||||
methodID:(NSInteger)methodID
|
||||
params:(NSArray *)params
|
||||
{
|
||||
if (moduleID < 0 || moduleID >= RCTExportedMethodsByModule().count) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSString *moduleName = RCTExportedModuleNameAtSortedIndex(moduleID);
|
||||
NSArray *methods = RCTExportedMethodsByModule()[moduleName];
|
||||
if (methodID < 0 || methodID >= methods.count) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
RCTModuleMethod *method = methods[methodID];
|
||||
NSUInteger methodArity = method.arity;
|
||||
if (params.count != methodArity) {
|
||||
RCTLogMustFix(
|
||||
@"Expected %tu arguments but got %tu invoking %@.%@",
|
||||
methodArity,
|
||||
params.count,
|
||||
moduleName,
|
||||
method.JSMethodName
|
||||
);
|
||||
return NO;
|
||||
}
|
||||
|
||||
__weak RCTBridge *weakSelf = self;
|
||||
dispatch_async(_shadowQueue, ^{
|
||||
__strong RCTBridge *strongSelf = weakSelf;
|
||||
|
||||
if (!strongSelf.isValid) {
|
||||
// strongSelf has been invalidated since the dispatch_async call and this
|
||||
// invocation should not continue.
|
||||
return;
|
||||
}
|
||||
|
||||
NSInvocation *invocation = [RCTBridge invocationForAdditionalArguments:methodArity];
|
||||
|
||||
// TODO: we should just store module instances by index, since that's how we look them up anyway
|
||||
id target = strongSelf->_moduleInstances[moduleName];
|
||||
RCTAssert(target != nil, @"No module found for name '%@'", moduleName);
|
||||
|
||||
[invocation setArgument:&target atIndex:0];
|
||||
|
||||
SEL selector = method.selector;
|
||||
[invocation setArgument:&selector atIndex:1];
|
||||
|
||||
// Retain used blocks until after invocation completes.
|
||||
NSMutableArray *blocks = [NSMutableArray array];
|
||||
|
||||
[params enumerateObjectsUsingBlock:^(id param, NSUInteger idx, BOOL *stop) {
|
||||
if ([param isEqual:[NSNull null]]) {
|
||||
param = nil;
|
||||
} else if ([method.blockArgumentIndexes containsIndex:idx]) {
|
||||
id block = [strongSelf createResponseSenderBlock:[param integerValue]];
|
||||
[blocks addObject:block];
|
||||
param = block;
|
||||
}
|
||||
|
||||
[invocation setArgument:¶m atIndex:idx + 2];
|
||||
}];
|
||||
|
||||
@try {
|
||||
[invocation invoke];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
RCTLogMustFix(@"Exception thrown while invoking %@ on target %@ with params %@: %@", method.JSMethodName, target, params, exception);
|
||||
}
|
||||
@finally {
|
||||
// Force `blocks` to remain alive until here.
|
||||
blocks = nil;
|
||||
}
|
||||
});
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)doneRegisteringModules
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
@ -456,8 +438,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
moduleConfig[@"methods"] = methods;
|
||||
|
||||
id target = [_moduleInstances objectForKey:moduleName];
|
||||
if ([target respondsToSelector:@selector(constantsToExport)] && ![target conformsToProtocol:@protocol(RCTNativeViewModule)]) {
|
||||
// TODO: find a more elegant way to handle RCTNativeViewModule constants as a special case
|
||||
if ([target respondsToSelector:@selector(constantsToExport)]) {
|
||||
moduleConfig[@"constants"] = [target constantsToExport];
|
||||
}
|
||||
moduleConfigs[moduleName] = moduleConfig;
|
||||
@ -484,6 +465,16 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)registerRootView:(RCTRootView *)rootView
|
||||
{
|
||||
// TODO: only used by RCTUIManager - can we eliminate this special case?
|
||||
for (id target in _moduleInstances.objectEnumerator) {
|
||||
if ([target respondsToSelector:@selector(registerRootView:)]) {
|
||||
[target registerRootView:rootView];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (BOOL)hasValidJSExecutor
|
||||
{
|
||||
return (_latestJSExecutor != nil && [_latestJSExecutor isValid]);
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#import "RCTLog.h"
|
||||
|
||||
@class RCTBridge;
|
||||
@class RCTSparseArray;
|
||||
@class RCTUIManager;
|
||||
|
||||
@ -42,6 +43,12 @@ typedef void (^RCTResponseSenderBlock)(NSArray *response);
|
||||
@protocol RCTNativeModule <NSObject>
|
||||
@optional
|
||||
|
||||
/**
|
||||
* Optional initializer for modules that require access
|
||||
* to bridge features, such as sending events or making JS calls
|
||||
*/
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||
|
||||
/**
|
||||
* Place this macro inside the method body of any method you want
|
||||
* to expose to JS. The optional js_name argument will be used as
|
||||
@ -74,7 +81,7 @@ _RCTExportSectionName))) static const RCTExportEntry __rct_export_entry__ = { __
|
||||
/**
|
||||
* Provides minimal interface needed to register a UIViewManager module
|
||||
*/
|
||||
@protocol RCTNativeViewModule <RCTNativeModule>
|
||||
@protocol RCTNativeViewModule <NSObject>
|
||||
|
||||
/**
|
||||
* This method instantiates a native view to be managed by the module.
|
||||
@ -83,6 +90,12 @@ _RCTExportSectionName))) static const RCTExportEntry __rct_export_entry__ = { __
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* The module name exposed to JS. If omitted, this will be inferred
|
||||
* automatically by using the view module's class name.
|
||||
*/
|
||||
+ (NSString *)moduleName;
|
||||
|
||||
/**
|
||||
* This method instantiates a shadow view to be managed by the module. If omitted,
|
||||
* an ordinary RCTShadowView instance will be created.
|
||||
@ -167,6 +180,12 @@ RCT_REMAP_VIEW_PROPERTY(name, name)
|
||||
*/
|
||||
- (NSDictionary *)customDirectEventTypes;
|
||||
|
||||
/**
|
||||
* Injects constants into JS. These constants are made accessible via
|
||||
* NativeModules.moduleName.X.
|
||||
*/
|
||||
- (NSDictionary *)constantsToExport;
|
||||
|
||||
/**
|
||||
* To deprecate, hopefully
|
||||
*/
|
||||
|
@ -14,9 +14,7 @@
|
||||
* JavaScript so that applications can clean up resources. (launch blocker).
|
||||
* TODO: Incremental module loading. (low pri).
|
||||
*/
|
||||
@interface RCTJavaScriptAppEngine : NSObject <RCTInvalidating>
|
||||
|
||||
@property (nonatomic, readonly, strong) RCTBridge *bridge;
|
||||
@interface RCTJavaScriptAppEngine : NSObject
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||
- (void)loadBundleAtURL:(NSURL *)moduleURL useCache:(BOOL)useCache onComplete:(RCTJavaScriptCompleteBlock)onComplete;
|
||||
|
@ -22,11 +22,8 @@
|
||||
*/
|
||||
@implementation RCTJavaScriptAppEngine
|
||||
{
|
||||
BOOL _isPaused; // Pauses drawing/updating of the JSView
|
||||
BOOL _pauseOnEnterBackground;
|
||||
CADisplayLink *_displayLink;
|
||||
NSTimer *_runTimer;
|
||||
NSDictionary *_loadedResource;
|
||||
__weak RCTBridge *_bridge;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
@ -54,118 +51,13 @@
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
if ((self = [super init])) {
|
||||
_bridge = bridge;
|
||||
_isPaused = NO;
|
||||
self.pauseOnEnterBackground = YES;
|
||||
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(run:)];
|
||||
if (_displayLink) {
|
||||
[_displayLink setFrameInterval:1];
|
||||
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
} else {
|
||||
RCTLogWarn(@"Failed to create a display link (probably on buildbot) - using an NSTimer for AppEngine instead.");
|
||||
_runTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / 60.0) target:self selector:@selector(run:) userInfo:nil repeats:YES];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Wait until operations on `javaScriptQueue` are complete.
|
||||
*/
|
||||
- (void)dealloc
|
||||
{
|
||||
RCTAssert(!self.valid, @"-invalidate must be called before -dealloc");
|
||||
}
|
||||
|
||||
#pragma mark - RCTInvalidating
|
||||
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return _displayLink != nil;
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
[_bridge invalidate];
|
||||
_bridge = nil;
|
||||
|
||||
[_displayLink invalidate];
|
||||
_displayLink = nil;
|
||||
|
||||
// Remove from notification center
|
||||
self.pauseOnEnterBackground = NO;
|
||||
}
|
||||
|
||||
#pragma mark - Run loop
|
||||
|
||||
- (void)run:(CADisplayLink *)sender
|
||||
{
|
||||
if (!_isPaused) {
|
||||
RCTAssertMainThread();
|
||||
[_bridge enqueueUpdateTimers];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)pauseRunLoop
|
||||
{
|
||||
if (!_isPaused) {
|
||||
[_displayLink removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
_isPaused = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resumeRunLoop
|
||||
{
|
||||
if (_isPaused) {
|
||||
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
_isPaused = NO;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See warnings from lint: UIApplicationDidBecomeActive fires in a critical
|
||||
* foreground path, and prevents the app from prioritizing the foreground
|
||||
* processing. Consider using
|
||||
* FBApplicationDidFinishEnteringForegroundAndIsNowIdleNotification.
|
||||
*/
|
||||
- (void)setPauseOnEnterBackground:(BOOL)pauses
|
||||
{
|
||||
NSArray *pauseN = @[
|
||||
UIApplicationWillResignActiveNotification,
|
||||
UIApplicationDidEnterBackgroundNotification,
|
||||
UIApplicationWillTerminateNotification
|
||||
];
|
||||
NSArray *resumeN =
|
||||
@[UIApplicationWillEnterForegroundNotification, UIApplicationDidBecomeActiveNotification];
|
||||
|
||||
if (pauses) {
|
||||
[self observeKeyPaths:pauseN selector:@selector(pauseRunLoop)];
|
||||
[self observeKeyPaths:resumeN selector:@selector(resumeRunLoop)];
|
||||
}
|
||||
else {
|
||||
[self removeObserverForKeyPaths:pauseN];
|
||||
[self removeObserverForKeyPaths:resumeN];
|
||||
}
|
||||
_pauseOnEnterBackground = pauses;
|
||||
}
|
||||
|
||||
- (void)removeObserverForKeyPaths:(NSArray*)keyPaths
|
||||
{
|
||||
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
||||
for (NSString *name in keyPaths) {
|
||||
[nc removeObserver:self name:name object:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)observeKeyPaths:(NSArray*)keyPaths selector:(SEL)selector
|
||||
{
|
||||
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
||||
for (NSString *name in keyPaths) {
|
||||
[nc addObserver:self selector:selector name:name object:nil];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Module and script loading
|
||||
|
||||
+ (void)resetCacheForBundleAtURL:(NSURL *)moduleURL
|
||||
|
@ -18,7 +18,6 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
|
||||
|
||||
@implementation RCTRootView
|
||||
{
|
||||
dispatch_queue_t _shadowQueue;
|
||||
RCTBridge *_bridge;
|
||||
RCTJavaScriptAppEngine *_appEngine;
|
||||
RCTTouchHandler *_touchHandler;
|
||||
@ -62,9 +61,6 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
// TODO: does it make sense to do this here? What if there's more than one host view?
|
||||
_shadowQueue = dispatch_queue_create("com.facebook.ReactKit.ShadowQueue", DISPATCH_QUEUE_SERIAL);
|
||||
|
||||
// Every root view that is created must have a unique react tag.
|
||||
// Numbering of these tags goes from 1, 11, 21, 31, etc
|
||||
static NSInteger rootViewTag = 1;
|
||||
@ -95,16 +91,16 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
|
||||
}
|
||||
} else {
|
||||
|
||||
[_bridge.uiManager registerRootView:self];
|
||||
[_bridge registerRootView:self];
|
||||
|
||||
NSString *moduleName = _moduleName ?: @"";
|
||||
NSDictionary *appParameters = @{
|
||||
@"rootTag": self.reactTag ?: @0,
|
||||
@"initialProps": self.initialProperties ?: @{},
|
||||
};
|
||||
[_appEngine.bridge enqueueJSCall:RCTModuleIDBundler
|
||||
methodID:RCTBundlerRunApplication
|
||||
args:@[moduleName, appParameters]];
|
||||
[_bridge enqueueJSCall:RCTModuleIDBundler
|
||||
methodID:RCTBundlerRunApplication
|
||||
args:@[moduleName, appParameters]];
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,12 +121,10 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
|
||||
};
|
||||
|
||||
[_executor invalidate];
|
||||
[_appEngine invalidate];
|
||||
[_bridge invalidate];
|
||||
|
||||
_executor = [[RCTContextExecutor alloc] init];
|
||||
_bridge = [[RCTBridge alloc] initWithJavaScriptExecutor:_executor
|
||||
shadowQueue:_shadowQueue
|
||||
javaScriptModulesConfig:[RCTModuleIDs config]];
|
||||
|
||||
_appEngine = [[RCTJavaScriptAppEngine alloc] initWithBridge:_bridge];
|
||||
|
@ -3,12 +3,12 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "RCTExport.h"
|
||||
#import "RCTInvalidating.h"
|
||||
|
||||
@class RCTBridge;
|
||||
|
||||
@interface RCTTiming : NSObject <RCTNativeModule>
|
||||
@interface RCTTiming : NSObject <RCTNativeModule, RCTInvalidating>
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||
- (void)enqueueUpdateTimers;
|
||||
|
||||
@end
|
||||
|
@ -11,7 +11,6 @@
|
||||
@interface RCTTimer : NSObject
|
||||
|
||||
@property (nonatomic, strong, readonly) NSDate *target;
|
||||
@property (nonatomic, assign, readonly, getter=isActive) BOOL active;
|
||||
@property (nonatomic, assign, readonly) BOOL repeats;
|
||||
@property (nonatomic, strong, readonly) NSNumber *callbackID;
|
||||
@property (nonatomic, assign, readonly) NSTimeInterval interval;
|
||||
@ -26,7 +25,6 @@
|
||||
repeats:(BOOL)repeats
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_active = YES;
|
||||
_interval = interval;
|
||||
_repeats = repeats;
|
||||
_callbackID = callbackID;
|
||||
@ -40,13 +38,9 @@
|
||||
*/
|
||||
- (BOOL)updateFoundNeedsJSUpdate
|
||||
{
|
||||
if (_active && _target.timeIntervalSinceNow <= 0) {
|
||||
if (_target && _target.timeIntervalSinceNow <= 0) {
|
||||
// The JS Timers will do fine grained calculating of expired timeouts.
|
||||
if (_repeats) {
|
||||
_target = [NSDate dateWithTimeIntervalSinceNow:_interval];
|
||||
} else {
|
||||
_active = NO;
|
||||
}
|
||||
_target = _repeats ? [NSDate dateWithTimeIntervalSinceNow:_interval] : nil;
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
@ -58,6 +52,7 @@
|
||||
{
|
||||
RCTSparseArray *_timers;
|
||||
RCTBridge *_bridge;
|
||||
id _updateTimer;
|
||||
}
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
@ -65,19 +60,74 @@
|
||||
if ((self = [super init])) {
|
||||
_bridge = bridge;
|
||||
_timers = [[RCTSparseArray alloc] init];
|
||||
[self startTimers];
|
||||
|
||||
for (NSString *name in @[UIApplicationWillResignActiveNotification,
|
||||
UIApplicationDidEnterBackgroundNotification,
|
||||
UIApplicationWillTerminateNotification]) {
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(stopTimers)
|
||||
name:name
|
||||
object:nil];
|
||||
}
|
||||
|
||||
for (NSString *name in @[UIApplicationDidBecomeActiveNotification,
|
||||
UIApplicationWillEnterForegroundNotification]) {
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(startTimers)
|
||||
name:name
|
||||
object:nil];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO (#5906496): Wait until operations on `javaScriptQueue` are complete to complete the
|
||||
* `dealloc`.
|
||||
*/
|
||||
/* - (void)dealloc
|
||||
{
|
||||
} */
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)enqueueUpdateTimers
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return _bridge != nil;
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
[self stopTimers];
|
||||
_bridge = nil;
|
||||
}
|
||||
|
||||
- (void)stopTimers
|
||||
{
|
||||
[_updateTimer invalidate];
|
||||
_updateTimer = nil;
|
||||
}
|
||||
|
||||
- (void)startTimers
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
if (![self isValid] || _updateTimer != nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
_updateTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];
|
||||
if (_updateTimer) {
|
||||
[_updateTimer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
} else {
|
||||
RCTLogWarn(@"Failed to create a display link (probably on buildbot) - using an NSTimer for AppEngine instead.");
|
||||
_updateTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / 60)
|
||||
target:self
|
||||
selector:@selector(update)
|
||||
userInfo:nil
|
||||
repeats:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)update
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
@ -86,7 +136,7 @@
|
||||
if ([timer updateFoundNeedsJSUpdate]) {
|
||||
[timersToCall addObject:timer.callbackID];
|
||||
}
|
||||
if (!timer.active) {
|
||||
if (!timer.target) {
|
||||
_timers[timer.callbackID] = nil;
|
||||
}
|
||||
}
|
||||
@ -97,14 +147,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scheduleCallbackID:(NSNumber *)callbackID interval:(NSTimeInterval)interval targetTime:(NSTimeInterval)targetTime repeats:(BOOL)repeats
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
RCTTimer *timer = [[RCTTimer alloc] initWithCallbackID:callbackID interval:interval targetTime:targetTime repeats:repeats];
|
||||
_timers[callbackID] = timer;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* There's a small difference between the time when we call
|
||||
* setTimeout/setInterval/requestAnimation frame and the time it actually makes
|
||||
@ -132,10 +174,13 @@
|
||||
interval = 0;
|
||||
}
|
||||
|
||||
[self scheduleCallbackID:callbackID
|
||||
interval:interval
|
||||
targetTime:targetTime
|
||||
repeats:repeats.boolValue];
|
||||
RCTTimer *timer = [[RCTTimer alloc] initWithCallbackID:callbackID
|
||||
interval:interval
|
||||
targetTime:targetTime
|
||||
repeats:repeats];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
_timers[callbackID] = timer;
|
||||
});
|
||||
}
|
||||
|
||||
- (void)deleteTimer:(NSNumber *)timerID
|
||||
|
@ -5,7 +5,7 @@
|
||||
#import "RCTExport.h"
|
||||
#import "RCTInvalidating.h"
|
||||
|
||||
@class RCTAnimationRegistry;
|
||||
@class RCTBridge;
|
||||
@class RCTRootView;
|
||||
@class RCTShadowView;
|
||||
|
||||
@ -14,13 +14,10 @@
|
||||
|
||||
@interface RCTUIManager : NSObject <RCTInvalidating, RCTNativeModule>
|
||||
|
||||
- (instancetype)initWithShadowQueue:(dispatch_queue_t)shadowQueue
|
||||
viewManagers:(NSDictionary *)viewManagers;
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||
|
||||
@property (nonatomic, strong) RCTEventDispatcher *eventDispatcher;
|
||||
@property (nonatomic, strong) RCTSparseArray *shadowViewRegistry;
|
||||
@property (nonatomic, strong) RCTSparseArray *viewRegistry;
|
||||
@property (nonatomic, strong) RCTAnimationRegistry *animationRegistry;
|
||||
@property (nonatomic, weak) id<RCTScrollableProtocol> mainScrollView;
|
||||
|
||||
/**
|
||||
|
@ -34,6 +34,64 @@ static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_b
|
||||
}
|
||||
}
|
||||
|
||||
static NSString *RCTModuleName(Class moduleClass)
|
||||
{
|
||||
if ([moduleClass respondsToSelector:@selector(moduleName)]) {
|
||||
|
||||
return [moduleClass moduleName];
|
||||
|
||||
} else {
|
||||
|
||||
// Default implementation, works in most cases
|
||||
NSString *className = NSStringFromClass(moduleClass);
|
||||
if ([className hasPrefix:@"RCTUI"]) {
|
||||
className = [className substringFromIndex:@"RCT".length];
|
||||
}
|
||||
if ([className hasSuffix:@"Manager"]) {
|
||||
className = [className substringToIndex:className.length - @"Manager".length];
|
||||
}
|
||||
return className;
|
||||
}
|
||||
}
|
||||
|
||||
static NSDictionary *RCTViewModuleClasses(void)
|
||||
{
|
||||
static NSMutableDictionary *modules;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
modules = [NSMutableDictionary dictionary];
|
||||
|
||||
unsigned int classCount;
|
||||
Class *classes = objc_copyClassList(&classCount);
|
||||
for (unsigned int i = 0; i < classCount; i++) {
|
||||
|
||||
Class cls = classes[i];
|
||||
|
||||
if (!class_getSuperclass(cls)) {
|
||||
// Class has no superclass - it's probably something weird
|
||||
continue;
|
||||
}
|
||||
|
||||
if (![cls conformsToProtocol:@protocol(RCTNativeViewModule)]) {
|
||||
// Not an RCTNativeModule
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get module name
|
||||
NSString *moduleName = RCTModuleName(cls);
|
||||
|
||||
// Check module name is unique
|
||||
id existingClass = modules[moduleName];
|
||||
RCTCAssert(existingClass == Nil, @"Attempted to register RCTNativeViewModule class %@ for the name '%@', but name was already registered by class %@", cls, moduleName, existingClass);
|
||||
modules[moduleName] = cls;
|
||||
}
|
||||
|
||||
free(classes);
|
||||
});
|
||||
|
||||
return modules;
|
||||
}
|
||||
|
||||
@implementation RCTUIManager
|
||||
{
|
||||
// Root views are only mutated on the shadow queue
|
||||
@ -42,13 +100,14 @@ static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_b
|
||||
NSMutableArray *_pendingUIBlocks;
|
||||
|
||||
pthread_mutex_t _pendingUIBlocksMutex;
|
||||
dispatch_queue_t _shadowQueue;
|
||||
NSDictionary *_nextLayoutAnimationConfig; // RCT thread only
|
||||
RCTResponseSenderBlock _nextLayoutAnimationCallback; // RCT thread only
|
||||
RCTResponseSenderBlock _layoutAnimationCallbackMT; // Main thread only
|
||||
|
||||
NSMutableDictionary *_defaultShadowViews;
|
||||
NSMutableDictionary *_defaultViews;
|
||||
|
||||
__weak RCTBridge *_bridge;
|
||||
}
|
||||
|
||||
- (id <RCTNativeViewModule>)_managerInstanceForViewWithModuleName:(NSString *)moduleName
|
||||
@ -62,17 +121,23 @@ static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_b
|
||||
return managerInstance;
|
||||
}
|
||||
|
||||
- (instancetype)initWithShadowQueue:(dispatch_queue_t)shadowQueue
|
||||
viewManagers:(NSDictionary *)viewManagers
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
|
||||
_bridge = bridge;
|
||||
pthread_mutex_init(&_pendingUIBlocksMutex, NULL);
|
||||
|
||||
// Instantiate view managers
|
||||
NSMutableDictionary *viewManagers = [[NSMutableDictionary alloc] init];
|
||||
[RCTViewModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, Class moduleClass, BOOL *stop) {
|
||||
viewManagers[moduleName] = [[moduleClass alloc] init];
|
||||
}];
|
||||
_viewManagers = viewManagers;
|
||||
|
||||
_viewRegistry = [[RCTSparseArray alloc] init];
|
||||
_shadowViewRegistry = [[RCTSparseArray alloc] init];
|
||||
_shadowQueue = shadowQueue;
|
||||
pthread_mutex_init(&_pendingUIBlocksMutex, NULL);
|
||||
|
||||
|
||||
// Internal resources
|
||||
_pendingUIBlocks = [[NSMutableArray alloc] init];
|
||||
_rootViewTags = [[NSMutableSet alloc] init];
|
||||
@ -101,6 +166,8 @@ static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_b
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
_viewRegistry = nil;
|
||||
_shadowViewRegistry = nil;
|
||||
|
||||
@ -111,6 +178,8 @@ static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_b
|
||||
|
||||
- (void)registerRootView:(RCTRootView *)rootView;
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
NSNumber *reactTag = rootView.reactTag;
|
||||
UIView *existingView = _viewRegistry[reactTag];
|
||||
RCTCAssert(existingView == nil || existingView == rootView,
|
||||
@ -121,7 +190,7 @@ static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_b
|
||||
CGRect frame = rootView.frame;
|
||||
|
||||
// Register shadow view
|
||||
dispatch_async(_shadowQueue, ^{
|
||||
dispatch_async(_bridge.shadowQueue, ^{
|
||||
|
||||
RCTShadowView *shadowView = [[RCTShadowView alloc] init];
|
||||
shadowView.reactTag = reactTag;
|
||||
@ -468,7 +537,7 @@ static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_b
|
||||
|
||||
- (UIView *)viewForViewManager:(id <RCTNativeViewModule>)manager
|
||||
{
|
||||
return [manager viewWithEventDispatcher:_eventDispatcher];
|
||||
return [manager viewWithEventDispatcher:_bridge.eventDispatcher];
|
||||
}
|
||||
|
||||
static BOOL RCTCallPropertySetter(SEL setter, id value, id view, id defaultView, id <RCTNativeViewModule>manager)
|
||||
@ -610,25 +679,25 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||
// First copy the previous blocks into a temporary variable, then reset the
|
||||
// pending blocks to a new array. This guards against mutation while
|
||||
// processing the pending blocks in another thread.
|
||||
|
||||
|
||||
for (id <RCTNativeViewModule>viewManager in _viewManagers.allValues) {
|
||||
RCTViewManagerUIBlock uiBlock = [viewManager respondsToSelector:@selector(uiBlockToAmendWithShadowViewRegistry:)] ? [viewManager uiBlockToAmendWithShadowViewRegistry:_shadowViewRegistry] : nil;
|
||||
if (uiBlock != nil) {
|
||||
[self addUIBlock:uiBlock];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (NSNumber *reactTag in _rootViewTags) {
|
||||
RCTShadowView *rootView = _shadowViewRegistry[reactTag];
|
||||
[self addUIBlock:[self uiBlockWithLayoutUpdateForRootView:rootView]];
|
||||
[self _amendPendingUIBlocksWithStylePropagationUpdateForRootView:rootView];
|
||||
}
|
||||
|
||||
|
||||
pthread_mutex_lock(&_pendingUIBlocksMutex);
|
||||
NSArray *previousPendingUIBlocks = _pendingUIBlocks;
|
||||
_pendingUIBlocks = [[NSMutableArray alloc] init];
|
||||
pthread_mutex_unlock(&_pendingUIBlocksMutex);
|
||||
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
for (dispatch_block_t block in previousPendingUIBlocks) {
|
||||
block();
|
||||
|
Loading…
x
Reference in New Issue
Block a user