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
|
# 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
|
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.
|
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.
|
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
|
We are looking forward to the community helping us target other platforms as
|
||||||
well :)
|
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
|
A. Copy the entire `Examples/TicTacToe` folder, rename stuff in Xcode, and
|
||||||
replace the `TicTacToeApp.js` with your own. Then, in `AppDelegate.m`, update
|
replace the `TicTacToeApp.js` with your own. Then, in `AppDelegate.m`, update
|
||||||
`moduleName` to match your call to
|
`moduleName` to match your call to
|
||||||
`Bundler.registerComponent(<moduleName>, <componentName>)` at the bottom of your
|
`Bundler.registerComponent(<moduleName>, <componentName>)` at the bottom of your
|
||||||
JS file, and update `jsCodeLocation` to match your JS file name and location.
|
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
|
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.
|
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
|
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
|
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
|
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
|
`AppDelegate.m` to point to that file and deploy to your device like you would
|
||||||
any other app.
|
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
|
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
|
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.
|
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
|
A. No, we just use the JavaScriptCore public API that is part of iOS 7 and
|
||||||
later.
|
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
|
A. React Native is designed to be extensible - come talk to us, we would love to
|
||||||
work with you.
|
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
|
A. Yes, React Native is designed to be extensible and allow integration of all
|
||||||
sorts of native components, such as `UINavigationController` (available as
|
sorts of native components, such as `UINavigationController` (available as
|
||||||
`<NavigatorIOS>`), `MKMapView` (not available yet), or your own custom
|
`<NavigatorIOS>`), `MKMapView` (not available yet), or your own custom
|
||||||
|
|
|
@ -6,33 +6,8 @@
|
||||||
|
|
||||||
@protocol RCTNativeModule;
|
@protocol RCTNativeModule;
|
||||||
|
|
||||||
@class RCTUIManager;
|
|
||||||
@class RCTEventDispatcher;
|
@class RCTEventDispatcher;
|
||||||
|
@class RCTRootView;
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utilities for constructing common response objects. When sending a
|
* Utilities for constructing common response objects. When sending a
|
||||||
|
@ -59,19 +34,19 @@ static inline NSDictionary *RCTAPIErrorObject(NSString *msg)
|
||||||
@interface RCTBridge : NSObject <RCTInvalidating>
|
@interface RCTBridge : NSObject <RCTInvalidating>
|
||||||
|
|
||||||
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor
|
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor
|
||||||
shadowQueue:(dispatch_queue_t)shadowQueue
|
|
||||||
javaScriptModulesConfig:(NSDictionary *)javaScriptModulesConfig;
|
javaScriptModulesConfig:(NSDictionary *)javaScriptModulesConfig;
|
||||||
|
|
||||||
- (void)enqueueJSCall:(NSUInteger)moduleID methodID:(NSUInteger)methodID args:(NSArray *)args;
|
- (void)enqueueJSCall:(NSUInteger)moduleID methodID:(NSUInteger)methodID args:(NSArray *)args;
|
||||||
- (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete:(RCTJavaScriptCompleteBlock)onComplete;
|
- (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) RCTEventDispatcher *eventDispatcher;
|
||||||
|
@property (nonatomic, readonly) dispatch_queue_t shadowQueue;
|
||||||
|
|
||||||
// For use in implementing delegates, which may need to queue responses.
|
// For use in implementing delegates, which may need to queue responses.
|
||||||
- (RCTResponseSenderBlock)createResponseSenderBlock:(NSInteger)callbackID;
|
- (RCTResponseSenderBlock)createResponseSenderBlock:(NSInteger)callbackID;
|
||||||
|
|
||||||
|
- (void)registerRootView:(RCTRootView *)rootView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global logging function will print to both xcode and js debugger consoles.
|
* Global logging function will print to both xcode and js debugger consoles.
|
||||||
*
|
*
|
||||||
|
|
|
@ -9,11 +9,34 @@
|
||||||
#import "RCTEventDispatcher.h"
|
#import "RCTEventDispatcher.h"
|
||||||
#import "RCTLog.h"
|
#import "RCTLog.h"
|
||||||
#import "RCTModuleIDs.h"
|
#import "RCTModuleIDs.h"
|
||||||
#import "RCTTiming.h"
|
|
||||||
#import "RCTUIManager.h"
|
|
||||||
#import "RCTUtils.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)]) {
|
if ([moduleClass respondsToSelector:@selector(moduleName)]) {
|
||||||
|
|
||||||
|
@ -22,22 +45,11 @@ NSString *RCTModuleName(Class moduleClass)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Default implementation, works in most cases
|
// Default implementation, works in most cases
|
||||||
NSString *className = NSStringFromClass(moduleClass);
|
return 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NSDictionary *RCTNativeModuleClasses(void)
|
static NSDictionary *RCTNativeModuleClasses(void)
|
||||||
{
|
{
|
||||||
static NSMutableDictionary *modules;
|
static NSMutableDictionary *modules;
|
||||||
static dispatch_once_t onceToken;
|
static dispatch_once_t onceToken;
|
||||||
|
@ -79,46 +91,30 @@ NSDictionary *RCTNativeModuleClasses(void)
|
||||||
{
|
{
|
||||||
NSMutableDictionary *_moduleInstances;
|
NSMutableDictionary *_moduleInstances;
|
||||||
NSDictionary *_javaScriptModulesConfig;
|
NSDictionary *_javaScriptModulesConfig;
|
||||||
dispatch_queue_t _shadowQueue;
|
|
||||||
RCTTiming *_timing;
|
|
||||||
id<RCTJavaScriptExecutor> _javaScriptExecutor;
|
id<RCTJavaScriptExecutor> _javaScriptExecutor;
|
||||||
}
|
}
|
||||||
|
|
||||||
static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
|
|
||||||
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor
|
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor
|
||||||
shadowQueue:(dispatch_queue_t)shadowQueue
|
|
||||||
javaScriptModulesConfig:(NSDictionary *)javaScriptModulesConfig
|
javaScriptModulesConfig:(NSDictionary *)javaScriptModulesConfig
|
||||||
{
|
{
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
_javaScriptExecutor = javaScriptExecutor;
|
_javaScriptExecutor = javaScriptExecutor;
|
||||||
_latestJSExecutor = _javaScriptExecutor;
|
_latestJSExecutor = _javaScriptExecutor;
|
||||||
_shadowQueue = shadowQueue;
|
|
||||||
_eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
|
_eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
|
||||||
|
|
||||||
_moduleInstances = [[NSMutableDictionary alloc] init];
|
|
||||||
|
|
||||||
// TODO (#5906496): Remove special case
|
|
||||||
_timing = [[RCTTiming alloc] initWithBridge:self];
|
|
||||||
_javaScriptModulesConfig = javaScriptModulesConfig;
|
_javaScriptModulesConfig = javaScriptModulesConfig;
|
||||||
_moduleInstances[RCTModuleName([RCTTiming class])] = _timing;
|
_shadowQueue = dispatch_queue_create("com.facebook.ReactKit.ShadowQueue", DISPATCH_QUEUE_SERIAL);
|
||||||
|
|
||||||
// TODO (#5906496): Remove special case
|
// Register modules
|
||||||
NSMutableDictionary *viewManagers = [[NSMutableDictionary alloc] init];
|
_moduleInstances = [[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
|
|
||||||
[RCTNativeModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, Class moduleClass, BOOL *stop) {
|
[RCTNativeModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, Class moduleClass, BOOL *stop) {
|
||||||
if (_moduleInstances[moduleName] == nil) {
|
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;
|
_javaScriptExecutor = nil;
|
||||||
|
|
||||||
dispatch_sync(_shadowQueue, ^{
|
dispatch_sync(_shadowQueue, ^{
|
||||||
// Make sure all dispatchers have been executed before
|
// Make sure all dispatchers have been executed before continuing
|
||||||
// freeing up memory from _asyncHookMapByModuleID
|
// TODO: is this still needed?
|
||||||
});
|
});
|
||||||
|
|
||||||
for (id target in _moduleInstances.objectEnumerator) {
|
for (id target in _moduleInstances.objectEnumerator) {
|
||||||
if ([target respondsToSelector:@selector(invalidate)]) {
|
if ([target respondsToSelector:@selector(invalidate)]) {
|
||||||
[(id<RCTInvalidating>)target invalidate];
|
[(id<RCTInvalidating>)target invalidate];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[_moduleInstances removeAllObjects];
|
[_moduleInstances removeAllObjects];
|
||||||
|
|
||||||
_timing = nil;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -199,11 +193,6 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)enqueueUpdateTimers
|
|
||||||
{
|
|
||||||
[_timing enqueueUpdateTimers];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Payload Generation
|
#pragma mark - Payload Generation
|
||||||
|
|
||||||
- (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args
|
- (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args
|
||||||
|
@ -274,12 +263,12 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NSArray *moduleIDs = [requestsArray objectAtIndex:RCTBridgeFieldRequestModuleIDs];
|
NSArray *moduleIDs = requestsArray[RCTBridgeFieldRequestModuleIDs];
|
||||||
NSArray *methodIDs = [requestsArray objectAtIndex:RCTBridgeFieldMethodIDs];
|
NSArray *methodIDs = requestsArray[RCTBridgeFieldMethodIDs];
|
||||||
NSArray *paramss = [requestsArray objectAtIndex:RCTBridgeFieldParamss];
|
NSArray *paramsArrays = requestsArray[RCTBridgeFieldParamss];
|
||||||
|
|
||||||
NSUInteger numRequests = [moduleIDs count];
|
NSUInteger numRequests = [moduleIDs count];
|
||||||
BOOL allSame = numRequests == [methodIDs count] && numRequests == [paramss count];
|
BOOL allSame = numRequests == [methodIDs count] && numRequests == [paramsArrays count];
|
||||||
if (!allSame) {
|
if (!allSame) {
|
||||||
RCTLogMustFix(@"Invalid data message - all must be length: %zd", numRequests);
|
RCTLogMustFix(@"Invalid data message - all must be length: %zd", numRequests);
|
||||||
return;
|
return;
|
||||||
|
@ -288,31 +277,102 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
for (NSUInteger i = 0; i < numRequests; i++) {
|
for (NSUInteger i = 0; i < numRequests; i++) {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
[self _handleRequestNumber:i
|
[self _handleRequestNumber:i
|
||||||
moduleID:[moduleIDs objectAtIndex:i]
|
moduleID:[moduleIDs[i] integerValue]
|
||||||
methodID:[methodIDs objectAtIndex:i]
|
methodID:[methodIDs[i] integerValue]
|
||||||
params:[paramss objectAtIndex:i]];
|
params:paramsArrays[i]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update modules
|
// TODO: only used by RCTUIManager - can we eliminate this special case?
|
||||||
for (id target in _moduleInstances.objectEnumerator) {
|
dispatch_async(_shadowQueue, ^{
|
||||||
if ([target respondsToSelector:@selector(batchDidComplete)]) {
|
for (id target in _moduleInstances.objectEnumerator) {
|
||||||
dispatch_async(_shadowQueue, ^{
|
if ([target respondsToSelector:@selector(batchDidComplete)]) {
|
||||||
[target 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);
|
RCTLogMustFix(@"Invalid module/method/params tuple for request #%zd", i);
|
||||||
return;
|
return NO;
|
||||||
}
|
}
|
||||||
[self _dispatchUsingAsyncHookMapWithModuleID:[moduleID integerValue]
|
|
||||||
methodID:[methodID integerValue]
|
if (moduleID < 0 || moduleID >= RCTExportedMethodsByModule().count) {
|
||||||
params:params];
|
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;
|
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
|
- (void)doneRegisteringModules
|
||||||
{
|
{
|
||||||
RCTAssertMainThread();
|
RCTAssertMainThread();
|
||||||
|
@ -456,8 +438,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
moduleConfig[@"methods"] = methods;
|
moduleConfig[@"methods"] = methods;
|
||||||
|
|
||||||
id target = [_moduleInstances objectForKey:moduleName];
|
id target = [_moduleInstances objectForKey:moduleName];
|
||||||
if ([target respondsToSelector:@selector(constantsToExport)] && ![target conformsToProtocol:@protocol(RCTNativeViewModule)]) {
|
if ([target respondsToSelector:@selector(constantsToExport)]) {
|
||||||
// TODO: find a more elegant way to handle RCTNativeViewModule constants as a special case
|
|
||||||
moduleConfig[@"constants"] = [target constantsToExport];
|
moduleConfig[@"constants"] = [target constantsToExport];
|
||||||
}
|
}
|
||||||
moduleConfigs[moduleName] = moduleConfig;
|
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
|
+ (BOOL)hasValidJSExecutor
|
||||||
{
|
{
|
||||||
return (_latestJSExecutor != nil && [_latestJSExecutor isValid]);
|
return (_latestJSExecutor != nil && [_latestJSExecutor isValid]);
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#import "RCTLog.h"
|
#import "RCTLog.h"
|
||||||
|
|
||||||
|
@class RCTBridge;
|
||||||
@class RCTSparseArray;
|
@class RCTSparseArray;
|
||||||
@class RCTUIManager;
|
@class RCTUIManager;
|
||||||
|
|
||||||
|
@ -42,6 +43,12 @@ typedef void (^RCTResponseSenderBlock)(NSArray *response);
|
||||||
@protocol RCTNativeModule <NSObject>
|
@protocol RCTNativeModule <NSObject>
|
||||||
@optional
|
@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
|
* 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
|
* 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
|
* 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.
|
* This method instantiates a native view to be managed by the module.
|
||||||
|
@ -83,6 +90,12 @@ _RCTExportSectionName))) static const RCTExportEntry __rct_export_entry__ = { __
|
||||||
|
|
||||||
@optional
|
@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,
|
* This method instantiates a shadow view to be managed by the module. If omitted,
|
||||||
* an ordinary RCTShadowView instance will be created.
|
* an ordinary RCTShadowView instance will be created.
|
||||||
|
@ -167,6 +180,12 @@ RCT_REMAP_VIEW_PROPERTY(name, name)
|
||||||
*/
|
*/
|
||||||
- (NSDictionary *)customDirectEventTypes;
|
- (NSDictionary *)customDirectEventTypes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injects constants into JS. These constants are made accessible via
|
||||||
|
* NativeModules.moduleName.X.
|
||||||
|
*/
|
||||||
|
- (NSDictionary *)constantsToExport;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To deprecate, hopefully
|
* To deprecate, hopefully
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -14,9 +14,7 @@
|
||||||
* JavaScript so that applications can clean up resources. (launch blocker).
|
* JavaScript so that applications can clean up resources. (launch blocker).
|
||||||
* TODO: Incremental module loading. (low pri).
|
* TODO: Incremental module loading. (low pri).
|
||||||
*/
|
*/
|
||||||
@interface RCTJavaScriptAppEngine : NSObject <RCTInvalidating>
|
@interface RCTJavaScriptAppEngine : NSObject
|
||||||
|
|
||||||
@property (nonatomic, readonly, strong) RCTBridge *bridge;
|
|
||||||
|
|
||||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||||
- (void)loadBundleAtURL:(NSURL *)moduleURL useCache:(BOOL)useCache onComplete:(RCTJavaScriptCompleteBlock)onComplete;
|
- (void)loadBundleAtURL:(NSURL *)moduleURL useCache:(BOOL)useCache onComplete:(RCTJavaScriptCompleteBlock)onComplete;
|
||||||
|
|
|
@ -22,11 +22,8 @@
|
||||||
*/
|
*/
|
||||||
@implementation RCTJavaScriptAppEngine
|
@implementation RCTJavaScriptAppEngine
|
||||||
{
|
{
|
||||||
BOOL _isPaused; // Pauses drawing/updating of the JSView
|
|
||||||
BOOL _pauseOnEnterBackground;
|
|
||||||
CADisplayLink *_displayLink;
|
|
||||||
NSTimer *_runTimer;
|
|
||||||
NSDictionary *_loadedResource;
|
NSDictionary *_loadedResource;
|
||||||
|
__weak RCTBridge *_bridge;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)init
|
- (instancetype)init
|
||||||
|
@ -54,118 +51,13 @@
|
||||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||||
{
|
{
|
||||||
RCTAssertMainThread();
|
RCTAssertMainThread();
|
||||||
|
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
_bridge = bridge;
|
_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;
|
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
|
#pragma mark - Module and script loading
|
||||||
|
|
||||||
+ (void)resetCacheForBundleAtURL:(NSURL *)moduleURL
|
+ (void)resetCacheForBundleAtURL:(NSURL *)moduleURL
|
||||||
|
|
|
@ -18,7 +18,6 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
|
||||||
|
|
||||||
@implementation RCTRootView
|
@implementation RCTRootView
|
||||||
{
|
{
|
||||||
dispatch_queue_t _shadowQueue;
|
|
||||||
RCTBridge *_bridge;
|
RCTBridge *_bridge;
|
||||||
RCTJavaScriptAppEngine *_appEngine;
|
RCTJavaScriptAppEngine *_appEngine;
|
||||||
RCTTouchHandler *_touchHandler;
|
RCTTouchHandler *_touchHandler;
|
||||||
|
@ -62,9 +61,6 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
|
||||||
|
|
||||||
- (void)setUp
|
- (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.
|
// Every root view that is created must have a unique react tag.
|
||||||
// Numbering of these tags goes from 1, 11, 21, 31, etc
|
// Numbering of these tags goes from 1, 11, 21, 31, etc
|
||||||
static NSInteger rootViewTag = 1;
|
static NSInteger rootViewTag = 1;
|
||||||
|
@ -95,16 +91,16 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
[_bridge.uiManager registerRootView:self];
|
[_bridge registerRootView:self];
|
||||||
|
|
||||||
NSString *moduleName = _moduleName ?: @"";
|
NSString *moduleName = _moduleName ?: @"";
|
||||||
NSDictionary *appParameters = @{
|
NSDictionary *appParameters = @{
|
||||||
@"rootTag": self.reactTag ?: @0,
|
@"rootTag": self.reactTag ?: @0,
|
||||||
@"initialProps": self.initialProperties ?: @{},
|
@"initialProps": self.initialProperties ?: @{},
|
||||||
};
|
};
|
||||||
[_appEngine.bridge enqueueJSCall:RCTModuleIDBundler
|
[_bridge enqueueJSCall:RCTModuleIDBundler
|
||||||
methodID:RCTBundlerRunApplication
|
methodID:RCTBundlerRunApplication
|
||||||
args:@[moduleName, appParameters]];
|
args:@[moduleName, appParameters]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,12 +121,10 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
|
||||||
};
|
};
|
||||||
|
|
||||||
[_executor invalidate];
|
[_executor invalidate];
|
||||||
[_appEngine invalidate];
|
|
||||||
[_bridge invalidate];
|
[_bridge invalidate];
|
||||||
|
|
||||||
_executor = [[RCTContextExecutor alloc] init];
|
_executor = [[RCTContextExecutor alloc] init];
|
||||||
_bridge = [[RCTBridge alloc] initWithJavaScriptExecutor:_executor
|
_bridge = [[RCTBridge alloc] initWithJavaScriptExecutor:_executor
|
||||||
shadowQueue:_shadowQueue
|
|
||||||
javaScriptModulesConfig:[RCTModuleIDs config]];
|
javaScriptModulesConfig:[RCTModuleIDs config]];
|
||||||
|
|
||||||
_appEngine = [[RCTJavaScriptAppEngine alloc] initWithBridge:_bridge];
|
_appEngine = [[RCTJavaScriptAppEngine alloc] initWithBridge:_bridge];
|
||||||
|
|
|
@ -3,12 +3,12 @@
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
#import "RCTExport.h"
|
#import "RCTExport.h"
|
||||||
|
#import "RCTInvalidating.h"
|
||||||
|
|
||||||
@class RCTBridge;
|
@class RCTBridge;
|
||||||
|
|
||||||
@interface RCTTiming : NSObject <RCTNativeModule>
|
@interface RCTTiming : NSObject <RCTNativeModule, RCTInvalidating>
|
||||||
|
|
||||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||||
- (void)enqueueUpdateTimers;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
@interface RCTTimer : NSObject
|
@interface RCTTimer : NSObject
|
||||||
|
|
||||||
@property (nonatomic, strong, readonly) NSDate *target;
|
@property (nonatomic, strong, readonly) NSDate *target;
|
||||||
@property (nonatomic, assign, readonly, getter=isActive) BOOL active;
|
|
||||||
@property (nonatomic, assign, readonly) BOOL repeats;
|
@property (nonatomic, assign, readonly) BOOL repeats;
|
||||||
@property (nonatomic, strong, readonly) NSNumber *callbackID;
|
@property (nonatomic, strong, readonly) NSNumber *callbackID;
|
||||||
@property (nonatomic, assign, readonly) NSTimeInterval interval;
|
@property (nonatomic, assign, readonly) NSTimeInterval interval;
|
||||||
|
@ -26,7 +25,6 @@
|
||||||
repeats:(BOOL)repeats
|
repeats:(BOOL)repeats
|
||||||
{
|
{
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
_active = YES;
|
|
||||||
_interval = interval;
|
_interval = interval;
|
||||||
_repeats = repeats;
|
_repeats = repeats;
|
||||||
_callbackID = callbackID;
|
_callbackID = callbackID;
|
||||||
|
@ -40,13 +38,9 @@
|
||||||
*/
|
*/
|
||||||
- (BOOL)updateFoundNeedsJSUpdate
|
- (BOOL)updateFoundNeedsJSUpdate
|
||||||
{
|
{
|
||||||
if (_active && _target.timeIntervalSinceNow <= 0) {
|
if (_target && _target.timeIntervalSinceNow <= 0) {
|
||||||
// The JS Timers will do fine grained calculating of expired timeouts.
|
// The JS Timers will do fine grained calculating of expired timeouts.
|
||||||
if (_repeats) {
|
_target = _repeats ? [NSDate dateWithTimeIntervalSinceNow:_interval] : nil;
|
||||||
_target = [NSDate dateWithTimeIntervalSinceNow:_interval];
|
|
||||||
} else {
|
|
||||||
_active = NO;
|
|
||||||
}
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
return NO;
|
return NO;
|
||||||
|
@ -58,6 +52,7 @@
|
||||||
{
|
{
|
||||||
RCTSparseArray *_timers;
|
RCTSparseArray *_timers;
|
||||||
RCTBridge *_bridge;
|
RCTBridge *_bridge;
|
||||||
|
id _updateTimer;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||||
|
@ -65,19 +60,74 @@
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
_bridge = bridge;
|
_bridge = bridge;
|
||||||
_timers = [[RCTSparseArray alloc] init];
|
_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;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
- (void)dealloc
|
||||||
* TODO (#5906496): Wait until operations on `javaScriptQueue` are complete to complete the
|
{
|
||||||
* `dealloc`.
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
*/
|
}
|
||||||
/* - (void)dealloc
|
|
||||||
{
|
|
||||||
} */
|
|
||||||
|
|
||||||
- (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();
|
RCTAssertMainThread();
|
||||||
|
|
||||||
|
@ -86,7 +136,7 @@
|
||||||
if ([timer updateFoundNeedsJSUpdate]) {
|
if ([timer updateFoundNeedsJSUpdate]) {
|
||||||
[timersToCall addObject:timer.callbackID];
|
[timersToCall addObject:timer.callbackID];
|
||||||
}
|
}
|
||||||
if (!timer.active) {
|
if (!timer.target) {
|
||||||
_timers[timer.callbackID] = nil;
|
_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
|
* There's a small difference between the time when we call
|
||||||
* setTimeout/setInterval/requestAnimation frame and the time it actually makes
|
* setTimeout/setInterval/requestAnimation frame and the time it actually makes
|
||||||
|
@ -132,10 +174,13 @@
|
||||||
interval = 0;
|
interval = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
[self scheduleCallbackID:callbackID
|
RCTTimer *timer = [[RCTTimer alloc] initWithCallbackID:callbackID
|
||||||
interval:interval
|
interval:interval
|
||||||
targetTime:targetTime
|
targetTime:targetTime
|
||||||
repeats:repeats.boolValue];
|
repeats:repeats];
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
_timers[callbackID] = timer;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)deleteTimer:(NSNumber *)timerID
|
- (void)deleteTimer:(NSNumber *)timerID
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#import "RCTExport.h"
|
#import "RCTExport.h"
|
||||||
#import "RCTInvalidating.h"
|
#import "RCTInvalidating.h"
|
||||||
|
|
||||||
@class RCTAnimationRegistry;
|
@class RCTBridge;
|
||||||
@class RCTRootView;
|
@class RCTRootView;
|
||||||
@class RCTShadowView;
|
@class RCTShadowView;
|
||||||
|
|
||||||
|
@ -14,13 +14,10 @@
|
||||||
|
|
||||||
@interface RCTUIManager : NSObject <RCTInvalidating, RCTNativeModule>
|
@interface RCTUIManager : NSObject <RCTInvalidating, RCTNativeModule>
|
||||||
|
|
||||||
- (instancetype)initWithShadowQueue:(dispatch_queue_t)shadowQueue
|
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||||
viewManagers:(NSDictionary *)viewManagers;
|
|
||||||
|
|
||||||
@property (nonatomic, strong) RCTEventDispatcher *eventDispatcher;
|
|
||||||
@property (nonatomic, strong) RCTSparseArray *shadowViewRegistry;
|
@property (nonatomic, strong) RCTSparseArray *shadowViewRegistry;
|
||||||
@property (nonatomic, strong) RCTSparseArray *viewRegistry;
|
@property (nonatomic, strong) RCTSparseArray *viewRegistry;
|
||||||
@property (nonatomic, strong) RCTAnimationRegistry *animationRegistry;
|
|
||||||
@property (nonatomic, weak) id<RCTScrollableProtocol> mainScrollView;
|
@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
|
@implementation RCTUIManager
|
||||||
{
|
{
|
||||||
// Root views are only mutated on the shadow queue
|
// 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;
|
NSMutableArray *_pendingUIBlocks;
|
||||||
|
|
||||||
pthread_mutex_t _pendingUIBlocksMutex;
|
pthread_mutex_t _pendingUIBlocksMutex;
|
||||||
dispatch_queue_t _shadowQueue;
|
|
||||||
NSDictionary *_nextLayoutAnimationConfig; // RCT thread only
|
NSDictionary *_nextLayoutAnimationConfig; // RCT thread only
|
||||||
RCTResponseSenderBlock _nextLayoutAnimationCallback; // RCT thread only
|
RCTResponseSenderBlock _nextLayoutAnimationCallback; // RCT thread only
|
||||||
RCTResponseSenderBlock _layoutAnimationCallbackMT; // Main thread only
|
RCTResponseSenderBlock _layoutAnimationCallbackMT; // Main thread only
|
||||||
|
|
||||||
NSMutableDictionary *_defaultShadowViews;
|
NSMutableDictionary *_defaultShadowViews;
|
||||||
NSMutableDictionary *_defaultViews;
|
NSMutableDictionary *_defaultViews;
|
||||||
|
|
||||||
|
__weak RCTBridge *_bridge;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id <RCTNativeViewModule>)_managerInstanceForViewWithModuleName:(NSString *)moduleName
|
- (id <RCTNativeViewModule>)_managerInstanceForViewWithModuleName:(NSString *)moduleName
|
||||||
|
@ -62,17 +121,23 @@ static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_b
|
||||||
return managerInstance;
|
return managerInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithShadowQueue:(dispatch_queue_t)shadowQueue
|
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||||
viewManagers:(NSDictionary *)viewManagers
|
|
||||||
{
|
{
|
||||||
if ((self = [super init])) {
|
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;
|
_viewManagers = viewManagers;
|
||||||
|
|
||||||
_viewRegistry = [[RCTSparseArray alloc] init];
|
_viewRegistry = [[RCTSparseArray alloc] init];
|
||||||
_shadowViewRegistry = [[RCTSparseArray alloc] init];
|
_shadowViewRegistry = [[RCTSparseArray alloc] init];
|
||||||
_shadowQueue = shadowQueue;
|
|
||||||
pthread_mutex_init(&_pendingUIBlocksMutex, NULL);
|
|
||||||
|
|
||||||
// Internal resources
|
// Internal resources
|
||||||
_pendingUIBlocks = [[NSMutableArray alloc] init];
|
_pendingUIBlocks = [[NSMutableArray alloc] init];
|
||||||
_rootViewTags = [[NSMutableSet alloc] init];
|
_rootViewTags = [[NSMutableSet alloc] init];
|
||||||
|
@ -101,6 +166,8 @@ static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_b
|
||||||
|
|
||||||
- (void)invalidate
|
- (void)invalidate
|
||||||
{
|
{
|
||||||
|
RCTAssertMainThread();
|
||||||
|
|
||||||
_viewRegistry = nil;
|
_viewRegistry = nil;
|
||||||
_shadowViewRegistry = nil;
|
_shadowViewRegistry = nil;
|
||||||
|
|
||||||
|
@ -111,6 +178,8 @@ static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_b
|
||||||
|
|
||||||
- (void)registerRootView:(RCTRootView *)rootView;
|
- (void)registerRootView:(RCTRootView *)rootView;
|
||||||
{
|
{
|
||||||
|
RCTAssertMainThread();
|
||||||
|
|
||||||
NSNumber *reactTag = rootView.reactTag;
|
NSNumber *reactTag = rootView.reactTag;
|
||||||
UIView *existingView = _viewRegistry[reactTag];
|
UIView *existingView = _viewRegistry[reactTag];
|
||||||
RCTCAssert(existingView == nil || existingView == rootView,
|
RCTCAssert(existingView == nil || existingView == rootView,
|
||||||
|
@ -121,7 +190,7 @@ static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_b
|
||||||
CGRect frame = rootView.frame;
|
CGRect frame = rootView.frame;
|
||||||
|
|
||||||
// Register shadow view
|
// Register shadow view
|
||||||
dispatch_async(_shadowQueue, ^{
|
dispatch_async(_bridge.shadowQueue, ^{
|
||||||
|
|
||||||
RCTShadowView *shadowView = [[RCTShadowView alloc] init];
|
RCTShadowView *shadowView = [[RCTShadowView alloc] init];
|
||||||
shadowView.reactTag = reactTag;
|
shadowView.reactTag = reactTag;
|
||||||
|
@ -468,7 +537,7 @@ static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_b
|
||||||
|
|
||||||
- (UIView *)viewForViewManager:(id <RCTNativeViewModule>)manager
|
- (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)
|
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
|
// First copy the previous blocks into a temporary variable, then reset the
|
||||||
// pending blocks to a new array. This guards against mutation while
|
// pending blocks to a new array. This guards against mutation while
|
||||||
// processing the pending blocks in another thread.
|
// processing the pending blocks in another thread.
|
||||||
|
|
||||||
for (id <RCTNativeViewModule>viewManager in _viewManagers.allValues) {
|
for (id <RCTNativeViewModule>viewManager in _viewManagers.allValues) {
|
||||||
RCTViewManagerUIBlock uiBlock = [viewManager respondsToSelector:@selector(uiBlockToAmendWithShadowViewRegistry:)] ? [viewManager uiBlockToAmendWithShadowViewRegistry:_shadowViewRegistry] : nil;
|
RCTViewManagerUIBlock uiBlock = [viewManager respondsToSelector:@selector(uiBlockToAmendWithShadowViewRegistry:)] ? [viewManager uiBlockToAmendWithShadowViewRegistry:_shadowViewRegistry] : nil;
|
||||||
if (uiBlock != nil) {
|
if (uiBlock != nil) {
|
||||||
[self addUIBlock:uiBlock];
|
[self addUIBlock:uiBlock];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (NSNumber *reactTag in _rootViewTags) {
|
for (NSNumber *reactTag in _rootViewTags) {
|
||||||
RCTShadowView *rootView = _shadowViewRegistry[reactTag];
|
RCTShadowView *rootView = _shadowViewRegistry[reactTag];
|
||||||
[self addUIBlock:[self uiBlockWithLayoutUpdateForRootView:rootView]];
|
[self addUIBlock:[self uiBlockWithLayoutUpdateForRootView:rootView]];
|
||||||
[self _amendPendingUIBlocksWithStylePropagationUpdateForRootView:rootView];
|
[self _amendPendingUIBlocksWithStylePropagationUpdateForRootView:rootView];
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_lock(&_pendingUIBlocksMutex);
|
pthread_mutex_lock(&_pendingUIBlocksMutex);
|
||||||
NSArray *previousPendingUIBlocks = _pendingUIBlocks;
|
NSArray *previousPendingUIBlocks = _pendingUIBlocks;
|
||||||
_pendingUIBlocks = [[NSMutableArray alloc] init];
|
_pendingUIBlocks = [[NSMutableArray alloc] init];
|
||||||
pthread_mutex_unlock(&_pendingUIBlocksMutex);
|
pthread_mutex_unlock(&_pendingUIBlocksMutex);
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
for (dispatch_block_t block in previousPendingUIBlocks) {
|
for (dispatch_block_t block in previousPendingUIBlocks) {
|
||||||
block();
|
block();
|
||||||
|
|
Loading…
Reference in New Issue