mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-24 04:16:00 +08:00
2015-02-03 updates
- Add back providesModule transform to JSAppServer | Joseph Savona - [ReactKit] fix open source performance issue | John Harper - [ReactKit] improve ReactIOSEventEmitter logics | Andrew Rasmussen - [reactkit] fix web view JS executor and bind it to Command-d | John Harper - Removed hardcoded RCTModuleIDs | Nick Lockwood - [ReactKit] Animated GIF support | Alex Akers - [ReactKit] Update RCTBridge to support non-`id` argument types | Alex Akers - [reactkit] fix typo in RCTCopyProperty() change | John Harper - [reactkit] fix shadow view crash on missing properties | John Harper - [reactkit] fix transform keypath | John Harper
This commit is contained in:
@@ -33,10 +33,9 @@ static inline NSDictionary *RCTAPIErrorObject(NSString *msg)
|
||||
*/
|
||||
@interface RCTBridge : NSObject <RCTInvalidating>
|
||||
|
||||
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor
|
||||
javaScriptModulesConfig:(NSDictionary *)javaScriptModulesConfig;
|
||||
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor;
|
||||
|
||||
- (void)enqueueJSCall:(NSUInteger)moduleID methodID:(NSUInteger)methodID args:(NSArray *)args;
|
||||
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args;
|
||||
- (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete:(RCTJavaScriptCompleteBlock)onComplete;
|
||||
|
||||
@property (nonatomic, readonly) RCTEventDispatcher *eventDispatcher;
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#import "RCTInvalidating.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTModuleIDs.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
/**
|
||||
@@ -36,19 +35,6 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) {
|
||||
RCTBridgeFieldFlushDateMillis
|
||||
};
|
||||
|
||||
static NSString *RCTModuleName(Class moduleClass)
|
||||
{
|
||||
if ([moduleClass respondsToSelector:@selector(moduleName)]) {
|
||||
|
||||
return [moduleClass moduleName];
|
||||
|
||||
} else {
|
||||
|
||||
// Default implementation, works in most cases
|
||||
return NSStringFromClass(moduleClass);
|
||||
}
|
||||
}
|
||||
|
||||
static NSDictionary *RCTNativeModuleClasses(void)
|
||||
{
|
||||
static NSMutableDictionary *modules;
|
||||
@@ -73,7 +59,7 @@ static NSDictionary *RCTNativeModuleClasses(void)
|
||||
}
|
||||
|
||||
// Get module name
|
||||
NSString *moduleName = RCTModuleName(cls);
|
||||
NSString *moduleName = [cls respondsToSelector:@selector(moduleName)] ? [cls moduleName] : NSStringFromClass(cls);
|
||||
|
||||
// Check module name is unique
|
||||
id existingClass = modules[moduleName];
|
||||
@@ -90,23 +76,22 @@ static NSDictionary *RCTNativeModuleClasses(void)
|
||||
@implementation RCTBridge
|
||||
{
|
||||
NSMutableDictionary *_moduleInstances;
|
||||
NSDictionary *_javaScriptModulesConfig;
|
||||
NSMutableDictionary *_moduleIDLookup;
|
||||
NSMutableDictionary *_methodIDLookup;
|
||||
id<RCTJavaScriptExecutor> _javaScriptExecutor;
|
||||
}
|
||||
|
||||
static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
|
||||
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor
|
||||
javaScriptModulesConfig:(NSDictionary *)javaScriptModulesConfig
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_javaScriptExecutor = javaScriptExecutor;
|
||||
_latestJSExecutor = _javaScriptExecutor;
|
||||
_eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
|
||||
_javaScriptModulesConfig = javaScriptModulesConfig;
|
||||
_shadowQueue = dispatch_queue_create("com.facebook.ReactKit.ShadowQueue", DISPATCH_QUEUE_SERIAL);
|
||||
|
||||
// Register modules
|
||||
// Instantiate modules
|
||||
_moduleInstances = [[NSMutableDictionary alloc] init];
|
||||
[RCTNativeModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, Class moduleClass, BOOL *stop) {
|
||||
if (_moduleInstances[moduleName] == nil) {
|
||||
@@ -118,6 +103,8 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
}
|
||||
}];
|
||||
|
||||
_moduleIDLookup = [[NSMutableDictionary alloc] init];
|
||||
_methodIDLookup = [[NSMutableDictionary alloc] init];
|
||||
[self doneRegisteringModules];
|
||||
}
|
||||
|
||||
@@ -168,10 +155,18 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
/**
|
||||
* Like JS::call, for objective-c.
|
||||
*/
|
||||
- (void)enqueueJSCall:(NSUInteger)moduleID methodID:(NSUInteger)methodID args:(NSArray *)args
|
||||
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
[self _invokeRemoteJSModule:moduleID methodID:methodID args:args];
|
||||
NSNumber *moduleID = _moduleIDLookup[moduleDotMethod];
|
||||
RCTAssert(moduleID, @"Module '%@' not registered.",
|
||||
[[moduleDotMethod componentsSeparatedByString:@"."] firstObject]);
|
||||
|
||||
NSNumber *methodID = _methodIDLookup[moduleDotMethod];
|
||||
RCTAssert(methodID, @"Method '%@' not registered.", moduleDotMethod);
|
||||
|
||||
[self _invokeAndProcessModule:@"BatchedBridge"
|
||||
method:@"callFunctionReturnFlushedQueue"
|
||||
arguments:@[moduleID, methodID, args]];
|
||||
}
|
||||
|
||||
- (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete:(RCTJavaScriptCompleteBlock)onComplete
|
||||
@@ -217,13 +212,6 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
callback:processResponse];
|
||||
}
|
||||
|
||||
- (void)_invokeRemoteJSModule:(NSUInteger)moduleID methodID:(NSUInteger)methodID args:(NSArray *)args
|
||||
{
|
||||
[self _invokeAndProcessModule:@"BatchedBridge"
|
||||
method:@"callFunctionReturnFlushedQueue"
|
||||
arguments:@[@(moduleID), @(methodID), args]];
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO (#5906496): Have responses piggy backed on a round trip with ObjC->JS requests.
|
||||
*/
|
||||
@@ -333,21 +321,21 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
// 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;
|
||||
|
||||
NSMethodSignature *methodSignature = [target methodSignatureForSelector:selector];
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
|
||||
[invocation setArgument:&target atIndex:0];
|
||||
[invocation setArgument:&selector atIndex:1];
|
||||
|
||||
// Retain used blocks until after invocation completes.
|
||||
NSMutableArray *blocks = [NSMutableArray array];
|
||||
|
||||
NS_VALID_UNTIL_END_OF_SCOPE NSMutableArray *blocks = [NSMutableArray array];
|
||||
|
||||
[params enumerateObjectsUsingBlock:^(id param, NSUInteger idx, BOOL *stop) {
|
||||
if ([param isEqual:[NSNull null]]) {
|
||||
param = nil;
|
||||
@@ -356,8 +344,58 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
[blocks addObject:block];
|
||||
param = block;
|
||||
}
|
||||
|
||||
[invocation setArgument:¶m atIndex:idx + 2];
|
||||
|
||||
NSUInteger argIdx = idx + 2;
|
||||
|
||||
BOOL shouldSet = YES;
|
||||
const char *argumentType = [methodSignature getArgumentTypeAtIndex:argIdx];
|
||||
switch (argumentType[0]) {
|
||||
case ':':
|
||||
if ([param isKindOfClass:[NSString class]]) {
|
||||
SEL selector = NSSelectorFromString(param);
|
||||
[invocation setArgument:&selector atIndex:argIdx];
|
||||
shouldSet = NO;
|
||||
}
|
||||
break;
|
||||
|
||||
case '*':
|
||||
if ([param isKindOfClass:[NSString class]]) {
|
||||
const char *string = [param UTF8String];
|
||||
[invocation setArgument:&string atIndex:argIdx];
|
||||
shouldSet = NO;
|
||||
}
|
||||
break;
|
||||
|
||||
#define CASE(_value, _type, _selector) \
|
||||
case _value: \
|
||||
if ([param respondsToSelector:@selector(_selector)]) { \
|
||||
_type value = [param _selector]; \
|
||||
[invocation setArgument:&value atIndex:argIdx]; \
|
||||
shouldSet = NO; \
|
||||
} \
|
||||
break;
|
||||
|
||||
CASE('c', char, charValue)
|
||||
CASE('C', unsigned char, unsignedCharValue)
|
||||
CASE('s', short, shortValue)
|
||||
CASE('S', unsigned short, unsignedShortValue)
|
||||
CASE('i', int, intValue)
|
||||
CASE('I', unsigned int, unsignedIntValue)
|
||||
CASE('l', long, longValue)
|
||||
CASE('L', unsigned long, unsignedLongValue)
|
||||
CASE('q', long long, longLongValue)
|
||||
CASE('Q', unsigned long long, unsignedLongLongValue)
|
||||
CASE('f', float, floatValue)
|
||||
CASE('d', double, doubleValue)
|
||||
CASE('B', BOOL, boolValue)
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (shouldSet) {
|
||||
[invocation setArgument:¶m atIndex:argIdx];
|
||||
}
|
||||
}];
|
||||
|
||||
@try {
|
||||
@@ -366,10 +404,6 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
@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;
|
||||
@@ -412,16 +446,71 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
return invocation;
|
||||
}
|
||||
|
||||
- (NSArray *)JSMethods
|
||||
{
|
||||
NSMutableArray *methods = [[NSMutableArray alloc] init];
|
||||
|
||||
// Add globally used methods
|
||||
[methods addObjectsFromArray:@[
|
||||
@"Bundler.runApplication",
|
||||
@"RCTEventEmitter.receiveEvent",
|
||||
@"RCTEventEmitter.receiveTouches",
|
||||
]];
|
||||
|
||||
// NOTE: these methods are currently unused in the OSS project
|
||||
// @"Dimensions.set",
|
||||
// @"RCTDeviceEventEmitter.emit",
|
||||
// @"RCTNativeAppEventEmitter.emit",
|
||||
// @"ReactIOS.unmountComponentAtNodeAndRemoveContainer",
|
||||
|
||||
// Register individual methods from modules
|
||||
for (Class cls in RCTNativeModuleClasses().allValues) {
|
||||
if (RCTClassOverridesClassMethod(cls, @selector(JSMethods))) {
|
||||
[methods addObjectsFromArray:[cls JSMethods]];
|
||||
}
|
||||
}
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
- (void)doneRegisteringModules
|
||||
{
|
||||
// TODO: everything in this method can actually be determined
|
||||
// statically from just the class, and can therefore be cached
|
||||
// in a dispatch_once instead of being repeated every time we
|
||||
// reload or create a new RootView.
|
||||
|
||||
RCTAssertMainThread();
|
||||
RCTAssert(_javaScriptModulesConfig != nil, @"JS module config not loaded in APP");
|
||||
|
||||
NSMutableDictionary *objectsToInject = [NSMutableDictionary dictionary];
|
||||
|
||||
// Dictionary of { moduleName0: { moduleID: 0, methods: { methodName0: { methodID: 0, type: remote }, methodName1: { ... }, ... }, ... }
|
||||
/**
|
||||
* This constructs the remote modules configuration data structure,
|
||||
* which represents the native modules and methods that will be called
|
||||
* by JS. A numeric ID is assigned to each module and method, which will
|
||||
* be used to communicate via the bridge. The structure of each
|
||||
* module is as follows:
|
||||
*
|
||||
* "ModuleName1": {
|
||||
* "moduleID": 0,
|
||||
* "methods": {
|
||||
* "methodName1": {
|
||||
* "methodID": 0,
|
||||
* "type": "remote"
|
||||
* },
|
||||
* "methodName2": {
|
||||
* "methodID": 1,
|
||||
* "type": "remote"
|
||||
* },
|
||||
* etc...
|
||||
* },
|
||||
* "constants": {
|
||||
* ...
|
||||
* }
|
||||
* },
|
||||
* etc...
|
||||
*/
|
||||
|
||||
NSUInteger moduleCount = RCTExportedMethodsByModule().count;
|
||||
NSMutableDictionary *moduleConfigs = [NSMutableDictionary dictionaryWithCapacity:RCTExportedMethodsByModule().count];
|
||||
NSMutableDictionary *remoteModules = [NSMutableDictionary dictionaryWithCapacity:RCTExportedMethodsByModule().count];
|
||||
for (NSUInteger i = 0; i < moduleCount; i++) {
|
||||
NSString *moduleName = RCTExportedModuleNameAtSortedIndex(i);
|
||||
NSArray *rawMethods = RCTExportedMethodsByModule()[moduleName];
|
||||
@@ -433,35 +522,87 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
};
|
||||
}];
|
||||
|
||||
NSMutableDictionary *moduleConfig = [NSMutableDictionary dictionary];
|
||||
moduleConfig[@"moduleID"] = @(i);
|
||||
moduleConfig[@"methods"] = methods;
|
||||
NSDictionary *module = @{
|
||||
@"moduleID": @(i),
|
||||
@"methods": methods
|
||||
};
|
||||
|
||||
id target = [_moduleInstances objectForKey:moduleName];
|
||||
if ([target respondsToSelector:@selector(constantsToExport)]) {
|
||||
moduleConfig[@"constants"] = [target constantsToExport];
|
||||
id target = _moduleInstances[moduleName];
|
||||
if (RCTClassOverridesClassMethod([target class], @selector(constantsToExport))) {
|
||||
module = [module mutableCopy];
|
||||
((NSMutableDictionary *)module)[@"constants"] = [[target class] constantsToExport];
|
||||
}
|
||||
moduleConfigs[moduleName] = moduleConfig;
|
||||
remoteModules[moduleName] = module;
|
||||
}
|
||||
NSDictionary *batchedBridgeConfig = @{
|
||||
@"remoteModuleConfig": moduleConfigs,
|
||||
@"localModulesConfig": _javaScriptModulesConfig
|
||||
};
|
||||
|
||||
NSString *configJSON = RCTJSONStringify(batchedBridgeConfig, NULL);
|
||||
objectsToInject[@"__fbBatchedBridgeConfig"] = configJSON;
|
||||
|
||||
|
||||
/**
|
||||
* As above, but for local modules/methods, which represent JS classes
|
||||
* and methods that will be called by the native code via the bridge.
|
||||
* Structure is essentially the same as for remote modules:
|
||||
*
|
||||
* "ModuleName1": {
|
||||
* "moduleID": 0,
|
||||
* "methods": {
|
||||
* "methodName1": {
|
||||
* "methodID": 0,
|
||||
* "type": "local"
|
||||
* },
|
||||
* "methodName2": {
|
||||
* "methodID": 1,
|
||||
* "type": "local"
|
||||
* },
|
||||
* etc...
|
||||
* }
|
||||
* },
|
||||
* etc...
|
||||
*/
|
||||
|
||||
NSMutableDictionary *localModules = [[NSMutableDictionary alloc] init];
|
||||
for (NSString *moduleDotMethod in [self JSMethods]) {
|
||||
|
||||
NSArray *parts = [moduleDotMethod componentsSeparatedByString:@"."];
|
||||
RCTAssert(parts.count == 2, @"'%@' is not a valid JS method definition - expected 'Module.method' format.", moduleDotMethod);
|
||||
|
||||
// Add module if it doesn't already exist
|
||||
NSString *moduleName = parts[0];
|
||||
NSDictionary *module = localModules[moduleName];
|
||||
if (!module) {
|
||||
module = @{
|
||||
@"moduleID": @(localModules.count),
|
||||
@"methods": [[NSMutableDictionary alloc] init]
|
||||
};
|
||||
localModules[moduleName] = module;
|
||||
}
|
||||
|
||||
// Add method if it doesn't already exist
|
||||
NSString *methodName = parts[1];
|
||||
NSMutableDictionary *methods = module[@"methods"];
|
||||
if (!methods[methodName]) {
|
||||
methods[methodName] = @{
|
||||
@"methodID": @(methods.count),
|
||||
@"type": @"local"
|
||||
};
|
||||
}
|
||||
|
||||
// Add module and method lookup
|
||||
_moduleIDLookup[moduleDotMethod] = module[@"moduleID"];
|
||||
_methodIDLookup[moduleDotMethod] = methods[methodName][@"methodID"];
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject module data into JS context
|
||||
*/
|
||||
NSString *configJSON = RCTJSONStringify(@{
|
||||
@"remoteModuleConfig": remoteModules,
|
||||
@"localModulesConfig": localModules
|
||||
}, NULL);
|
||||
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||
[objectsToInject enumerateKeysAndObjectsUsingBlock:^(NSString *objectName, NSString *script, BOOL *stop) {
|
||||
[_javaScriptExecutor injectJSONText:script asGlobalObjectNamed:objectName callback:^(id err) {
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}];
|
||||
[_javaScriptExecutor injectJSONText:configJSON asGlobalObjectNamed:@"__fbBatchedBridgeConfig" callback:^(id err) {
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}];
|
||||
|
||||
for (NSUInteger i = 0, count = objectsToInject.count; i < count; i++) {
|
||||
if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC)) != 0) {
|
||||
RCTLogMustFix(@"JavaScriptExecutor take too long to inject JSON object");
|
||||
}
|
||||
if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC)) != 0) {
|
||||
RCTLogMustFix(@"JavaScriptExecutor take too long to inject JSON object");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
+ (UIColor *)UIColor:(id)json;
|
||||
+ (CGColorRef)CGColor:(id)json;
|
||||
|
||||
+ (CAKeyframeAnimation *)GIF:(id)json;
|
||||
+ (UIImage *)UIImage:(id)json;
|
||||
+ (CGImageRef)CGImage:(id)json;
|
||||
|
||||
@@ -56,4 +57,4 @@
|
||||
+ (css_position_type_t)css_position_type_t:(id)json;
|
||||
+ (css_wrap_type_t)css_wrap_type_t:(id)json;
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
#import "RCTConvert.h"
|
||||
|
||||
#import <ImageIO/ImageIO.h>
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
|
||||
#import "RCTLog.h"
|
||||
|
||||
CGFloat const RCTDefaultFontSize = 14;
|
||||
@@ -426,6 +429,83 @@ RCT_STRUCT_CONVERTER(CGAffineTransform, (@[@"a", @"b", @"c", @"d", @"tx", @"ty"]
|
||||
return [self UIColor:json].CGColor;
|
||||
}
|
||||
|
||||
+ (CAKeyframeAnimation *)GIF:(id)json
|
||||
{
|
||||
CGImageSourceRef imageSource;
|
||||
if ([json isKindOfClass:[NSString class]]) {
|
||||
NSString *path = json;
|
||||
if (path.length == 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSURL *fileURL = [path isAbsolutePath] ? [NSURL fileURLWithPath:path] : [[NSBundle mainBundle] URLForResource:path withExtension:nil];
|
||||
imageSource = CGImageSourceCreateWithURL((CFURLRef)fileURL, NULL);
|
||||
} else if ([json isKindOfClass:[NSData class]]) {
|
||||
NSData *data = json;
|
||||
if (data.length == 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
imageSource = CGImageSourceCreateWithData((CFDataRef)data, NULL);
|
||||
}
|
||||
|
||||
if (!UTTypeConformsTo(CGImageSourceGetType(imageSource), kUTTypeGIF)) {
|
||||
CFRelease(imageSource);
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSDictionary *properties = (__bridge_transfer NSDictionary *)CGImageSourceCopyProperties(imageSource, NULL);
|
||||
NSUInteger loopCount = [properties[(id)kCGImagePropertyGIFDictionary][(id)kCGImagePropertyGIFLoopCount] unsignedIntegerValue];
|
||||
|
||||
size_t imageCount = CGImageSourceGetCount(imageSource);
|
||||
NSTimeInterval duration = 0;
|
||||
NSMutableArray *delays = [NSMutableArray arrayWithCapacity:imageCount];
|
||||
NSMutableArray *images = [NSMutableArray arrayWithCapacity:imageCount];
|
||||
for (size_t i = 0; i < imageCount; i++) {
|
||||
CGImageRef image = CGImageSourceCreateImageAtIndex(imageSource, i, NULL);
|
||||
NSDictionary *frameProperties = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(imageSource, i, NULL);
|
||||
NSDictionary *frameGIFProperties = frameProperties[(id)kCGImagePropertyGIFDictionary];
|
||||
|
||||
const NSTimeInterval kDelayTimeIntervalDefault = 0.1;
|
||||
NSNumber *delayTime = frameGIFProperties[(id)kCGImagePropertyGIFUnclampedDelayTime] ?: frameGIFProperties[(id)kCGImagePropertyGIFDelayTime];
|
||||
if (delayTime == nil) {
|
||||
if (i == 0) {
|
||||
delayTime = @(kDelayTimeIntervalDefault);
|
||||
} else {
|
||||
delayTime = delays[i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
const NSTimeInterval kDelayTimeIntervalMinimum = 0.02;
|
||||
if (delayTime.floatValue < (float)kDelayTimeIntervalMinimum - FLT_EPSILON) {
|
||||
delayTime = @(kDelayTimeIntervalDefault);
|
||||
}
|
||||
|
||||
duration += delayTime.doubleValue;
|
||||
delays[i] = delayTime;
|
||||
images[i] = (__bridge_transfer id)image;
|
||||
}
|
||||
|
||||
CFRelease(imageSource);
|
||||
|
||||
NSMutableArray *keyTimes = [NSMutableArray arrayWithCapacity:delays.count];
|
||||
NSTimeInterval runningDuration = 0;
|
||||
for (NSNumber *delayNumber in delays) {
|
||||
[keyTimes addObject:@(runningDuration / duration)];
|
||||
runningDuration += delayNumber.doubleValue;
|
||||
}
|
||||
|
||||
[keyTimes addObject:@1.0];
|
||||
|
||||
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
|
||||
animation.calculationMode = kCAAnimationDiscrete;
|
||||
animation.repeatCount = loopCount == 0 ? HUGE_VALF : loopCount;
|
||||
animation.keyTimes = keyTimes;
|
||||
animation.values = images;
|
||||
animation.duration = duration;
|
||||
return animation;
|
||||
}
|
||||
|
||||
+ (UIImage *)UIImage:(id)json
|
||||
{
|
||||
if (![json isKindOfClass:[NSString class]]) {
|
||||
|
||||
@@ -33,9 +33,11 @@ typedef NS_ENUM(NSInteger, RCTScrollEventType) {
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||
|
||||
/**
|
||||
* Send an arbitrary event type
|
||||
* Send a named event. For most purposes, use the an
|
||||
* event type of RCTEventTypeDefault, the other types
|
||||
* are used internally by the React framework.
|
||||
*/
|
||||
- (void)sendRawEventWithType:(NSString *)eventType body:(NSDictionary *)body;
|
||||
- (void)sendEventWithName:(NSString *)name body:(NSDictionary *)body;
|
||||
|
||||
/**
|
||||
* Send an array of touch events
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#import "RCTEventDispatcher.h"
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTModuleIDs.h"
|
||||
#import "UIView+ReactKit.h"
|
||||
|
||||
@implementation RCTEventDispatcher
|
||||
@@ -19,39 +18,13 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSArray *)touchEvents
|
||||
- (void)sendEventWithName:(NSString *)name body:(NSDictionary *)body
|
||||
{
|
||||
static NSArray *events;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
events = @[
|
||||
@"topTouchStart",
|
||||
@"topTouchMove",
|
||||
@"topTouchEnd",
|
||||
@"topTouchCancel",
|
||||
];
|
||||
});
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
- (void)sendRawEventWithType:(NSString *)eventType body:(NSDictionary *)body
|
||||
{
|
||||
static NSSet *touchEvents;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
touchEvents = [NSSet setWithArray:[self touchEvents]];
|
||||
});
|
||||
|
||||
RCTAssert(![touchEvents containsObject:eventType], @"Touch events must be"
|
||||
"sent via the sendTouchEventWithOrderedTouches: method, not sendRawEventWithType:");
|
||||
|
||||
RCTAssert([body[@"target"] isKindOfClass:[NSNumber class]],
|
||||
@"Event body dictionary must include a 'target' property containing a react tag");
|
||||
@"Event body dictionary must include a 'target' property containing a react tag");
|
||||
|
||||
[_bridge enqueueJSCall:RCTModuleIDReactIOSEventEmitter
|
||||
methodID:RCTEventEmitterReceiveEvent
|
||||
args:@[body[@"target"], eventType, body]];
|
||||
[_bridge enqueueJSCall:@"RCTEventEmitter.receiveEvent"
|
||||
args:@[body[@"target"], name, body]];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,30 +42,32 @@
|
||||
touches:(NSArray *)touches
|
||||
changedIndexes:(NSArray *)changedIndexes
|
||||
{
|
||||
static NSString *events[] = {
|
||||
@"topTouchStart",
|
||||
@"topTouchMove",
|
||||
@"topTouchEnd",
|
||||
@"topTouchCancel",
|
||||
};
|
||||
|
||||
RCTAssert(touches.count, @"No touches in touchEventArgsForOrderedTouches");
|
||||
|
||||
[_bridge enqueueJSCall:RCTModuleIDReactIOSEventEmitter
|
||||
methodID:RCTEventEmitterReceiveTouches
|
||||
args:@[[self touchEvents][type], touches, changedIndexes]];
|
||||
[_bridge enqueueJSCall:@"RCTEventEmitter.receiveTouches"
|
||||
args:@[events[type], touches, changedIndexes]];
|
||||
}
|
||||
|
||||
- (void)sendTextEventWithType:(RCTTextEventType)type
|
||||
reactTag:(NSNumber *)reactTag
|
||||
text:(NSString *)text
|
||||
{
|
||||
static NSArray *events;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
events = @[
|
||||
@"topFocus",
|
||||
@"topBlur",
|
||||
@"topChange",
|
||||
@"topSubmitEditing",
|
||||
@"topEndEditing",
|
||||
];
|
||||
});
|
||||
static NSString *events[] = {
|
||||
@"topFocus",
|
||||
@"topBlur",
|
||||
@"topChange",
|
||||
@"topSubmitEditing",
|
||||
@"topEndEditing",
|
||||
};
|
||||
|
||||
[self sendRawEventWithType:events[type] body:@{
|
||||
[self sendEventWithName:events[type] body:@{
|
||||
@"text": text,
|
||||
@"target": reactTag
|
||||
}];
|
||||
@@ -111,18 +86,14 @@
|
||||
scrollView:(UIScrollView *)scrollView
|
||||
userData:(NSDictionary *)userData
|
||||
{
|
||||
static NSArray *events;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
events = @[
|
||||
@"topScrollBeginDrag",
|
||||
@"topScroll",
|
||||
@"topScrollEndDrag",
|
||||
@"topMomentumScrollBegin",
|
||||
@"topMomentumScrollEnd",
|
||||
@"topScrollAnimationEnd",
|
||||
];
|
||||
});
|
||||
static NSString *events[] = {
|
||||
@"topScrollBeginDrag",
|
||||
@"topScroll",
|
||||
@"topScrollEndDrag",
|
||||
@"topMomentumScrollBegin",
|
||||
@"topMomentumScrollEnd",
|
||||
@"topScrollAnimationEnd",
|
||||
};
|
||||
|
||||
NSDictionary *body = @{
|
||||
@"contentOffset": @{
|
||||
@@ -147,7 +118,7 @@
|
||||
body = mutableBody;
|
||||
}
|
||||
|
||||
[self sendRawEventWithType:events[type] body:body];
|
||||
[self sendEventWithName:events[type] body:body];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -26,6 +26,7 @@ extern NSString *RCTExportedModuleNameAtSortedIndex(NSUInteger index);
|
||||
extern NSDictionary *RCTExportedMethodsByModule(void);
|
||||
|
||||
extern BOOL RCTSetProperty(id target, NSString *keypath, id value);
|
||||
extern BOOL RCTCopyProperty(id target, id source, NSString *keypath);
|
||||
extern BOOL RCTCallSetter(id target, SEL setter, id value);
|
||||
/* ------------------------------------------------------------------- */
|
||||
|
||||
@@ -66,9 +67,22 @@ _RCTExportSectionName))) static const RCTExportEntry __rct_export_entry__ = { __
|
||||
|
||||
/**
|
||||
* Injects constants into JS. These constants are made accessible via
|
||||
* NativeModules.moduleName.X.
|
||||
* NativeModules.moduleName.X. Note that this method is not inherited when you
|
||||
* subclass a module, and you should not call [super constantsToExport] when
|
||||
* implementing it.
|
||||
*/
|
||||
- (NSDictionary *)constantsToExport;
|
||||
+ (NSDictionary *)constantsToExport;
|
||||
|
||||
/**
|
||||
* An array of JavaScript methods that the module will call via the
|
||||
* -[RCTBridge enqueueJSCall:args:] method. Each method should be specified
|
||||
* as a string of the form "JSModuleName.jsMethodName". Attempting to call a
|
||||
* method that has not been registered will result in an error. If a method
|
||||
* has already been regsistered by another module, it is not necessary to
|
||||
* register it again, but it is good pratice. Registering the same method
|
||||
* more than once is silently ignored and will not result in an error.
|
||||
*/
|
||||
+ (NSArray *)JSMethods;
|
||||
|
||||
/**
|
||||
* Notifies the module that a batch of JS method invocations has just completed.
|
||||
@@ -84,9 +98,18 @@ _RCTExportSectionName))) static const RCTExportEntry __rct_export_entry__ = { __
|
||||
@protocol RCTNativeViewModule <NSObject>
|
||||
|
||||
/**
|
||||
* This method instantiates a native view to be managed by the module.
|
||||
* Designated initializer for view modules. The event dispatched can either be
|
||||
* used directly by the module, or passed on to instantiated views for event handling.
|
||||
*/
|
||||
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher;
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher;
|
||||
|
||||
/**
|
||||
* This method instantiates a native view to be managed by the module. The method
|
||||
* will be called many times, and should return a fresh instance each time. The
|
||||
* view module MUST NOT cache the returned view and return the same instance
|
||||
* for subsequent calls.
|
||||
*/
|
||||
- (UIView *)view;
|
||||
|
||||
@optional
|
||||
|
||||
@@ -98,7 +121,8 @@ _RCTExportSectionName))) static const RCTExportEntry __rct_export_entry__ = { __
|
||||
|
||||
/**
|
||||
* 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. As with the -view method,
|
||||
* the -shadowView method should return a fresh instance each time it is called.
|
||||
*/
|
||||
- (RCTShadowView *)shadowView;
|
||||
|
||||
@@ -164,8 +188,11 @@ RCT_REMAP_VIEW_PROPERTY(name, name)
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* Note that this method is not inherited when you subclass a view module, and
|
||||
* you should not call [super customBubblingEventTypes] when implementing it.
|
||||
*/
|
||||
- (NSDictionary *)customBubblingEventTypes;
|
||||
+ (NSDictionary *)customBubblingEventTypes;
|
||||
|
||||
/**
|
||||
* Returns a dictionary of config data passed to JS that defines eligible events
|
||||
@@ -177,14 +204,19 @@ RCT_REMAP_VIEW_PROPERTY(name, name)
|
||||
* @"registrationName": @"onTwirl"
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* Note that this method is not inherited when you subclass a view module, and
|
||||
* you should not call [super customDirectEventTypes] when implementing it.
|
||||
*/
|
||||
- (NSDictionary *)customDirectEventTypes;
|
||||
+ (NSDictionary *)customDirectEventTypes;
|
||||
|
||||
/**
|
||||
* Injects constants into JS. These constants are made accessible via
|
||||
* NativeModules.moduleName.X.
|
||||
* NativeModules.moduleName.X. Note that this method is not inherited when you
|
||||
* subclass a view module, and you should not call [super constantsToExport]
|
||||
* when implementing it.
|
||||
*/
|
||||
- (NSDictionary *)constantsToExport;
|
||||
+ (NSDictionary *)constantsToExport;
|
||||
|
||||
/**
|
||||
* To deprecate, hopefully
|
||||
|
||||
@@ -349,6 +349,36 @@ BOOL RCTSetProperty(id target, NSString *keypath, id value)
|
||||
return YES;
|
||||
}
|
||||
|
||||
BOOL RCTCopyProperty(id target, id source, NSString *keypath)
|
||||
{
|
||||
// Split keypath
|
||||
NSArray *parts = [keypath componentsSeparatedByString:@"."];
|
||||
NSString *key = [parts lastObject];
|
||||
for (NSUInteger i = 0; i < parts.count - 1; i++) {
|
||||
source = [source valueForKey:parts[i]];
|
||||
target = [target valueForKey:parts[i]];
|
||||
if (!source || !target) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
// Check class for property definition
|
||||
if (!class_getProperty([source class], [key UTF8String])) {
|
||||
// Check if setter exists
|
||||
SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:",
|
||||
[[key substringToIndex:1] uppercaseString],
|
||||
[key substringFromIndex:1]]);
|
||||
|
||||
if (![source respondsToSelector:setter]
|
||||
|| ![target respondsToSelector:setter]) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
[target setValue:[source valueForKey:key] forKey:key];
|
||||
return YES;
|
||||
}
|
||||
|
||||
BOOL RCTCallSetter(id target, SEL setter, id value)
|
||||
{
|
||||
// Get property name
|
||||
|
||||
@@ -2,12 +2,16 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
typedef void (^RCTDataDownloadBlock)(NSData *data, NSError *error);
|
||||
typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error);
|
||||
|
||||
@interface RCTImageDownloader : NSObject
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
- (id)downloadDataForURL:(NSURL *)url
|
||||
block:(RCTDataDownloadBlock)block;
|
||||
|
||||
- (id)downloadImageForURL:(NSURL *)url
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
|
||||
@@ -18,6 +18,20 @@
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (id)downloadDataForURL:(NSURL *)url
|
||||
block:(RCTDataDownloadBlock)block
|
||||
{
|
||||
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
// Dispatch back to main thread
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
block(data, error);
|
||||
});
|
||||
}];
|
||||
|
||||
[task resume];
|
||||
return task;
|
||||
}
|
||||
|
||||
- (id)downloadImageForURL:(NSURL *)url
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
* All of this will be replaced with an auto-generated bridge.
|
||||
*/
|
||||
|
||||
typedef NS_ENUM(NSUInteger, RCTJSModuleIDs) {
|
||||
RCTModuleIDReactIOSEventEmitter,
|
||||
RCTModuleIDJSTimers, // JS timer tracking module
|
||||
RCTModuleIDReactIOS,
|
||||
RCTModuleIDBundler,
|
||||
RCTModuleIDDimensions,
|
||||
RCTModuleIDDeviceEventEmitter,
|
||||
RCTModuleIDNativeAppEventEmitter,
|
||||
};
|
||||
|
||||
/**
|
||||
* JS module `RCTIOSEventEmitter`.
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, RCTEventEmitterRemoteMethodIDs) {
|
||||
RCTEventEmitterReceiveEvent = 0,
|
||||
RCTEventEmitterReceiveTouches
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSUInteger, RCTKeyCode) {
|
||||
RCTKeyCodeBackspace = 8,
|
||||
RCTKeyCodeReturn = 13,
|
||||
};
|
||||
|
||||
/**
|
||||
* JS timer tracking module.
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, RCTJSTimersMethodIDs) {
|
||||
RCTJSTimersCallTimers = 0
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSUInteger, RCTReactIOSMethodIDs) {
|
||||
RCTReactIOSUnmountComponentAtNodeAndRemoveContainer = 0,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSUInteger, RCTBundlerMethodIDs) {
|
||||
RCTBundlerRunApplication = 0
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSUInteger, RCTDimensionsMethodIDs) {
|
||||
RCTDimensionsSet = 0
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSUInteger, RCTDeviceEventEmitterMethodIDs) {
|
||||
RCTDeviceEventEmitterEmit = 0
|
||||
};
|
||||
|
||||
@interface RCTModuleIDs : NSObject
|
||||
|
||||
+ (NSDictionary *)config;
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTModuleIDs.h"
|
||||
|
||||
@implementation RCTModuleIDs
|
||||
|
||||
/**
|
||||
* Configures invocations from IOS -> JS. Simply passes the name of the key in
|
||||
* the configuration object `require('ReactIOSEventEmitter')`.
|
||||
*/
|
||||
+ (NSDictionary *)config
|
||||
{
|
||||
return @{
|
||||
@"Dimensions": @{
|
||||
@"moduleID": @(RCTModuleIDDimensions),
|
||||
@"methods": @{
|
||||
@"set": @{
|
||||
@"methodID": @(RCTDimensionsSet),
|
||||
@"type": @"local"
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@"RCTDeviceEventEmitter": @{
|
||||
@"moduleID": @(RCTModuleIDDeviceEventEmitter),
|
||||
@"methods": @{
|
||||
@"emit": @{
|
||||
@"methodID": @(RCTDeviceEventEmitterEmit),
|
||||
@"type": @"local"
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@"RCTEventEmitter": @{
|
||||
@"moduleID": @(RCTModuleIDReactIOSEventEmitter),
|
||||
@"methods": @{
|
||||
@"receiveEvent": @{
|
||||
@"methodID": @(RCTEventEmitterReceiveEvent),
|
||||
@"type": @"local"
|
||||
},
|
||||
@"receiveTouches": @{
|
||||
@"methodID": @(RCTEventEmitterReceiveTouches),
|
||||
@"type": @"local"
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@"RCTNativeAppEventEmitter": @{
|
||||
@"moduleID": @(RCTModuleIDNativeAppEventEmitter),
|
||||
@"methods": @{
|
||||
@"emit": @{
|
||||
@"methodID": @(RCTDeviceEventEmitterEmit),
|
||||
@"type": @"local"
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@"RCTJSTimers": @{
|
||||
@"moduleID": @(RCTModuleIDJSTimers),
|
||||
@"methods": @{
|
||||
// Last argument is the callback.
|
||||
@"callTimers": @{
|
||||
@"methodID": @(RCTJSTimersCallTimers),
|
||||
@"type": @"local"
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@"ReactIOS": @{
|
||||
@"moduleID": @(RCTModuleIDReactIOS),
|
||||
@"methods": @{
|
||||
@"unmountComponentAtNodeAndRemoveContainer": @{
|
||||
@"methodID": @(RCTReactIOSUnmountComponentAtNodeAndRemoveContainer),
|
||||
@"type": @"local"
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@"Bundler": @{
|
||||
@"moduleID": @(RCTModuleIDBundler),
|
||||
@"methods": @{
|
||||
@"runApplication": @{
|
||||
@"methodID": @(RCTBundlerRunApplication),
|
||||
@"type": @"local"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
#import "RCTContextExecutor.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTJavaScriptAppEngine.h"
|
||||
#import "RCTModuleIDs.h"
|
||||
#import "RCTTouchHandler.h"
|
||||
#import "RCTUIManager.h"
|
||||
#import "RCTUtils.h"
|
||||
#import "RCTViewManager.h"
|
||||
#import "RCTWebViewExecutor.h"
|
||||
#import "UIView+ReactKit.h"
|
||||
#import "RCTKeyCommands.h"
|
||||
|
||||
@@ -23,6 +23,8 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
|
||||
RCTTouchHandler *_touchHandler;
|
||||
}
|
||||
|
||||
static BOOL _useWebExec;
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
|
||||
@@ -34,6 +36,13 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
|
||||
action:^(UIKeyCommand *command) {
|
||||
[self reloadAll];
|
||||
}];
|
||||
// Cmd-D reloads using the web view executor, allows attaching from Safari dev tools.
|
||||
[[RCTKeyCommands sharedInstance] registerKeyCommandWithInput:@"d"
|
||||
modifierFlags:UIKeyModifierCommand
|
||||
action:^(UIKeyCommand *command) {
|
||||
_useWebExec = YES;
|
||||
[self reloadAll];
|
||||
}];
|
||||
|
||||
#endif
|
||||
|
||||
@@ -98,8 +107,8 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
|
||||
@"rootTag": self.reactTag ?: @0,
|
||||
@"initialProps": self.initialProperties ?: @{},
|
||||
};
|
||||
[_bridge enqueueJSCall:RCTModuleIDBundler
|
||||
methodID:RCTBundlerRunApplication
|
||||
|
||||
[_bridge enqueueJSCall:@"Bundler.runApplication"
|
||||
args:@[moduleName, appParameters]];
|
||||
}
|
||||
}
|
||||
@@ -123,9 +132,13 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
|
||||
[_executor invalidate];
|
||||
[_bridge invalidate];
|
||||
|
||||
_executor = [[RCTContextExecutor alloc] init];
|
||||
_bridge = [[RCTBridge alloc] initWithJavaScriptExecutor:_executor
|
||||
javaScriptModulesConfig:[RCTModuleIDs config]];
|
||||
if (!_useWebExec) {
|
||||
_executor = [[RCTContextExecutor alloc] init];
|
||||
} else {
|
||||
_executor = [[RCTWebViewExecutor alloc] init];
|
||||
}
|
||||
|
||||
_bridge = [[RCTBridge alloc] initWithJavaScriptExecutor:_executor];
|
||||
|
||||
_appEngine = [[RCTJavaScriptAppEngine alloc] initWithBridge:_bridge];
|
||||
_touchHandler = [[RCTTouchHandler alloc] initWithEventDispatcher:_bridge.eventDispatcher rootView:self];
|
||||
@@ -165,22 +178,4 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTRootViewReloadNotification object:nil];
|
||||
}
|
||||
|
||||
#pragma mark - Key commands
|
||||
|
||||
- (NSArray *)keyCommands
|
||||
{
|
||||
return @[
|
||||
|
||||
// Reload
|
||||
[UIKeyCommand keyCommandWithInput:@"r"
|
||||
modifierFlags:UIKeyModifierCommand
|
||||
action:@selector(reload)]
|
||||
];
|
||||
}
|
||||
|
||||
- (BOOL)canBecomeFirstResponder
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
|
||||
@interface RCTSparseArray : NSObject <NSCopying>
|
||||
|
||||
- (instancetype)init;
|
||||
- (instancetype)initWithCapacity:(NSUInteger)capacity;
|
||||
- (instancetype)initWithSparseArray:(RCTSparseArray *)sparseArray;
|
||||
- (instancetype)initWithCapacity:(NSUInteger)capacity NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)initWithSparseArray:(RCTSparseArray *)sparseArray NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
+ (instancetype)sparseArray;
|
||||
+ (instancetype)sparseArrayWithCapacity:(NSUInteger)capacity;
|
||||
|
||||
@@ -28,14 +28,6 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithStorage:(NSDictionary *)storage
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_storage = [storage copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)sparseArray
|
||||
{
|
||||
return [[self alloc] init];
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
#import "RCTUtils.h"
|
||||
#import "UIView+ReactKit.h"
|
||||
|
||||
// TODO: this class behaves a lot like a module, and could be implemented as a
|
||||
// module if we were to assume that modules and RootViews had a 1:1 relationship
|
||||
|
||||
@implementation RCTTouchHandler
|
||||
{
|
||||
__weak UIView *_rootView;
|
||||
@@ -27,11 +30,6 @@
|
||||
NSMutableArray *_touchViews;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
RCT_NOT_DESIGNATED_INITIALIZER();
|
||||
}
|
||||
|
||||
- (instancetype)initWithTarget:(id)target action:(SEL)action
|
||||
{
|
||||
RCT_NOT_DESIGNATED_INITIALIZER();
|
||||
|
||||
@@ -20,8 +20,9 @@ id RCTJSONParse(NSString *jsonString, NSError **error);
|
||||
// Get MD5 hash of a string
|
||||
NSString *RCTMD5Hash(NSString *string);
|
||||
|
||||
// Get screen scale in a thread-safe way
|
||||
// Get screen metrics in a thread-safe way
|
||||
CGFloat RCTScreenScale(void);
|
||||
CGSize RCTScreenSize(void);
|
||||
|
||||
// Round float coordinates to nearest whole screen pixel (not point)
|
||||
CGFloat RCTRoundPixelValue(CGFloat value);
|
||||
@@ -34,3 +35,7 @@ NSTimeInterval RCTTGetAbsoluteTime(void);
|
||||
// Method swizzling
|
||||
void RCTSwapClassMethods(Class cls, SEL original, SEL replacement);
|
||||
void RCTSwapInstanceMethods(Class cls, SEL original, SEL replacement);
|
||||
|
||||
// Module subclass support
|
||||
BOOL RCTClassOverridesClassMethod(Class cls, SEL selector);
|
||||
BOOL RCTClassOverridesInstanceMethod(Class cls, SEL selector);
|
||||
|
||||
@@ -35,7 +35,7 @@ NSString *RCTMD5Hash(NSString *string)
|
||||
|
||||
CGFloat RCTScreenScale()
|
||||
{
|
||||
static CGFloat scale = -1;
|
||||
static CGFloat scale;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
if (![NSThread isMainThread]) {
|
||||
@@ -50,6 +50,23 @@ CGFloat RCTScreenScale()
|
||||
return scale;
|
||||
}
|
||||
|
||||
CGSize RCTScreenSize()
|
||||
{
|
||||
static CGSize size;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
if (![NSThread isMainThread]) {
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
size = [UIScreen mainScreen].bounds.size;
|
||||
});
|
||||
} else {
|
||||
size = [UIScreen mainScreen].bounds.size;
|
||||
}
|
||||
});
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
CGFloat RCTRoundPixelValue(CGFloat value)
|
||||
{
|
||||
CGFloat scale = RCTScreenScale();
|
||||
@@ -119,4 +136,25 @@ void RCTSwapInstanceMethods(Class cls, SEL original, SEL replacement)
|
||||
{
|
||||
method_exchangeImplementations(originalMethod, replacementMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOL RCTClassOverridesClassMethod(Class cls, SEL selector)
|
||||
{
|
||||
return RCTClassOverridesInstanceMethod(object_getClass(cls), selector);
|
||||
}
|
||||
|
||||
BOOL RCTClassOverridesInstanceMethod(Class cls, SEL selector)
|
||||
{
|
||||
unsigned int numberOfMethods;
|
||||
Method *methods = class_copyMethodList(cls, &numberOfMethods);
|
||||
for (unsigned int i = 0; i < numberOfMethods; i++)
|
||||
{
|
||||
if (method_getName(methods[i]) == selector)
|
||||
{
|
||||
free(methods);
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user