diff --git a/Examples/2048/Game2048.js b/Examples/2048/Game2048.js
index a6e12ff13..a6e041ceb 100644
--- a/Examples/2048/Game2048.js
+++ b/Examples/2048/Game2048.js
@@ -73,19 +73,29 @@ class Tile extends React.Component {
if (tile.isNew()) {
offset.opacity = 0;
} else {
- var point = [
- animationPosition(tile.toColumn()),
- animationPosition(tile.toRow()),
- ];
- AnimationExperimental.startAnimation(this.refs['this'], 100, 0, 'easeInOutQuad', {position: point});
+ var point = {
+ x: animationPosition(tile.toColumn()),
+ y: animationPosition(tile.toRow()),
+ };
+ AnimationExperimental.startAnimation({
+ node: this.refs['this'],
+ duration: 100,
+ easing: 'easeInOutQuad',
+ property: 'position',
+ toValue: point,
+ });
}
-
return offset;
}
-
componentDidMount() {
- AnimationExperimental.startAnimation(this.refs['this'], 100, 0, 'easeInOutQuad', {opacity: 1});
+ AnimationExperimental.startAnimation({
+ node: this.refs['this'],
+ duration: 100,
+ easing: 'easeInOutQuad',
+ property: 'opacity',
+ toValue: 1,
+ });
}
render() {
diff --git a/Examples/SampleApp/SampleApp.xcodeproj/project.pbxproj b/Examples/SampleApp/SampleApp.xcodeproj/project.pbxproj
index 5b716c3c0..2ea052a2e 100644
--- a/Examples/SampleApp/SampleApp.xcodeproj/project.pbxproj
+++ b/Examples/SampleApp/SampleApp.xcodeproj/project.pbxproj
@@ -8,7 +8,6 @@
/* Begin PBXBuildFile section */
00481BE81AC0C86700671115 /* libRCTWebSocketDebugger.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00481BE61AC0C7FA00671115 /* libRCTWebSocketDebugger.a */; };
- 00481BEA1AC0C89D00671115 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 00481BE91AC0C89D00671115 /* libicucore.dylib */; };
008F07F31AC5B25A0029DE68 /* main.jsbundle in Resources */ = {isa = PBXBuildFile; fileRef = 008F07F21AC5B25A0029DE68 /* main.jsbundle */; };
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; };
00C302E61ABCBA2D00DB3ED1 /* libRCTAdSupport.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302B41ABCB8E700DB3ED1 /* libRCTAdSupport.a */; };
@@ -116,7 +115,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 00481BEA1AC0C89D00671115 /* libicucore.dylib in Frameworks */,
146834051AC3E58100842450 /* libReact.a in Frameworks */,
00481BE81AC0C86700671115 /* libRCTWebSocketDebugger.a in Frameworks */,
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */,
diff --git a/Examples/SampleApp/iOS/Info.plist b/Examples/SampleApp/iOS/Info.plist
index c6d2494cc..0a56a6ea8 100644
--- a/Examples/SampleApp/iOS/Info.plist
+++ b/Examples/SampleApp/iOS/Info.plist
@@ -36,5 +36,7 @@
UIViewControllerBasedStatusBarAppearance
+ NSLocationWhenInUseUsageDescription
+
diff --git a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj
index 030e2576c..348d04f0d 100644
--- a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj
+++ b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj
@@ -9,7 +9,6 @@
/* Begin PBXBuildFile section */
004D28A31AAF61C70097A701 /* UIExplorerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 004D28A21AAF61C70097A701 /* UIExplorerTests.m */; };
00D2771A1AB8C3E100DC1E48 /* libRCTWebSocketDebugger.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00D277131AB8C2C700DC1E48 /* libRCTWebSocketDebugger.a */; };
- 00D2771C1AB8C55500DC1E48 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 00D2771B1AB8C55500DC1E48 /* libicucore.dylib */; };
13417FE91AA91432003F314A /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13417FE81AA91428003F314A /* libRCTImage.a */; };
134180011AA9153C003F314A /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13417FEF1AA914B8003F314A /* libRCTText.a */; };
1341802C1AA9178B003F314A /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1341802B1AA91779003F314A /* libRCTNetwork.a */; };
@@ -118,7 +117,6 @@
004D28A11AAF61C70097A701 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
004D28A21AAF61C70097A701 /* UIExplorerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UIExplorerTests.m; sourceTree = ""; };
00D2770E1AB8C2C700DC1E48 /* RCTWebSocketDebugger.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocketDebugger.xcodeproj; path = ../../Libraries/RCTWebSocketDebugger/RCTWebSocketDebugger.xcodeproj; sourceTree = ""; };
- 00D2771B1AB8C55500DC1E48 /* libicucore.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libicucore.dylib; path = usr/lib/libicucore.dylib; sourceTree = SDKROOT; };
13417FE31AA91428003F314A /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = ../../Libraries/Image/RCTImage.xcodeproj; sourceTree = ""; };
13417FEA1AA914B8003F314A /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = ../../Libraries/Text/RCTText.xcodeproj; sourceTree = ""; };
134180261AA91779003F314A /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = ../../Libraries/Network/RCTNetwork.xcodeproj; sourceTree = ""; };
@@ -150,7 +148,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 00D2771C1AB8C55500DC1E48 /* libicucore.dylib in Frameworks */,
14AADF051AC3DBB1002390C9 /* libReact.a in Frameworks */,
00D2771A1AB8C3E100DC1E48 /* libRCTWebSocketDebugger.a in Frameworks */,
58005BF21ABA80A60062E044 /* libRCTTest.a in Frameworks */,
@@ -207,7 +204,6 @@
13417FEA1AA914B8003F314A /* RCTText.xcodeproj */,
00D2770E1AB8C2C700DC1E48 /* RCTWebSocketDebugger.xcodeproj */,
D85B82911AB6D5CE003F4FE2 /* RCTVibration.xcodeproj */,
- 00D2771B1AB8C55500DC1E48 /* libicucore.dylib */,
);
name = Libraries;
sourceTree = "";
diff --git a/Examples/UIExplorer/UIExplorer.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme b/Examples/UIExplorer/UIExplorer.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme
index 2189d2d0e..b231b77ee 100644
--- a/Examples/UIExplorer/UIExplorer.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme
+++ b/Examples/UIExplorer/UIExplorer.xcodeproj/xcshareddata/xcschemes/UIExplorer.xcscheme
@@ -72,7 +72,8 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
-
+
-
+
+@interface RCTActionSheetManager ()
@end
-@implementation RCTActionSheetManager {
+@implementation RCTActionSheetManager
+{
NSMutableDictionary *_callbacks;
}
+RCT_EXPORT_MODULE()
+
- (instancetype)init
{
if ((self = [super init])) {
@@ -27,12 +30,10 @@
return self;
}
-- (void)showActionSheetWithOptions:(NSDictionary *)options
- failureCallback:(RCTResponseSenderBlock)failureCallback
- successCallback:(RCTResponseSenderBlock)successCallback
+RCT_EXPORT_METHOD(showActionSheetWithOptions:(NSDictionary *)options
+ failureCallback:(RCTResponseSenderBlock)failureCallback
+ successCallback:(RCTResponseSenderBlock)successCallback)
{
- RCT_EXPORT();
-
dispatch_async(dispatch_get_main_queue(), ^{
UIActionSheet *actionSheet = [[UIActionSheet alloc] init];
@@ -62,12 +63,10 @@
});
}
-- (void)showShareActionSheetWithOptions:(NSDictionary *)options
- failureCallback:(RCTResponseSenderBlock)failureCallback
- successCallback:(RCTResponseSenderBlock)successCallback
+RCT_EXPORT_METHOD(showShareActionSheetWithOptions:(NSDictionary *)options
+ failureCallback:(RCTResponseSenderBlock)failureCallback
+ successCallback:(RCTResponseSenderBlock)successCallback)
{
- RCT_EXPORT();
-
dispatch_async(dispatch_get_main_queue(), ^{
NSMutableArray *items = [NSMutableArray array];
id message = options[@"message"];
@@ -134,7 +133,7 @@
#pragma mark Private
-static NSString *keyForInstance(id instance)
+NS_INLINE NSString *keyForInstance(id instance)
{
return [NSString stringWithFormat:@"%p", instance];
}
diff --git a/Libraries/AdSupport/RCTAdSupport.m b/Libraries/AdSupport/RCTAdSupport.m
index e6be96f6c..1b2ad92de 100644
--- a/Libraries/AdSupport/RCTAdSupport.m
+++ b/Libraries/AdSupport/RCTAdSupport.m
@@ -11,10 +11,11 @@
@implementation RCTAdSupport
-- (void)getAdvertisingId:(RCTResponseSenderBlock)callback withErrorCallback:(RCTResponseSenderBlock)errorCallback
-{
- RCT_EXPORT();
+RCT_EXPORT_MODULE()
+RCT_EXPORT_METHOD(getAdvertisingId:(RCTResponseSenderBlock)callback
+ withErrorCallback:(RCTResponseSenderBlock)errorCallback)
+{
if ([ASIdentifierManager class]) {
callback(@[[[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString]]);
} else {
@@ -22,12 +23,11 @@
}
}
-- (void)getAdvertisingTrackingEnabled:(RCTResponseSenderBlock)callback withErrorCallback:(RCTResponseSenderBlock)errorCallback
+RCT_EXPORT_METHOD(getAdvertisingTrackingEnabled:(RCTResponseSenderBlock)callback
+ withErrorCallback:(RCTResponseSenderBlock)errorCallback)
{
- RCT_EXPORT();
-
if ([ASIdentifierManager class]) {
- bool hasTracking = [[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled];
+ BOOL hasTracking = [[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled];
callback(@[@(hasTracking)]);
} else {
return errorCallback(@[@"as_identifier_unavailable"]);
diff --git a/Libraries/Animation/AnimationExperimental.js b/Libraries/Animation/AnimationExperimental.js
index 79daa4550..0a32c3f44 100644
--- a/Libraries/Animation/AnimationExperimental.js
+++ b/Libraries/Animation/AnimationExperimental.js
@@ -16,6 +16,17 @@ var AnimationUtils = require('AnimationUtils');
type EasingFunction = (t: number) => number;
+var Properties = {
+ opacity: true,
+ position: true,
+ positionX: true,
+ positionY: true,
+ rotation: true,
+ scaleXY: true,
+};
+
+type ValueType = number | Array | {[key: string]: number};
+
/**
* This is an experimental module that is under development, incomplete,
* potentially buggy, not used in any production apps, and will probably change
@@ -24,24 +35,34 @@ type EasingFunction = (t: number) => number;
* Use at your own risk.
*/
var AnimationExperimental = {
- Mixin: require('AnimationExperimentalMixin'),
-
startAnimation: function(
- node: any,
- duration: number,
- delay: number,
- easing: (string | EasingFunction),
- properties: {[key: string]: any}
+ anim: {
+ node: any;
+ duration: number;
+ easing: ($Enum | EasingFunction);
+ property: $Enum;
+ toValue: ValueType;
+ fromValue?: ValueType;
+ delay?: number;
+ },
+ callback?: ?(finished: bool) => void
): number {
- var nodeHandle = +node.getNodeHandle();
- var easingSample = AnimationUtils.evaluateEasingFunction(duration, easing);
- var tag: number = RCTAnimationManager.startAnimation(
+ var nodeHandle = anim.node.getNodeHandle();
+ var easingSample = AnimationUtils.evaluateEasingFunction(
+ anim.duration,
+ anim.easing
+ );
+ var tag: number = AnimationUtils.allocateTag();
+ var props = {};
+ props[anim.property] = {to: anim.toValue};
+ RCTAnimationManager.startAnimation(
nodeHandle,
- AnimationUtils.allocateTag(),
- duration,
- delay,
+ tag,
+ anim.duration,
+ anim.delay,
easingSample,
- properties
+ props,
+ callback
);
return tag;
},
@@ -51,4 +72,18 @@ var AnimationExperimental = {
},
};
+if (__DEV__) {
+ if (RCTAnimationManager && RCTAnimationManager.Properties) {
+ var a = Object.keys(Properties);
+ var b = RCTAnimationManager.Properties;
+ var diff = a.filter((i) => b.indexOf(i) < 0).concat(
+ b.filter((i) => a.indexOf(i) < 0)
+ );
+ if (diff.length > 0) {
+ throw new Error('JS animation properties don\'t match native properties.' +
+ JSON.stringify(diff, null, ' '));
+ }
+ }
+}
+
module.exports = AnimationExperimental;
diff --git a/Libraries/Animation/AnimationExperimentalMixin.js b/Libraries/Animation/AnimationExperimentalMixin.js
deleted file mode 100644
index 7cee9b72b..000000000
--- a/Libraries/Animation/AnimationExperimentalMixin.js
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * Copyright (c) 2015-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- * @providesModule AnimationExperimentalMixin
- * @flow
- */
-'use strict';
-
-var AnimationUtils = require('AnimationUtils');
-var RCTAnimationManager = require('NativeModules').AnimationExperimentalManager;
-
-var invariant = require('invariant');
-
-type EasingFunction = (t: number) => number;
-
-/**
- * This is an experimental module that is under development, incomplete,
- * potentially buggy, not used in any production apps, and will probably change
- * in non-backward compatible ways.
- *
- * Use at your own risk.
- */
-var AnimationExperimentalMixin = {
- getInitialState: function(): Object {
- return {};
- },
-
- startAnimation: function(
- refKey: string,
- duration: number,
- delay: number,
- easing: (string | EasingFunction),
- properties: {[key: string]: any}
- ): number {
- var ref = this.refs[refKey];
- invariant(
- ref,
- 'Invalid refKey ' + refKey + '; ' +
- 'valid refs: ' + JSON.stringify(Object.keys(this.refs))
- );
-
- var nodeHandle = +ref.getNodeHandle();
- var easingSample = AnimationUtils.evaluateEasingFunction(duration, easing);
- var tag: number = RCTAnimationManager.startAnimation(nodeHandle, AnimationUtils.allocateTag(), duration, delay, easingSample, properties);
- return tag;
- },
-
- stopAnimation: function(tag: number) {
- RCTAnimationManager.stopAnimation(tag);
- },
-};
-
-module.exports = AnimationExperimentalMixin;
diff --git a/Libraries/Animation/AnimationUtils.js b/Libraries/Animation/AnimationUtils.js
index ae9be5ccf..d6d95f62d 100644
--- a/Libraries/Animation/AnimationUtils.js
+++ b/Libraries/Animation/AnimationUtils.js
@@ -20,27 +20,27 @@
type EasingFunction = (t: number) => number;
var defaults = {
- easeInQuad: function(t) {
+ easeInQuad: function(t: number): number {
return t * t;
},
- easeOutQuad: function(t) {
+ easeOutQuad: function(t: number): number {
return -t * (t - 2);
},
- easeInOutQuad: function(t) {
+ easeInOutQuad: function(t: number): number {
t = t * 2;
if (t < 1) {
return 0.5 * t * t;
}
return -((t - 1) * (t - 3) - 1) / 2;
},
- easeInCubic: function(t) {
+ easeInCubic: function(t: number): number {
return t * t * t;
},
- easeOutCubic: function(t) {
+ easeOutCubic: function(t: number): number {
t -= 1;
return t * t * t + 1;
},
- easeInOutCubic: function(t) {
+ easeInOutCubic: function(t: number): number {
t *= 2;
if (t < 1) {
return 0.5 * t * t * t;
@@ -48,14 +48,14 @@ var defaults = {
t -= 2;
return (t * t * t + 2) / 2;
},
- easeInQuart: function(t) {
+ easeInQuart: function(t: number): number {
return t * t * t * t;
},
- easeOutQuart: function(t) {
+ easeOutQuart: function(t: number): number {
t -= 1;
return -(t * t * t * t - 1);
},
- easeInOutQuart: function(t) {
+ easeInOutQuart: function(t: number): number {
t *= 2;
if (t < 1) {
return 0.5 * t * t * t * t;
@@ -63,14 +63,14 @@ var defaults = {
t -= 2;
return -(t * t * t * t - 2) / 2;
},
- easeInQuint: function(t) {
+ easeInQuint: function(t: number): number {
return t * t * t * t * t;
},
- easeOutQuint: function(t) {
+ easeOutQuint: function(t: number): number {
t -= 1;
return t * t * t * t * t + 1;
},
- easeInOutQuint: function(t) {
+ easeInOutQuint: function(t: number): number {
t *= 2;
if (t < 1) {
return (t * t * t * t * t) / 2;
@@ -78,22 +78,22 @@ var defaults = {
t -= 2;
return (t * t * t * t * t + 2) / 2;
},
- easeInSine: function(t) {
+ easeInSine: function(t: number): number {
return -Math.cos(t * (Math.PI / 2)) + 1;
},
- easeOutSine: function(t) {
+ easeOutSine: function(t: number): number {
return Math.sin(t * (Math.PI / 2));
},
- easeInOutSine: function(t) {
+ easeInOutSine: function(t: number): number {
return -(Math.cos(Math.PI * t) - 1) / 2;
},
- easeInExpo: function(t) {
+ easeInExpo: function(t: number): number {
return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1));
},
- easeOutExpo: function(t) {
+ easeOutExpo: function(t: number): number {
return (t === 1) ? 1 : (-Math.pow(2, -10 * t) + 1);
},
- easeInOutExpo: function(t) {
+ easeInOutExpo: function(t: number): number {
if (t === 0) {
return 0;
}
@@ -106,14 +106,14 @@ var defaults = {
}
return (-Math.pow(2, -10 * (t - 1)) + 2) / 2;
},
- easeInCirc: function(t) {
+ easeInCirc: function(t: number): number {
return -(Math.sqrt(1 - t * t) - 1);
},
- easeOutCirc: function(t) {
+ easeOutCirc: function(t: number): number {
t -= 1;
return Math.sqrt(1 - t * t);
},
- easeInOutCirc: function(t) {
+ easeInOutCirc: function(t: number): number {
t *= 2;
if (t < 1) {
return -(Math.sqrt(1 - t * t) - 1) / 2;
@@ -121,7 +121,7 @@ var defaults = {
t -= 2;
return (Math.sqrt(1 - t * t) + 1) / 2;
},
- easeInElastic: function(t) {
+ easeInElastic: function(t: number): number {
var s = 1.70158;
var p = 0.3;
if (t === 0) {
@@ -134,7 +134,7 @@ var defaults = {
t -= 1;
return -(Math.pow(2, 10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
},
- easeOutElastic: function(t) {
+ easeOutElastic: function(t: number): number {
var s = 1.70158;
var p = 0.3;
if (t === 0) {
@@ -146,7 +146,7 @@ var defaults = {
var s = p / (2 * Math.PI) * Math.asin(1);
return Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1;
},
- easeInOutElastic: function(t) {
+ easeInOutElastic: function(t: number): number {
var s = 1.70158;
var p = 0.3 * 1.5;
if (t === 0) {
@@ -164,16 +164,16 @@ var defaults = {
t -= 1;
return Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) / 2 + 1;
},
- easeInBack: function(t) {
+ easeInBack: function(t: number): number {
var s = 1.70158;
return t * t * ((s + 1) * t - s);
},
- easeOutBack: function(t) {
+ easeOutBack: function(t: number): number {
var s = 1.70158;
t -= 1;
return (t * t * ((s + 1) * t + s) + 1);
},
- easeInOutBack: function(t) {
+ easeInOutBack: function(t: number): number {
var s = 1.70158 * 1.525;
t *= 2;
if (t < 1) {
@@ -182,10 +182,10 @@ var defaults = {
t -= 2;
return (t * t * ((s + 1) * t + s) + 2) / 2;
},
- easeInBounce: function(t) {
+ easeInBounce: function(t: number): number {
return 1 - this.easeOutBounce(1 - t);
},
- easeOutBounce: function(t) {
+ easeOutBounce: function(t: number): number {
if (t < (1 / 2.75)) {
return 7.5625 * t * t;
} else if (t < (2 / 2.75)) {
@@ -199,7 +199,7 @@ var defaults = {
return 7.5625 * t * t + 0.984375;
}
},
- easeInOutBounce: function(t) {
+ easeInOutBounce: function(t: number): number {
if (t < 0.5) {
return this.easeInBounce(t * 2) / 2;
}
@@ -234,4 +234,6 @@ module.exports = {
return samples;
},
+
+ Defaults: defaults,
};
diff --git a/Libraries/Animation/LayoutAnimation.js b/Libraries/Animation/LayoutAnimation.js
index 12128055c..c297123ba 100644
--- a/Libraries/Animation/LayoutAnimation.js
+++ b/Libraries/Animation/LayoutAnimation.js
@@ -17,18 +17,20 @@ var RCTUIManager = require('NativeModules').UIManager;
var createStrictShapeTypeChecker = require('createStrictShapeTypeChecker');
var keyMirror = require('keyMirror');
-var Types = keyMirror({
+var TypesEnum = {
spring: true,
linear: true,
easeInEaseOut: true,
easeIn: true,
easeOut: true,
-});
+};
+var Types = keyMirror(TypesEnum);
-var Properties = keyMirror({
+var PropertiesEnum = {
opacity: true,
scaleXY: true,
-});
+};
+var Properties = keyMirror(PropertiesEnum);
var animChecker = createStrictShapeTypeChecker({
duration: PropTypes.number,
@@ -48,8 +50,8 @@ type Anim = {
delay?: number;
springDamping?: number;
initialVelocity?: number;
- type?: $Enum;
- property?: $Enum;
+ type?: $Enum;
+ property?: $Enum;
}
var configChecker = createStrictShapeTypeChecker({
diff --git a/Libraries/Animation/RCTAnimationExperimentalManager.m b/Libraries/Animation/RCTAnimationExperimentalManager.m
index b7c76c9f5..eb2ddd1cd 100644
--- a/Libraries/Animation/RCTAnimationExperimentalManager.m
+++ b/Libraries/Animation/RCTAnimationExperimentalManager.m
@@ -13,6 +13,7 @@
#import "RCTSparseArray.h"
#import "RCTUIManager.h"
+#import "RCTUtils.h"
#if CGFLOAT_IS_DOUBLE
#define CG_APPEND(PREFIX, SUFFIX_F, SUFFIX_D) PREFIX##SUFFIX_D
@@ -23,14 +24,45 @@
@implementation RCTAnimationExperimentalManager
{
RCTSparseArray *_animationRegistry; // Main thread only; animation tag -> view tag
+ RCTSparseArray *_callbackRegistry; // Main thread only; animation tag -> callback
+ NSDictionary *_keypathMapping;
}
+RCT_EXPORT_MODULE()
+
@synthesize bridge = _bridge;
- (instancetype)init
{
if ((self = [super init])) {
_animationRegistry = [[RCTSparseArray alloc] init];
+ _callbackRegistry = [[RCTSparseArray alloc] init];
+ _keypathMapping = @{
+ @"opacity": @{
+ @"keypath": @"opacity",
+ @"type": @"NSNumber",
+ },
+ @"position": @{
+ @"keypath": @"position",
+ @"type": @"CGPoint",
+ },
+ @"positionX": @{
+ @"keypath": @"position.x",
+ @"type": @"NSNumber",
+ },
+ @"positionY": @{
+ @"keypath": @"position.y",
+ @"type": @"NSNumber",
+ },
+ @"rotation": @{
+ @"keypath": @"transform.rotation.z",
+ @"type": @"NSNumber",
+ },
+ @"scaleXY": @{
+ @"keypath": @"transform.scale",
+ @"type": @"CGPoint",
+ },
+ };
}
return self;
@@ -61,10 +93,26 @@
};
}
-- (void)startAnimationForTag:(NSNumber *)reactTag animationTag:(NSNumber *)animationTag duration:(double)duration delay:(double)delay easingSample:(NSArray *)easingSample properties:(NSDictionary *)properties
+static void RCTInvalidAnimationProp(RCTSparseArray *callbacks, NSNumber *tag, NSString *key, id value)
{
- RCT_EXPORT(startAnimation);
+ RCTResponseSenderBlock callback = callbacks[tag];
+ RCTLogError(@"Invalid animation property `%@ = %@`", key, value);
+ if (callback) {
+ callback(@[@NO]);
+ callbacks[tag] = nil;
+ }
+ [CATransaction commit];
+ return;
+}
+RCT_EXPORT_METHOD(startAnimation:(NSNumber *)reactTag
+ animationTag:(NSNumber *)animationTag
+ duration:(NSTimeInterval)duration
+ delay:(NSTimeInterval)delay
+ easingSample:(NSArray *)easingSample
+ properties:(NSDictionary *)properties
+ callback:(RCTResponseSenderBlock)callback)
+{
__weak RCTAnimationExperimentalManager *weakSelf = self;
[_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
RCTAnimationExperimentalManager *strongSelf = weakSelf;
@@ -74,12 +122,21 @@
RCTLogWarn(@"React tag #%@ is not registered with the view registry", reactTag);
return;
}
-
- [properties enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
+ __block BOOL completionBlockSet = NO;
+ [CATransaction begin];
+ for (NSString *prop in properties) {
+ NSString *keypath = _keypathMapping[prop][@"keypath"];
+ id obj = properties[prop][@"to"];
+ if (!keypath) {
+ return RCTInvalidAnimationProp(strongSelf->_callbackRegistry, animationTag, keypath, obj);
+ }
NSValue *toValue = nil;
- if ([key isEqualToString:@"scaleXY"]) {
- key = @"transform.scale";
- toValue = obj[0];
+ if ([keypath isEqualToString:@"transform.scale"]) {
+ CGPoint point = [RCTConvert CGPoint:obj];
+ if (point.x != point.y) {
+ return RCTInvalidAnimationProp(strongSelf->_callbackRegistry, animationTag, keypath, obj);
+ }
+ toValue = @(point.x);
} else if ([obj respondsToSelector:@selector(count)]) {
switch ([obj count]) {
case 2:
@@ -95,11 +152,15 @@
case 16:
toValue = [NSValue valueWithCGAffineTransform:[RCTConvert CGAffineTransform:obj]];
break;
+ default:
+ return RCTInvalidAnimationProp(strongSelf->_callbackRegistry, animationTag, keypath, obj);
}
+ } else if (![obj respondsToSelector:@selector(objCType)]) {
+ return RCTInvalidAnimationProp(strongSelf->_callbackRegistry, animationTag, keypath, obj);
+ }
+ if (!toValue) {
+ toValue = obj;
}
-
- if (!toValue) toValue = obj;
-
const char *typeName = toValue.objCType;
size_t count;
@@ -150,7 +211,7 @@
break;
}
- NSValue *fromValue = [view.layer.presentationLayer valueForKeyPath:key];
+ NSValue *fromValue = [view.layer.presentationLayer valueForKeyPath:keypath];
CGFloat fromFields[count];
[fromValue getValue:fromFields];
@@ -161,27 +222,38 @@
CGFloat t = sample.CG_APPEND(, floatValue, doubleValue);
[sampledValues addObject:interpolationBlock(t)];
}
-
- CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:key];
- animation.beginTime = CACurrentMediaTime() + delay / 1000.0;
- animation.duration = duration / 1000.0;
+ CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:keypath];
+ animation.beginTime = CACurrentMediaTime() + delay;
+ animation.duration = duration;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animation.values = sampledValues;
-
- [view.layer setValue:toValue forKey:key];
-
- NSString *animationKey = [NSString stringWithFormat:@"RCT.%@.%@", animationTag, key];
- [view.layer addAnimation:animation forKey:animationKey];
- }];
-
+ @try {
+ [view.layer setValue:toValue forKey:keypath];
+ NSString *animationKey = [@"RCT" stringByAppendingString:RCTJSONStringify(@{@"tag": animationTag, @"key": keypath}, nil)];
+ [view.layer addAnimation:animation forKey:animationKey];
+ if (!completionBlockSet) {
+ strongSelf->_callbackRegistry[animationTag] = callback;
+ [CATransaction setCompletionBlock:^{
+ RCTResponseSenderBlock cb = strongSelf->_callbackRegistry[animationTag];
+ if (cb) {
+ cb(@[@YES]);
+ strongSelf->_callbackRegistry[animationTag] = nil;
+ }
+ }];
+ completionBlockSet = YES;
+ }
+ }
+ @catch (NSException *exception) {
+ return RCTInvalidAnimationProp(strongSelf->_callbackRegistry, animationTag, keypath, toValue);
+ }
+ }
+ [CATransaction commit];
strongSelf->_animationRegistry[animationTag] = reactTag;
}];
}
-- (void)stopAnimation:(NSNumber *)animationTag
+RCT_EXPORT_METHOD(stopAnimation:(NSNumber *)animationTag)
{
- RCT_EXPORT(stopAnimation);
-
__weak RCTAnimationExperimentalManager *weakSelf = self;
[_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
RCTAnimationExperimentalManager *strongSelf = weakSelf;
@@ -191,19 +263,25 @@
UIView *view = viewRegistry[reactTag];
for (NSString *animationKey in view.layer.animationKeys) {
- if ([animationKey hasPrefix:@"RCT"]) {
- NSRange periodLocation = [animationKey rangeOfString:@"." options:0 range:(NSRange){3, animationKey.length - 3}];
- if (periodLocation.location != NSNotFound) {
- NSInteger integerTag = [[animationKey substringWithRange:(NSRange){3, periodLocation.location}] integerValue];
- if (animationTag.integerValue == integerTag) {
- [view.layer removeAnimationForKey:animationKey];
- }
+ if ([animationKey hasPrefix:@"RCT{"]) {
+ NSDictionary *data = RCTJSONParse([animationKey substringFromIndex:3], nil);
+ if (animationTag.integerValue == [data[@"tag"] integerValue]) {
+ [view.layer removeAnimationForKey:animationKey];
}
}
}
-
+ RCTResponseSenderBlock cb = strongSelf->_callbackRegistry[animationTag];
+ if (cb) {
+ cb(@[@NO]);
+ strongSelf->_callbackRegistry[animationTag] = nil;
+ }
strongSelf->_animationRegistry[animationTag] = nil;
}];
}
+- (NSDictionary *)constantsToExport
+{
+ return @{@"Properties": [_keypathMapping allKeys] };
+}
+
@end
diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js
index dc29af70a..db6ceea33 100644
--- a/Libraries/Components/TextInput/TextInput.js
+++ b/Libraries/Components/TextInput/TextInput.js
@@ -492,7 +492,7 @@ var TextInput = React.createClass({
_renderAndroid: function() {
var autoCapitalize = autoCapitalizeConsts[this.props.autoCapitalize];
- return (
+ var textContainer =
+ value={this.state.bufferedValue}
+ />;
+
+ return (
+
+ {textContainer}
+
);
},
diff --git a/Libraries/Components/Touchable/TouchableBounce.js b/Libraries/Components/Touchable/TouchableBounce.js
index ffcc8e737..7cba22164 100644
--- a/Libraries/Components/Touchable/TouchableBounce.js
+++ b/Libraries/Components/Touchable/TouchableBounce.js
@@ -22,7 +22,7 @@ var copyProperties = require('copyProperties');
var onlyChild = require('onlyChild');
type State = {
- animationID: ?number;
+ animationID: ?number;
};
/**
@@ -60,7 +60,7 @@ var TouchableBounce = React.createClass({
value: number,
velocity: number,
bounciness: number,
- fromValue?: ?Function | number,
+ fromValue?: ?number,
callback?: ?Function
) {
if (POPAnimation) {
@@ -71,21 +71,21 @@ var TouchableBounce = React.createClass({
toValue: [value, value],
velocity: [velocity, velocity],
springBounciness: bounciness,
- fromValue: (undefined: ?any),
+ fromValue: fromValue ? [fromValue, fromValue] : undefined,
};
- if (fromValue) {
- anim.fromValue = [fromValue, fromValue];
- }
this.state.animationID = POPAnimation.createSpringAnimation(anim);
this.addAnimation(this.state.animationID, callback);
} else {
- AnimationExperimental.startAnimation(this, 300, 0, 'easeOutBack', {scaleXY: [value, value]});
- if (fromValue && typeof fromValue === 'function') {
- callback = fromValue;
- }
- if (callback) {
- setTimeout(callback, 300);
- }
+ AnimationExperimental.startAnimation(
+ {
+ node: this,
+ duration: 300,
+ easing: 'easeOutBack',
+ property: 'scaleXY',
+ toValue: { x: value, y: value},
+ },
+ callback
+ );
}
},
diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js
index 0ad3c4269..f7af45ffe 100644
--- a/Libraries/CustomComponents/Navigator/Navigator.js
+++ b/Libraries/CustomComponents/Navigator/Navigator.js
@@ -28,7 +28,6 @@
var AnimationsDebugModule = require('NativeModules').AnimationsDebugModule;
var BackAndroid = require('BackAndroid');
-var Dimensions = require('Dimensions');
var InteractionMixin = require('InteractionMixin');
var NavigatorBreadcrumbNavigationBar = require('NavigatorBreadcrumbNavigationBar');
var NavigatorInterceptor = require('NavigatorInterceptor');
@@ -52,9 +51,6 @@ var rebound = require('rebound');
var PropTypes = React.PropTypes;
-var SCREEN_WIDTH = Dimensions.get('window').width;
-var SCREEN_HEIGHT = Dimensions.get('window').height;
-
var OFF_SCREEN = {style: {opacity: 0}};
var __uid = 0;
@@ -69,8 +65,11 @@ var styles = StyleSheet.create({
overflow: 'hidden',
},
defaultSceneStyle: {
- width: SCREEN_WIDTH,
- height: SCREEN_HEIGHT,
+ position: 'absolute',
+ left: 0,
+ right: 0,
+ bottom: 0,
+ top: 0,
},
presentNavItem: {
position: 'absolute',
@@ -88,7 +87,7 @@ var styles = StyleSheet.create({
},
transitioner: {
flex: 1,
- backgroundColor: '#555555',
+ backgroundColor: 'transparent',
overflow: 'hidden',
}
});
@@ -510,7 +509,7 @@ var Navigator = React.createClass({
this.state.fromIndex = this.state.presentedIndex;
this.state.toIndex = this.state.presentedIndex;
}
- this._hideOtherScenes(presentedIndex);
+ this._hideOtherScenes(this.state.presentedIndex);
},
_transitionToToIndexWithVelocity: function(v) {
@@ -596,7 +595,7 @@ var Navigator = React.createClass({
_handlePanResponderGrant: function(e, gestureState) {
invariant(
- this._expectingGestureGrant,
+ this._expectingGestureGrant || this.state.isAnimating,
'Responder granted unexpectedly.'
);
this._activeGestureAction = this._expectingGestureGrant;
diff --git a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js b/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js
index 3072074b2..ac16542ad 100644
--- a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js
+++ b/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js
@@ -34,7 +34,7 @@ var buildStyleInterpolator = require('buildStyleInterpolator');
var SCREEN_WIDTH = Dimensions.get('window').width;
var SCREEN_HEIGHT = Dimensions.get('window').height;
-var ToTheLeft = {
+var FadeToTheLeft = {
// Rotate *requires* you to break out each individual component of
// rotation (x, y, z, w)
transformTranslate: {
@@ -101,6 +101,23 @@ var ToTheLeft = {
},
};
+var ToTheLeft = {
+ transformTranslate: {
+ from: {x: 0, y: 0, z: 0},
+ to: {x: -Dimensions.get('window').width, y: 0, z: 0},
+ min: 0,
+ max: 1,
+ type: 'linear',
+ extrapolate: true,
+ round: PixelRatio.get(),
+ },
+ opacity: {
+ value: 1.0,
+ type: 'constant',
+ },
+};
+
+
var FromTheRight = {
opacity: {
value: 1.0,
@@ -271,7 +288,7 @@ var BaseConfig = {
// Animation interpolators for horizontal transitioning:
animationInterpolators: {
into: buildStyleInterpolator(FromTheRight),
- out: buildStyleInterpolator(ToTheLeft),
+ out: buildStyleInterpolator(FadeToTheLeft),
},
};
@@ -312,8 +329,12 @@ var NavigatorSceneConfigs = {
overswipe: BaseOverswipeConfig,
edgeHitWidth: null,
},
- }
- }
+ },
+ animationInterpolators: {
+ into: buildStyleInterpolator(FromTheRight),
+ out: buildStyleInterpolator(ToTheLeft),
+ },
+ },
};
module.exports = NavigatorSceneConfigs;
diff --git a/Libraries/Geolocation/Geolocation.ios.js b/Libraries/Geolocation/Geolocation.ios.js
index 6a022f76f..13fe40a23 100644
--- a/Libraries/Geolocation/Geolocation.ios.js
+++ b/Libraries/Geolocation/Geolocation.ios.js
@@ -23,11 +23,9 @@ var subscriptions = [];
var updatesEnabled = false;
/**
- * /!\ ATTENTION /!\
- * You need to add NSLocationWhenInUseUsageDescription key
- * in Info.plist to enable geolocation, otherwise it's going
- * to *fail silently*!
- * \!/ \!/
+ * You need to include the `NSLocationWhenInUseUsageDescription` key
+ * in Info.plist to enable geolocation. Geolocation is enabled by default
+ * when you create a project with `react-native init`.
*
* Geolocation follows the MDN specification:
* https://developer.mozilla.org/en-US/docs/Web/API/Geolocation
diff --git a/Libraries/Geolocation/RCTLocationObserver.m b/Libraries/Geolocation/RCTLocationObserver.m
index d1182bbe6..6bb95acb0 100644
--- a/Libraries/Geolocation/RCTLocationObserver.m
+++ b/Libraries/Geolocation/RCTLocationObserver.m
@@ -99,6 +99,8 @@ static NSDictionary *RCTPositionError(RCTPositionErrorCode code, NSString *msg /
RCTLocationOptions _observerOptions;
}
+RCT_EXPORT_MODULE()
+
@synthesize bridge = _bridge;
#pragma mark - Lifecycle
@@ -151,9 +153,9 @@ static NSDictionary *RCTPositionError(RCTPositionErrorCode code, NSString *msg /
#pragma mark - Public API
-- (void)startObserving:(NSDictionary *)optionsJSON
+RCT_EXPORT_METHOD(startObserving:(NSDictionary *)optionsJSON)
{
- RCT_EXPORT();
+ [self checkLocationConfig];
dispatch_async(dispatch_get_main_queue(), ^{
@@ -170,10 +172,8 @@ static NSDictionary *RCTPositionError(RCTPositionErrorCode code, NSString *msg /
});
}
-- (void)stopObserving
+RCT_EXPORT_METHOD(stopObserving)
{
- RCT_EXPORT();
-
dispatch_async(dispatch_get_main_queue(), ^{
// Stop observing
@@ -187,11 +187,11 @@ static NSDictionary *RCTPositionError(RCTPositionErrorCode code, NSString *msg /
});
}
-- (void)getCurrentPosition:(NSDictionary *)optionsJSON
- withSuccessCallback:(RCTResponseSenderBlock)successBlock
- errorCallback:(RCTResponseSenderBlock)errorBlock
+RCT_EXPORT_METHOD(getCurrentPosition:(NSDictionary *)optionsJSON
+ withSuccessCallback:(RCTResponseSenderBlock)successBlock
+ errorCallback:(RCTResponseSenderBlock)errorBlock)
{
- RCT_EXPORT();
+ [self checkLocationConfig];
if (!successBlock) {
RCTLogError(@"%@.getCurrentPosition called with nil success parameter.", [self class]);
@@ -323,4 +323,11 @@ static NSDictionary *RCTPositionError(RCTPositionErrorCode code, NSString *msg /
_locationManager.desiredAccuracy = RCT_DEFAULT_LOCATION_ACCURACY;
}
+- (void)checkLocationConfig
+{
+ if (![[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"]) {
+ RCTLogError(@"NSLocationWhenInUseUsageDescription key must be present in Info.plist to use geolocation.");
+ }
+}
+
@end
diff --git a/Libraries/Image/RCTCameraRollManager.m b/Libraries/Image/RCTCameraRollManager.m
index b0734b1bd..8e6c8a532 100644
--- a/Libraries/Image/RCTCameraRollManager.m
+++ b/Libraries/Image/RCTCameraRollManager.m
@@ -14,15 +14,17 @@
#import
#import
- #import "RCTImageLoader.h"
+#import "RCTImageLoader.h"
#import "RCTLog.h"
@implementation RCTCameraRollManager
-- (void)saveImageWithTag:(NSString *)imageTag successCallback:(RCTResponseSenderBlock)successCallback errorCallback:(RCTResponseSenderBlock)errorCallback
-{
- RCT_EXPORT();
+RCT_EXPORT_MODULE()
+RCT_EXPORT_METHOD(saveImageWithTag:(NSString *)imageTag
+ successCallback:(RCTResponseSenderBlock)successCallback
+ errorCallback:(RCTResponseSenderBlock)errorCallback)
+{
[RCTImageLoader loadImageWithTag:imageTag callback:^(NSError *loadError, UIImage *loadedImage) {
if (loadError) {
errorCallback(@[[loadError localizedDescription]]);
@@ -59,10 +61,10 @@
}]);
}
-- (void)getPhotos:(NSDictionary *)params callback:(RCTResponseSenderBlock)callback errorCallback:(RCTResponseSenderBlock)errorCallback
+RCT_EXPORT_METHOD(getPhotos:(NSDictionary *)params
+ callback:(RCTResponseSenderBlock)callback
+ errorCallback:(RCTResponseSenderBlock)errorCallback)
{
- RCT_EXPORT();
-
NSUInteger first = [params[@"first"] integerValue];
NSString *afterCursor = params[@"after"];
NSString *groupTypesStr = params[@"groupTypes"];
diff --git a/Libraries/Image/RCTNetworkImageViewManager.m b/Libraries/Image/RCTNetworkImageViewManager.m
index 22268e8c0..005b726cf 100644
--- a/Libraries/Image/RCTNetworkImageViewManager.m
+++ b/Libraries/Image/RCTNetworkImageViewManager.m
@@ -18,6 +18,8 @@
@implementation RCTNetworkImageViewManager
+RCT_EXPORT_MODULE()
+
- (UIView *)view
{
RCTNetworkImageView *view = [[RCTNetworkImageView alloc] initWithFrame:CGRectZero imageDownloader:[RCTImageDownloader sharedInstance]];
diff --git a/Libraries/Image/RCTStaticImageManager.m b/Libraries/Image/RCTStaticImageManager.m
index 7668613bd..2d80117e4 100644
--- a/Libraries/Image/RCTStaticImageManager.m
+++ b/Libraries/Image/RCTStaticImageManager.m
@@ -18,6 +18,8 @@
@implementation RCTStaticImageManager
+RCT_EXPORT_MODULE()
+
- (UIView *)view
{
return [[RCTStaticImage alloc] init];
diff --git a/Libraries/LinkingIOS/RCTLinkingManager.m b/Libraries/LinkingIOS/RCTLinkingManager.m
index 565469c5d..33fac75f0 100644
--- a/Libraries/LinkingIOS/RCTLinkingManager.m
+++ b/Libraries/LinkingIOS/RCTLinkingManager.m
@@ -18,6 +18,8 @@ NSString *const RCTOpenURLNotification = @"RCTOpenURLNotification";
@synthesize bridge = _bridge;
+RCT_EXPORT_MODULE()
+
- (instancetype)init
{
if ((self = [super init])) {
@@ -52,19 +54,15 @@ NSString *const RCTOpenURLNotification = @"RCTOpenURLNotification";
body:[notification userInfo]];
}
-- (void)openURL:(NSString *)url
+RCT_EXPORT_METHOD(openURL:(NSURL *)url)
{
- RCT_EXPORT();
-
- [[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];
+ [[UIApplication sharedApplication] openURL:url];
}
-- (void)canOpenURL:(NSString *)url
- callback:(RCTResponseSenderBlock)callback
+RCT_EXPORT_METHOD(canOpenURL:(NSURL *)url
+ callback:(RCTResponseSenderBlock)callback)
{
- RCT_EXPORT();
-
- BOOL supported = [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:url]];
+ BOOL supported = [[UIApplication sharedApplication] canOpenURL:url];
callback(@[@(supported)]);
}
diff --git a/Libraries/Network/RCTDataManager.m b/Libraries/Network/RCTDataManager.m
index 13e3c37ac..6aa2842a3 100644
--- a/Libraries/Network/RCTDataManager.m
+++ b/Libraries/Network/RCTDataManager.m
@@ -15,17 +15,17 @@
@implementation RCTDataManager
+RCT_EXPORT_MODULE()
+
/**
* Executes a network request.
* The responseSender block won't be called on same thread as called.
*/
-- (void)executeQuery:(NSString *)queryType
- query:(id)query
- queryHash:(__unused NSString *)queryHash
- responseSender:(RCTResponseSenderBlock)responseSender
+RCT_EXPORT_METHOD(queryData:(NSString *)queryType
+ withQuery:(id)query
+ queryHash:(__unused NSString *)queryHash
+ responseSender:(RCTResponseSenderBlock)responseSender)
{
- RCT_EXPORT(queryData);
-
if ([queryType isEqualToString:@"http"]) {
// Parse query
diff --git a/Libraries/Network/RCTReachability.m b/Libraries/Network/RCTReachability.m
index 2dec4f812..b5f30de30 100644
--- a/Libraries/Network/RCTReachability.m
+++ b/Libraries/Network/RCTReachability.m
@@ -53,6 +53,8 @@ static void RCTReachabilityCallback(__unused SCNetworkReachabilityRef target, SC
}
}
+RCT_EXPORT_MODULE()
+
#pragma mark - Lifecycle
- (instancetype)initWithHost:(NSString *)host
@@ -81,11 +83,9 @@ static void RCTReachabilityCallback(__unused SCNetworkReachabilityRef target, SC
#pragma mark - Public API
// TODO: remove error callback - not needed except by Subscribable interface
-- (void)getCurrentReachability:(RCTResponseSenderBlock)getSuccess
- withErrorCallback:(__unused RCTResponseSenderBlock)getError
+RCT_EXPORT_METHOD(getCurrentReachability:(RCTResponseSenderBlock)getSuccess
+ withErrorCallback:(__unused RCTResponseSenderBlock)getError)
{
- RCT_EXPORT();
-
getSuccess(@[@{@"network_reachability": _status}]);
}
diff --git a/Libraries/PushNotificationIOS/RCTPushNotificationManager.m b/Libraries/PushNotificationIOS/RCTPushNotificationManager.m
index 6a54e457c..17ceb204c 100644
--- a/Libraries/PushNotificationIOS/RCTPushNotificationManager.m
+++ b/Libraries/PushNotificationIOS/RCTPushNotificationManager.m
@@ -19,6 +19,8 @@ NSString *const RCTRemoteNotificationReceived = @"RemoteNotificationReceived";
NSDictionary *_initialNotification;
}
+RCT_EXPORT_MODULE()
+
@synthesize bridge = _bridge;
- (instancetype)init
@@ -66,29 +68,23 @@ NSString *const RCTRemoteNotificationReceived = @"RemoteNotificationReceived";
/**
* Update the application icon badge number on the home screen
*/
-+ (void)setApplicationIconBadgeNumber:(NSInteger)number
+RCT_EXPORT_METHOD(setApplicationIconBadgeNumber:(NSInteger)number)
{
- RCT_EXPORT();
-
[UIApplication sharedApplication].applicationIconBadgeNumber = number;
}
/**
* Get the current application icon badge number on the home screen
*/
-+ (void)getApplicationIconBadgeNumber:(RCTResponseSenderBlock)callback
+RCT_EXPORT_METHOD(getApplicationIconBadgeNumber:(RCTResponseSenderBlock)callback)
{
- RCT_EXPORT();
-
callback(@[
@([UIApplication sharedApplication].applicationIconBadgeNumber)
]);
}
-+ (void)requestPermissions
+RCT_EXPORT_METHOD(requestPermissions)
{
- RCT_EXPORT();
-
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0
// if we are targeting iOS 7, *and* the new UIUserNotificationSettings
@@ -104,13 +100,10 @@ NSString *const RCTRemoteNotificationReceived = @"RemoteNotificationReceived";
UIUserNotificationType types = UIUserNotificationTypeSound | UIUserNotificationTypeBadge | UIUserNotificationTypeAlert;
UIUserNotificationSettings *notificationSettings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
-
}
-+ (void)checkPermissions:(RCTResponseSenderBlock)callback
+RCT_EXPORT_METHOD(checkPermissions:(RCTResponseSenderBlock)callback)
{
- RCT_EXPORT();
-
NSMutableDictionary *permissions = [[NSMutableDictionary alloc] init];
UIUserNotificationType types = [[[UIApplication sharedApplication] currentUserNotificationSettings] types];
diff --git a/Libraries/RCTTest/RCTTestModule.m b/Libraries/RCTTest/RCTTestModule.m
index 88bbe0ee6..58b6572f8 100644
--- a/Libraries/RCTTest/RCTTestModule.m
+++ b/Libraries/RCTTest/RCTTestModule.m
@@ -13,12 +13,15 @@
#import "RCTAssert.h"
#import "RCTLog.h"
-@implementation RCTTestModule {
+@implementation RCTTestModule
+{
__weak FBSnapshotTestController *_snapshotController;
__weak UIView *_view;
NSMutableDictionary *_snapshotCounter;
}
+RCT_EXPORT_MODULE()
+
- (instancetype)initWithSnapshotController:(FBSnapshotTestController *)controller view:(UIView *)view
{
if ((self = [super init])) {
@@ -29,10 +32,8 @@
return self;
}
-- (void)verifySnapshot:(RCTResponseSenderBlock)callback
+RCT_EXPORT_METHOD(verifySnapshot:(RCTResponseSenderBlock)callback)
{
- RCT_EXPORT();
-
if (!_snapshotController) {
RCTLogWarn(@"No snapshot controller configured.");
callback(@[]);
@@ -52,10 +53,8 @@
});
}
-- (void)markTestCompleted
+RCT_EXPORT_METHOD(markTestCompleted)
{
- RCT_EXPORT();
-
dispatch_async(dispatch_get_main_queue(), ^{
_done = YES;
});
diff --git a/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m b/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m
index 4d6aba5cc..2f74628c0 100644
--- a/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m
+++ b/Libraries/RCTWebSocketDebugger/RCTWebSocketExecutor.m
@@ -47,7 +47,7 @@ typedef void (^WSMessageCallback)(NSError *error, NSDictionary *reply);
[NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:startDevToolsURL] delegate:nil];
if (![self connectToProxy]) {
- RCTLogError(@"Connection to %@ timed out. Are you running node proxy?", url);
+ RCTLogError(@"Connection to %@ timed out. Are you running node proxy? If you are running on the device check if you have the right IP address on `RCTWebSocketExecutor.m` file.", url);
[self invalidate];
return nil;
}
diff --git a/Libraries/RCTWebSocketDebugger/SRWebSocket.m b/Libraries/RCTWebSocketDebugger/SRWebSocket.m
index 8cf5b4d74..3fd675103 100644
--- a/Libraries/RCTWebSocketDebugger/SRWebSocket.m
+++ b/Libraries/RCTWebSocketDebugger/SRWebSocket.m
@@ -19,9 +19,12 @@
#import
-#if TARGET_OS_IPHONE
-#define HAS_ICU
-#endif
+//NOTE: libicucore ins't actually needed for the socket to function
+//and by commenting this out, we avoid the need to import it into every app.
+
+//#if TARGET_OS_IPHONE
+//#define HAS_ICU
+//#endif
#ifdef HAS_ICU
#import
diff --git a/Libraries/Text/RCTRawTextManager.m b/Libraries/Text/RCTRawTextManager.m
index ab856d049..221b8daeb 100644
--- a/Libraries/Text/RCTRawTextManager.m
+++ b/Libraries/Text/RCTRawTextManager.m
@@ -13,6 +13,8 @@
@implementation RCTRawTextManager
+RCT_EXPORT_MODULE()
+
- (UIView *)view
{
return nil;
diff --git a/Libraries/Text/RCTText.m b/Libraries/Text/RCTText.m
index d6e00f1b6..87c625cd3 100644
--- a/Libraries/Text/RCTText.m
+++ b/Libraries/Text/RCTText.m
@@ -38,34 +38,53 @@
- (void)setAttributedText:(NSAttributedString *)attributedText
{
- [_textStorage setAttributedString:attributedText];
+ for (NSLayoutManager *existingLayoutManager in _textStorage.layoutManagers) {
+ [_textStorage removeLayoutManager:existingLayoutManager];
+ }
+
+ _textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedText];
+
+ if (_layoutManager) {
+ [_textStorage addLayoutManager:_layoutManager];
+ }
+
[self setNeedsDisplay];
}
- (void)setTextContainer:(NSTextContainer *)textContainer
{
- if ([_textContainer isEqual:textContainer]) return;
+ if ([_textContainer isEqual:textContainer]) {
+ return;
+ }
_textContainer = textContainer;
for (NSInteger i = _layoutManager.textContainers.count - 1; i >= 0; i--) {
[_layoutManager removeTextContainerAtIndex:i];
}
- [_layoutManager addTextContainer:_textContainer];
+
+ if (_textContainer) {
+ [_layoutManager addTextContainer:_textContainer];
+ }
[self setNeedsDisplay];
}
- (void)setLayoutManager:(NSLayoutManager *)layoutManager
{
- if ([_layoutManager isEqual:layoutManager]) return;
+ if ([_layoutManager isEqual:layoutManager]) {
+ return;
+ }
_layoutManager = layoutManager;
for (NSLayoutManager *existingLayoutManager in _textStorage.layoutManagers) {
[_textStorage removeLayoutManager:existingLayoutManager];
}
- [_textStorage addLayoutManager:_layoutManager];
+
+ if (_layoutManager) {
+ [_textStorage addLayoutManager:_layoutManager];
+ }
[self setNeedsDisplay];
}
diff --git a/Libraries/Text/RCTTextManager.m b/Libraries/Text/RCTTextManager.m
index d19dfa71e..b8dabcf2c 100644
--- a/Libraries/Text/RCTTextManager.m
+++ b/Libraries/Text/RCTTextManager.m
@@ -20,6 +20,8 @@
@implementation RCTTextManager
+RCT_EXPORT_MODULE()
+
- (UIView *)view
{
return [[RCTText alloc] init];
diff --git a/Libraries/Vibration/RCTVibration.m b/Libraries/Vibration/RCTVibration.m
index 0680860b1..f2544d767 100644
--- a/Libraries/Vibration/RCTVibration.m
+++ b/Libraries/Vibration/RCTVibration.m
@@ -13,9 +13,10 @@
@implementation RCTVibration
-- (void)vibrate
+RCT_EXPORT_MODULE()
+
+RCT_EXPORT_METHOD(vibrate)
{
- RCT_EXPORT();
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
}
diff --git a/React/Base/RCTBridge.h b/React/Base/RCTBridge.h
index 3f0ad735e..f5c21bb3c 100644
--- a/React/Base/RCTBridge.h
+++ b/React/Base/RCTBridge.h
@@ -26,8 +26,6 @@
*/
typedef NSArray *(^RCTBridgeModuleProviderBlock)(void);
-extern NSString *const RCTReloadBridge;
-
/**
* This function returns the module name for a given class.
*/
@@ -38,8 +36,6 @@ extern NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass);
*/
@interface RCTBridge : NSObject
-@property (nonatomic, assign, readonly, getter=isLoaded) BOOL loaded;
-
/**
* The designated initializer. This creates a new bridge on top of the specified
* executor. The bridge should then be used for all subsequent communication
@@ -55,16 +51,31 @@ extern NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass);
/**
* This method is used to call functions in the JavaScript application context.
* It is primarily intended for use by modules that require two-way communication
- * with the JavaScript code.
+ * with the JavaScript code. Method should be regsitered using the
+ * RCT_IMPORT_METHOD macro below. Attempting to call a method that has not been
+ * registered will result in an error.
*/
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args;
+/**
+ * This macro is used to register a JS method to be called via the enqueueJSCall
+ * bridge method. You should place this macro inside any file that uses the
+ * imported method. If a method has already been registered by another class, it
+ * is not necessary to register it again, but it is good practice. Registering
+ * the same method more than once will not result in an error.
+ */
+#define RCT_IMPORT_METHOD(module, method) \
+__attribute__((used, section("__DATA,RCTImport"))) \
+static const char *__rct_import_##module##_##method##__ = #module"."#method;
+
/**
* This method is used to execute a new application script. It is called
* internally whenever a JS application bundle is loaded/reloaded, but should
* probably not be used at any other time.
*/
-- (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete:(RCTJavaScriptCompleteBlock)onComplete;
+- (void)enqueueApplicationScript:(NSString *)script
+ url:(NSURL *)url
+ onComplete:(RCTJavaScriptCompleteBlock)onComplete;
@property (nonatomic, strong) Class executorClass;
@@ -86,14 +97,19 @@ extern NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass);
*/
@property (nonatomic, readonly) dispatch_queue_t shadowQueue;
+/**
+ * The launch options that were used to initialize the bridge.
+ */
@property (nonatomic, copy, readonly) NSDictionary *launchOptions;
+/**
+ * Use this to check if the bridge is currently loading.
+ */
+@property (nonatomic, readonly, getter=isLoaded) BOOL loaded;
/**
- * Method to check that a valid executor exists with which to log
+ * Reload the bundle and reset executor and modules.
*/
-+ (BOOL)hasValidJSExecutor;
-
- (void)reload;
@end
diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m
index 3bf23a5d3..c48afa565 100644
--- a/React/Base/RCTBridge.m
+++ b/React/Base/RCTBridge.m
@@ -26,7 +26,6 @@
#import "RCTRootView.h"
#import "RCTSparseArray.h"
#import "RCTUtils.h"
-#import "RCTWebViewExecutor.h"
/**
* Must be kept in sync with `MessageQueue.js`.
@@ -40,11 +39,32 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) {
RCTBridgeFieldFlushDateMillis
};
-NSString *const RCTReloadBridge = @"RCTReloadBridge";
+#ifdef __LP64__
+typedef uint64_t RCTHeaderValue;
+typedef struct section_64 RCTHeaderSection;
+#define RCTGetSectByNameFromHeader getsectbynamefromheader_64
+#else
+typedef uint32_t RCTHeaderValue;
+typedef struct section RCTHeaderSection;
+#define RCTGetSectByNameFromHeader getsectbynamefromheader
+#endif
+/**
+ * This function returns the module name for a given class.
+ */
NSString *RCTBridgeModuleNameForClass(Class cls)
{
- return [cls respondsToSelector:@selector(moduleName)] ? [cls moduleName] : NSStringFromClass(cls);
+ NSString *name = nil;
+ if ([cls respondsToSelector:@selector(moduleName)]) {
+ name = [cls valueForKey:@"moduleName"];
+ }
+ if ([name length] == 0) {
+ name = NSStringFromClass(cls);
+ }
+ if ([name hasPrefix:@"RK"]) {
+ name = [name stringByReplacingCharactersInRange:(NSRange){0,@"RK".length} withString:@"RCT"];
+ }
+ return name;
}
/**
@@ -58,11 +78,22 @@ static NSArray *RCTJSMethods(void)
dispatch_once(&onceToken, ^{
NSMutableSet *uniqueMethods = [NSMutableSet set];
- RCTEnumerateClasses(^(__unsafe_unretained Class cls) {
- if (RCTClassOverridesClassMethod(cls, @selector(JSMethods))) {
- [uniqueMethods addObjectsFromArray:[cls JSMethods]];
+ Dl_info info;
+ dladdr(&RCTJSMethods, &info);
+
+ const RCTHeaderValue mach_header = (RCTHeaderValue)info.dli_fbase;
+ const RCTHeaderSection *section = RCTGetSectByNameFromHeader((void *)mach_header, "__DATA", "RCTImport");
+
+ if (section) {
+ for (RCTHeaderValue addr = section->offset;
+ addr < section->offset + section->size;
+ addr += sizeof(const char **)) {
+
+ // Get data entry
+ NSString *entry = @(*(const char **)(mach_header + addr));
+ [uniqueMethods addObject:entry];
}
- });
+ }
JSMethods = [uniqueMethods allObjects];
});
@@ -71,35 +102,85 @@ static NSArray *RCTJSMethods(void)
}
/**
- * This function scans all classes available at runtime and returns an array
- * of all classes that implement the RTCBridgeModule protocol.
+ * This function scans all exported modules available at runtime and returns an
+ * array. As a backup, it also scans all classes that implement the
+ * RTCBridgeModule protocol to ensure they've been exported. This scanning
+ * functionality is disabled in release mode to improve startup performance.
*/
static NSArray *RCTModuleNamesByID;
+static NSArray *RCTModuleClassesByID;
static NSArray *RCTBridgeModuleClassesByModuleID(void)
{
- static NSArray *modules;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
- modules = [NSMutableArray array];
+
RCTModuleNamesByID = [NSMutableArray array];
+ RCTModuleClassesByID = [NSMutableArray array];
- RCTEnumerateClasses(^(__unsafe_unretained Class cls) {
- if ([cls conformsToProtocol:@protocol(RCTBridgeModule)]) {
+ Dl_info info;
+ dladdr(&RCTBridgeModuleClassesByModuleID, &info);
- // Add module
- [(NSMutableArray *)modules addObject:cls];
+ const RCTHeaderValue mach_header = (RCTHeaderValue)info.dli_fbase;
+ const RCTHeaderSection *section = RCTGetSectByNameFromHeader((void *)mach_header, "__DATA", "RCTExportModule");
- // Add module name
- NSString *moduleName = RCTBridgeModuleNameForClass(cls);
- [(NSMutableArray *)RCTModuleNamesByID addObject:moduleName];
+ if (section) {
+ for (RCTHeaderValue addr = section->offset;
+ addr < section->offset + section->size;
+ addr += sizeof(const char **)) {
+
+ // Get data entry
+ NSString *entry = @(*(const char **)(mach_header + addr));
+ NSArray *parts = [[entry substringWithRange:(NSRange){2, entry.length - 3}] componentsSeparatedByString:@" "];
+
+ // Parse class name
+ NSString *moduleClassName = parts[0];
+ NSRange categoryRange = [moduleClassName rangeOfString:@"("];
+ if (categoryRange.length) {
+ moduleClassName = [moduleClassName substringToIndex:categoryRange.location];
+ }
+
+ // Get class
+ Class cls = NSClassFromString(moduleClassName);
+ RCTCAssert([cls conformsToProtocol:@protocol(RCTBridgeModule)],
+ @"%@ does not conform to the RCTBridgeModule protocol",
+ NSStringFromClass(cls));
+
+ // Register module
+ [(NSMutableArray *)RCTModuleNamesByID addObject:RCTBridgeModuleNameForClass(cls)];
+ [(NSMutableArray *)RCTModuleClassesByID addObject:cls];
}
- });
+ }
+
+#if DEBUG
+
+ // We may be able to get rid of this check in future, once people
+ // get used to the new registration system. That would potentially
+ // allow you to create modules that are not automatically registered
+
+ static unsigned int classCount;
+ Class *classes = objc_copyClassList(&classCount);
+ for (unsigned int i = 0; i < classCount; i++)
+ {
+ Class cls = classes[i];
+ Class superclass = cls;
+ while (superclass)
+ {
+ if (class_conformsToProtocol(superclass, @protocol(RCTBridgeModule)))
+ {
+ if (![RCTModuleClassesByID containsObject:cls]) {
+ RCTLogError(@"Class %@ was not exported. Did you forget to use RCT_EXPORT_MODULE()?", NSStringFromClass(cls));
+ }
+ break;
+ }
+ superclass = class_getSuperclass(superclass);
+ }
+ }
+
+#endif
- modules = [modules copy];
- RCTModuleNamesByID = [RCTModuleNamesByID copy];
});
- return modules;
+ return RCTModuleClassesByID;
}
@interface RCTBridge ()
@@ -132,30 +213,61 @@ static NSArray *RCTBridgeModuleClassesByModuleID(void)
static Class _globalExecutorClass;
+NS_INLINE NSString *RCTStringUpToFirstArgument(NSString *methodName) {
+ NSRange colonRange = [methodName rangeOfString:@":"];
+ if (colonRange.length) {
+ methodName = [methodName substringToIndex:colonRange.location];
+ }
+ return methodName;
+}
+
- (instancetype)initWithMethodName:(NSString *)methodName
JSMethodName:(NSString *)JSMethodName
{
if ((self = [super init])) {
-
_methodName = methodName;
- NSArray *parts = [[methodName substringWithRange:NSMakeRange(2, methodName.length - 3)] componentsSeparatedByString:@" "];
+ NSArray *parts = [[methodName substringWithRange:(NSRange){2, methodName.length - 3}] componentsSeparatedByString:@" "];
// Parse class and method
_moduleClassName = parts[0];
NSRange categoryRange = [_moduleClassName rangeOfString:@"("];
- if (categoryRange.length)
- {
+ if (categoryRange.length) {
_moduleClassName = [_moduleClassName substringToIndex:categoryRange.location];
}
+ NSArray *argumentNames = nil;
+ if ([parts[1] hasPrefix:@"__rct_export__"]) {
+ // New format
+ NSString *selectorString = [parts[1] substringFromIndex:14];
+ _selector = NSSelectorFromString(selectorString);
+ _JSMethodName = RCTStringUpToFirstArgument(selectorString);
+
+ static NSRegularExpression *regExp;
+ if (!regExp) {
+ NSString *unusedPattern = @"(?:(?:__unused|__attribute__\\(\\(unused\\)\\)))";
+ NSString *constPattern = @"(?:const)";
+ NSString *constUnusedPattern = [NSString stringWithFormat:@"(?:(?:%@|%@)\\s*)", unusedPattern, constPattern];
+ NSString *pattern = [NSString stringWithFormat:@"\\(%1$@?(\\w+?)(?:\\s*\\*)?%1$@?\\)", constUnusedPattern];
+ regExp = [[NSRegularExpression alloc] initWithPattern:pattern options:0 error:NULL];
+ }
+
+ argumentNames = [NSMutableArray array];
+ [regExp enumerateMatchesInString:JSMethodName options:0 range:NSMakeRange(0, JSMethodName.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
+ NSString *argumentName = [JSMethodName substringWithRange:[result rangeAtIndex:1]];
+ [(NSMutableArray *)argumentNames addObject:argumentName];
+ }];
+ } else {
+ // Old format
+ NSString *selectorString = parts[1];
+ _selector = NSSelectorFromString(selectorString);
+ _JSMethodName = JSMethodName ?: RCTStringUpToFirstArgument(selectorString);
+ }
+
// Extract class and method details
_isClassMethod = [methodName characterAtIndex:0] == '+';
_moduleClass = NSClassFromString(_moduleClassName);
- _selector = NSSelectorFromString(parts[1]);
- _JSMethodName = JSMethodName ?: [NSStringFromSelector(_selector) componentsSeparatedByString:@":"][0];
#if DEBUG
-
// Sanity check
RCTAssert([_moduleClass conformsToProtocol:@protocol(RCTBridgeModule)],
@"You are attempting to export the method %@, but %@ does not \
@@ -170,15 +282,87 @@ static Class _globalExecutorClass;
// Process arguments
NSUInteger numberOfArguments = _methodSignature.numberOfArguments;
NSMutableArray *argumentBlocks = [[NSMutableArray alloc] initWithCapacity:numberOfArguments - 2];
- for (NSUInteger i = 2; i < numberOfArguments; i++) {
- const char *argumentType = [_methodSignature getArgumentTypeAtIndex:i];
- switch (argumentType[0]) {
#define RCT_ARG_BLOCK(_logic) \
- [argumentBlocks addObject:^(RCTBridge *bridge, NSInvocation *invocation, NSUInteger index, id json) { \
- _logic \
- [invocation setArgument:&value atIndex:index]; \
- }]; \
+ [argumentBlocks addObject:^(RCTBridge *bridge, NSInvocation *invocation, NSUInteger index, id json) { \
+ _logic \
+ [invocation setArgument:&value atIndex:index]; \
+ }]; \
+
+ void (^addBlockArgument)(void) = ^{
+ RCT_ARG_BLOCK(
+ if (json && ![json isKindOfClass:[NSNumber class]]) {
+ RCTLogError(@"Argument %tu (%@) of %@.%@ should be a number", index,
+ json, RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName);
+ return;
+ }
+
+ // Marked as autoreleasing, because NSInvocation doesn't retain arguments
+ __autoreleasing id value = (json ? ^(NSArray *args) {
+ [bridge _invokeAndProcessModule:@"BatchedBridge"
+ method:@"invokeCallbackAndReturnFlushedQueue"
+ arguments:@[json, args]];
+ } : ^(NSArray *unused) {});
+ )
+ };
+
+ void (^defaultCase)(const char *) = ^(const char *argumentType) {
+ static const char *blockType = @encode(typeof(^{}));
+ if (!strcmp(argumentType, blockType)) {
+ addBlockArgument();
+ } else {
+ RCT_ARG_BLOCK( id value = json; )
+ }
+ };
+
+ for (NSUInteger i = 2; i < numberOfArguments; i++) {
+ const char *argumentType = [_methodSignature getArgumentTypeAtIndex:i];
+
+ BOOL useFallback = YES;
+ if (argumentNames) {
+ NSString *argumentName = argumentNames[i - 2];
+ SEL selector = NSSelectorFromString([argumentName stringByAppendingString:@":"]);
+ if ([RCTConvert respondsToSelector:selector]) {
+ useFallback = NO;
+ switch (argumentType[0]) {
+
+#define RCT_CONVERT_CASE(_value, _type) \
+ case _value: { \
+ _type (*convert)(id, SEL, id) = (typeof(convert))[RCTConvert methodForSelector:selector]; \
+ RCT_ARG_BLOCK( _type value = convert([RCTConvert class], selector, json); ) \
+ break; \
+ }
+
+ RCT_CONVERT_CASE(':', SEL)
+ RCT_CONVERT_CASE('*', const char *)
+ RCT_CONVERT_CASE('c', char)
+ RCT_CONVERT_CASE('C', unsigned char)
+ RCT_CONVERT_CASE('s', short)
+ RCT_CONVERT_CASE('S', unsigned short)
+ RCT_CONVERT_CASE('i', int)
+ RCT_CONVERT_CASE('I', unsigned int)
+ RCT_CONVERT_CASE('l', long)
+ RCT_CONVERT_CASE('L', unsigned long)
+ RCT_CONVERT_CASE('q', long long)
+ RCT_CONVERT_CASE('Q', unsigned long long)
+ RCT_CONVERT_CASE('f', float)
+ RCT_CONVERT_CASE('d', double)
+ RCT_CONVERT_CASE('B', BOOL)
+ RCT_CONVERT_CASE('@', id)
+ RCT_CONVERT_CASE('^', void *)
+
+ default:
+ defaultCase(argumentType);
+ break;
+ }
+ } else if ([argumentName isEqualToString:@"RCTResponseSenderBlock"]) {
+ addBlockArgument();
+ useFallback = NO;
+ }
+ }
+
+ if (useFallback) {
+ switch (argumentType[0]) {
#define RCT_CASE(_value, _class, _logic) \
case _value: { \
@@ -193,61 +377,46 @@ static Class _globalExecutorClass;
break; \
}
- RCT_CASE(':', NSString, SEL value = NSSelectorFromString(json); );
- RCT_CASE('*', NSString, const char *value = [json UTF8String]; );
+ RCT_CASE(':', NSString, SEL value = NSSelectorFromString(json); )
+ RCT_CASE('*', NSString, const char *value = [json UTF8String]; )
#define RCT_SIMPLE_CASE(_value, _type, _selector) \
- case _value: { \
- RCT_ARG_BLOCK( \
- if (json && ![json respondsToSelector:@selector(_selector)]) { \
- RCTLogError(@"Argument %tu (%@) of %@.%@ does not respond to selector: %@", \
- index, json, RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName, @#_selector); \
- return; \
- } \
- _type value = [json _selector]; \
- ) \
- break; \
- }
-
- RCT_SIMPLE_CASE('c', char, charValue)
- RCT_SIMPLE_CASE('C', unsigned char, unsignedCharValue)
- RCT_SIMPLE_CASE('s', short, shortValue)
- RCT_SIMPLE_CASE('S', unsigned short, unsignedShortValue)
- RCT_SIMPLE_CASE('i', int, intValue)
- RCT_SIMPLE_CASE('I', unsigned int, unsignedIntValue)
- RCT_SIMPLE_CASE('l', long, longValue)
- RCT_SIMPLE_CASE('L', unsigned long, unsignedLongValue)
- RCT_SIMPLE_CASE('q', long long, longLongValue)
- RCT_SIMPLE_CASE('Q', unsigned long long, unsignedLongLongValue)
- RCT_SIMPLE_CASE('f', float, floatValue)
- RCT_SIMPLE_CASE('d', double, doubleValue)
- RCT_SIMPLE_CASE('B', BOOL, boolValue)
-
- default: {
- static const char *blockType = @encode(typeof(^{}));
- if (!strcmp(argumentType, blockType)) {
- RCT_ARG_BLOCK(
- if (json && ![json isKindOfClass:[NSNumber class]]) {
- RCTLogError(@"Argument %tu (%@) of %@.%@ should be a number", index,
- json, RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName);
- return;
- }
- // Marked as autoreleasing, because NSInvocation doesn't retain arguments
- __autoreleasing id value = (json ? ^(NSArray *args) {
- [bridge _invokeAndProcessModule:@"BatchedBridge"
- method:@"invokeCallbackAndReturnFlushedQueue"
- arguments:@[json, args]];
- } : ^(NSArray *unused) {});
- )
- } else {
- RCT_ARG_BLOCK( id value = json; )
+ case _value: { \
+ RCT_ARG_BLOCK( \
+ if (json && ![json respondsToSelector:@selector(_selector)]) { \
+ RCTLogError(@"Argument %tu (%@) of %@.%@ does not respond to selector: %@", \
+ index, json, RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName, @#_selector); \
+ return; \
+ } \
+ _type value = [json _selector]; \
+ ) \
+ break; \
}
- break;
+
+ RCT_SIMPLE_CASE('c', char, charValue)
+ RCT_SIMPLE_CASE('C', unsigned char, unsignedCharValue)
+ RCT_SIMPLE_CASE('s', short, shortValue)
+ RCT_SIMPLE_CASE('S', unsigned short, unsignedShortValue)
+ RCT_SIMPLE_CASE('i', int, intValue)
+ RCT_SIMPLE_CASE('I', unsigned int, unsignedIntValue)
+ RCT_SIMPLE_CASE('l', long, longValue)
+ RCT_SIMPLE_CASE('L', unsigned long, unsignedLongValue)
+ RCT_SIMPLE_CASE('q', long long, longLongValue)
+ RCT_SIMPLE_CASE('Q', unsigned long long, unsignedLongLongValue)
+ RCT_SIMPLE_CASE('f', float, floatValue)
+ RCT_SIMPLE_CASE('d', double, doubleValue)
+ RCT_SIMPLE_CASE('B', BOOL, boolValue)
+
+ default:
+ defaultCase(argumentType);
+ break;
}
}
}
+
_argumentBlocks = [argumentBlocks copy];
}
+
return self;
}
@@ -257,7 +426,6 @@ static Class _globalExecutorClass;
{
#if DEBUG
-
// Sanity check
RCTAssert([module class] == _moduleClass, @"Attempted to invoke method \
%@ on a module of class %@", _methodName, [module class]);
@@ -274,6 +442,7 @@ static Class _globalExecutorClass;
// Create invocation (we can't re-use this as it wouldn't be thread-safe)
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:_methodSignature];
[invocation setArgument:&_selector atIndex:1];
+ [invocation retainArguments];
// Set arguments
NSUInteger index = 0;
@@ -281,7 +450,7 @@ static Class _globalExecutorClass;
id arg = (json == [NSNull null]) ? nil : json;
void (^block)(RCTBridge *, NSInvocation *, NSUInteger, id) = _argumentBlocks[index];
block(bridge, invocation, index + 2, arg);
- index ++;
+ index++;
}
// Invoke method
@@ -309,18 +478,8 @@ static RCTSparseArray *RCTExportedMethodsByModuleID(void)
Dl_info info;
dladdr(&RCTExportedMethodsByModuleID, &info);
-#ifdef __LP64__
- typedef uint64_t RCTExportValue;
- typedef struct section_64 RCTExportSection;
-#define RCTGetSectByNameFromHeader getsectbynamefromheader_64
-#else
- typedef uint32_t RCTExportValue;
- typedef struct section RCTExportSection;
-#define RCTGetSectByNameFromHeader getsectbynamefromheader
-#endif
-
- const RCTExportValue mach_header = (RCTExportValue)info.dli_fbase;
- const RCTExportSection *section = RCTGetSectByNameFromHeader((void *)mach_header, "__DATA", "RCTExport");
+ const RCTHeaderValue mach_header = (RCTHeaderValue)info.dli_fbase;
+ const RCTHeaderSection *section = RCTGetSectByNameFromHeader((void *)mach_header, "__DATA", "RCTExport");
if (section == NULL) {
return;
@@ -329,7 +488,7 @@ static RCTSparseArray *RCTExportedMethodsByModuleID(void)
NSArray *classes = RCTBridgeModuleClassesByModuleID();
NSMutableDictionary *methodsByModuleClassName = [NSMutableDictionary dictionaryWithCapacity:[classes count]];
- for (RCTExportValue addr = section->offset;
+ for (RCTHeaderValue addr = section->offset;
addr < section->offset + section->size;
addr += sizeof(const char **) * 2) {
@@ -530,11 +689,7 @@ static id _latestJSExecutor;
- (void)setUp
{
Class executorClass = _executorClass ?: _globalExecutorClass ?: [RCTContextExecutor class];
- if ([NSStringFromClass(executorClass) isEqualToString:@"RCTWebViewExecutor"]) {
- _javaScriptExecutor = [[RCTWebViewExecutor alloc] initWithWebView:[[UIWebView alloc] init]];
- } else {
- _javaScriptExecutor = [[executorClass alloc] init];
- }
+ _javaScriptExecutor = [[executorClass alloc] init];
_latestJSExecutor = _javaScriptExecutor;
_eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
_shadowQueue = dispatch_queue_create("com.facebook.ReactKit.ShadowQueue", DISPATCH_QUEUE_SERIAL);
@@ -598,32 +753,34 @@ static id _latestJSExecutor;
_loaded = YES;
} else if (_bundlePath != nil) { // Allow testing without a script
RCTJavaScriptLoader *loader = [[RCTJavaScriptLoader alloc] initWithBridge:self];
- [loader loadBundleAtURL:[NSURL URLWithString:_bundlePath]
- onComplete:^(NSError *error) {
- _loaded = YES;
- if (error != nil) {
- NSArray *stack = [[error userInfo] objectForKey:@"stack"];
- if (stack) {
- [[RCTRedBox sharedInstance] showErrorMessage:[error localizedDescription] withStack:stack];
- } else {
- [[RCTRedBox sharedInstance] showErrorMessage:[error localizedDescription] withDetails:[error localizedFailureReason]];
- }
- } else {
- [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
- object:self];
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(reload)
- name:RCTReloadNotification
- object:nil];
- ;
- }
- }];
+ [loader loadBundleAtURL:[NSURL URLWithString:_bundlePath] onComplete:^(NSError *error) {
+ _loaded = YES;
+ if (error != nil) {
+ NSArray *stack = [[error userInfo] objectForKey:@"stack"];
+ if (stack) {
+ [[RCTRedBox sharedInstance] showErrorMessage:[error localizedDescription]
+ withStack:stack];
+ } else {
+ [[RCTRedBox sharedInstance] showErrorMessage:[error localizedDescription]
+ withDetails:[error localizedFailureReason]];
+ }
+ } else {
+ [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
+ object:self];
+ }
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(reload)
+ name:RCTReloadNotification
+ object:nil];
+ }];
}
}
- (void)bindKeys
{
#if TARGET_IPHONE_SIMULATOR
+ __weak RCTBridge *weakSelf = self;
+
// Workaround around the first cmd+r not working: http://openradar.appspot.com/19613391
// You can register just the cmd key and do nothing. This will trigger the bug and cmd+r
// will work like a charm!
@@ -632,31 +789,38 @@ static id _latestJSExecutor;
action:^(UIKeyCommand *command) {
// Do nothing
}];
-
[[RCTKeyCommands sharedInstance] registerKeyCommandWithInput:@"r"
modifierFlags:UIKeyModifierCommand
action:^(UIKeyCommand *command) {
- [self reload];
+ [weakSelf reload];
}];
[[RCTKeyCommands sharedInstance] registerKeyCommandWithInput:@"n"
modifierFlags:UIKeyModifierCommand
action:^(UIKeyCommand *command) {
- _executorClass = Nil;
- [self reload];
+ RCTBridge *strongSelf = weakSelf;
+ if (!strongSelf) {
+ return;
+ }
+ strongSelf->_executorClass = Nil;
+ [strongSelf reload];
}];
-
[[RCTKeyCommands sharedInstance] registerKeyCommandWithInput:@"d"
modifierFlags:UIKeyModifierCommand
action:^(UIKeyCommand *command) {
- _executorClass = NSClassFromString(@"RCTWebSocketExecutor");
- if (!_executorClass) {
+ RCTBridge *strongSelf = weakSelf;
+ if (!strongSelf) {
+ return;
+ }
+ strongSelf->_executorClass = NSClassFromString(@"RCTWebSocketExecutor");
+ if (!strongSelf->_executorClass) {
RCTLogError(@"WebSocket debugger is not available. Did you forget to include RCTWebSocketExecutor?");
}
- [self reload];
+ [strongSelf reload];
}];
#endif
}
+
- (NSDictionary *)modules
{
RCTAssert(_modulesByName != nil, @"Bridge modules have not yet been initialized. \
@@ -693,6 +857,11 @@ static id _latestJSExecutor;
[_javaScriptExecutor invalidate];
_javaScriptExecutor = nil;
+ // Wait for queued methods to finish
+ dispatch_sync(self.shadowQueue, ^{
+ // Make sure all dispatchers have been executed before continuing
+ });
+
// Invalidate modules
for (id target in _modulesByID.allObjects) {
if ([target respondsToSelector:@selector(invalidate)]) {
@@ -888,15 +1057,9 @@ static id _latestJSExecutor;
}
}
-+ (BOOL)hasValidJSExecutor
-{
- return (_latestJSExecutor != nil && [_latestJSExecutor isValid]);
-}
-
+ (void)logMessage:(NSString *)message level:(NSString *)level
{
if (!_latestJSExecutor || ![_latestJSExecutor isValid]) {
- RCTLogError(@"ERROR: No valid JS executor to log '%@'.", message);
return;
}
diff --git a/React/Base/RCTBridgeModule.h b/React/Base/RCTBridgeModule.h
index 2134dc2e5..7fc526e35 100644
--- a/React/Base/RCTBridgeModule.h
+++ b/React/Base/RCTBridgeModule.h
@@ -9,8 +9,6 @@
#import
-#import "RCTJSMethodRegistrar.h"
-
@class RCTBridge;
/**
@@ -22,7 +20,7 @@ typedef void (^RCTResponseSenderBlock)(NSArray *response);
/**
* Provides the interface needed to register a bridge module.
*/
-@protocol RCTBridgeModule
+@protocol RCTBridgeModule
@optional
/**
@@ -34,10 +32,14 @@ typedef void (^RCTResponseSenderBlock)(NSArray *response);
@property (nonatomic, strong) RCTBridge *bridge;
/**
- * The module name exposed to JS. If omitted, this will be inferred
- * automatically by using the native module's class name.
+ * Place this macro in your class implementation, to automatically register
+ * your module with the bridge when it loads. The optional js_name argument
+ * will be used as the JS module name. If omitted, the JS module name will
+ * match the Objective-C class name.
*/
-+ (NSString *)moduleName;
+#define RCT_EXPORT_MODULE(js_name) \
++ (NSString *)moduleName { __attribute__((used, section("__DATA,RCTExportModule" \
+))) static const char *__rct_export_entry__ = { __func__ }; return @#js_name; } \
/**
* Place this macro inside the method body of any method you want to expose
@@ -46,8 +48,37 @@ typedef void (^RCTResponseSenderBlock)(NSArray *response);
* If omitted, the JS method name will match the first part of the Objective-C
* method selector name (up to the first colon).
*/
-#define RCT_EXPORT(js_name) __attribute__((used, section("__DATA,RCTExport" \
-))) static const char *__rct_export_entry__[] = { __func__, #js_name }
+#define RCT_EXPORT(js_name) \
+ _Pragma("message(\"RCT_EXPORT is deprecated. Use RCT_EXPORT_METHOD instead.\")") \
+ __attribute__((used, section("__DATA,RCTExport"))) \
+ static const char *__rct_export_entry__[] = { __func__, #js_name }
+
+/**
+ * Wrap the parameter line of your method implementation with this macro to
+ * expose it to JS. Unlike the deprecated RCT_EXPORT, this macro does not take
+ * a js_name argument and the exposed method will match the first part of the
+ * Objective-C method selector name (up to the first colon).
+ *
+ * For example, in MyClass.m:
+ *
+ * - (void)doSomething:(NSString *)aString withA:(NSInteger)a andB:(NSInteger)b
+ * {}
+ *
+ * becomes
+ *
+ * RCT_EXPORT_METHOD(doSomething:(NSString *)aString
+ * withA:(NSInteger)a
+ * andB:(NSInteger)b)
+ * {}
+ *
+ * and is exposed to JavaScript as `NativeModules.ModuleName.doSomething`.
+ */
+#define RCT_EXPORT_METHOD(method) \
+ - (void)__rct_export__##method { \
+ __attribute__((used, section("__DATA,RCTExport"))) \
+ static const char *__rct_export_entry__[] = { __func__, #method }; \
+ } \
+ - (void)method
/**
* Injects constants into JS. These constants are made accessible via
@@ -65,3 +96,11 @@ typedef void (^RCTResponseSenderBlock)(NSArray *response);
- (void)batchDidComplete;
@end
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void RCTBridgeModuleRegisterClass(Class cls, NSString *moduleName);
+#ifdef __cplusplus
+}
+#endif
diff --git a/React/Base/RCTConvert.h b/React/Base/RCTConvert.h
index 7e573370e..ff5fb970b 100644
--- a/React/Base/RCTConvert.h
+++ b/React/Base/RCTConvert.h
@@ -82,14 +82,26 @@
+ (UIFont *)UIFont:(UIFont *)font withFamily:(id)family
size:(id)size weight:(id)weight style:(id)style;
-+ (NSArray *)NSStringArray:(id)json;
-+ (NSArray *)NSDictionaryArray:(id)json;
-+ (NSArray *)NSURLArray:(id)json;
-+ (NSArray *)NSNumberArray:(id)json;
-+ (NSArray *)UIColorArray:(id)json;
-+ (NSArray *)CGColorArray:(id)json;
+typedef NSArray NSStringArray;
++ (NSStringArray *)NSStringArray:(id)json;
-+ (BOOL)css_overflow:(id)json;
+typedef NSArray NSDictionaryArray;
++ (NSDictionaryArray *)NSDictionaryArray:(id)json;
+
+typedef NSArray NSURLArray;
++ (NSURLArray *)NSURLArray:(id)json;
+
+typedef NSArray NSNumberArray;
++ (NSNumberArray *)NSNumberArray:(id)json;
+
+typedef NSArray UIColorArray;
++ (UIColorArray *)UIColorArray:(id)json;
+
+typedef NSArray CGColorArray;
++ (CGColorArray *)CGColorArray:(id)json;
+
+typedef BOOL css_overflow;
++ (css_overflow)css_overflow:(id)json;
+ (css_flex_direction_t)css_flex_direction_t:(id)json;
+ (css_justify_t)css_justify_t:(id)json;
+ (css_align_t)css_align_t:(id)json;
@@ -195,7 +207,7 @@ RCT_CUSTOM_CONVERTER(type, type, [[self NSNumber:json] getter])
* This macro is used for creating converter functions for typed arrays.
*/
#define RCT_ARRAY_CONVERTER(type) \
-+ (NSArray *)type##Array:(id)json \
++ (type##Array *)type##Array:(id)json \
{ \
NSMutableArray *values = [[NSMutableArray alloc] init]; \
for (id jsonValue in [self NSArray:json]) { \
diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m
index 1ddc9884c..a46379ae8 100644
--- a/React/Base/RCTConvert.m
+++ b/React/Base/RCTConvert.m
@@ -670,11 +670,6 @@ static BOOL RCTFontIsCondensed(UIFont *font)
isCondensed = RCTFontIsCondensed(font);
}
- // Get font weight
- if (weight) {
- fontWeight = [self RCTFontWeight:weight];
- }
-
// Get font style
if (style) {
isItalic = [self RCTFontStyle:style];
@@ -700,6 +695,11 @@ static BOOL RCTFontIsCondensed(UIFont *font)
}
}
+ // Get font weight
+ if (weight) {
+ fontWeight = [self RCTFontWeight:weight];
+ }
+
// Get closest match
UIFont *bestMatch = font;
CGFloat closestWeight = font ? RCTWeightOfFont(font) : INFINITY;
@@ -742,8 +742,6 @@ RCT_ARRAY_CONVERTER(UIColor)
return colors;
}
-typedef BOOL css_overflow;
-
RCT_ENUM_CONVERTER(css_overflow, (@{
@"hidden": @NO,
@"visible": @YES
diff --git a/React/Base/RCTDevMenu.m b/React/Base/RCTDevMenu.m
index 5fe58f608..fdb79b8fb 100644
--- a/React/Base/RCTDevMenu.m
+++ b/React/Base/RCTDevMenu.m
@@ -12,6 +12,7 @@
#import "RCTRedBox.h"
#import "RCTRootView.h"
#import "RCTSourceCode.h"
+#import "RCTWebViewExecutor.h"
@interface RCTDevMenu () {
BOOL _liveReload;
@@ -33,13 +34,14 @@
- (void)show
{
- NSString *debugTitle = self.bridge.executorClass == Nil ? @"Enable Debugging" : @"Disable Debugging";
+ NSString *debugTitleChrome = self.bridge.executorClass != Nil && self.bridge.executorClass == NSClassFromString(@"RCTWebSocketExecutor") ? @"Disable Chrome Debugging" : @"Enable Chrome Debugging";
+ NSString *debugTitleSafari = self.bridge.executorClass == [RCTWebViewExecutor class] ? @"Disable Safari Debugging" : @"Enable Safari Debugging";
NSString *liveReloadTitle = _liveReload ? @"Disable Live Reload" : @"Enable Live Reload";
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"React Native: Development"
delegate:self
cancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil
- otherButtonTitles:@"Reload", debugTitle, liveReloadTitle, nil];
+ otherButtonTitles:@"Reload", debugTitleChrome, debugTitleSafari, liveReloadTitle, nil];
actionSheet.actionSheetStyle = UIBarStyleBlack;
[actionSheet showInView:[[[[UIApplication sharedApplication] keyWindow] rootViewController] view]];
}
@@ -49,9 +51,14 @@
if (buttonIndex == 0) {
[self.bridge reload];
} else if (buttonIndex == 1) {
- self.bridge.executorClass = self.bridge.executorClass == Nil ? NSClassFromString(@"RCTWebSocketExecutor") : nil;
+ Class cls = NSClassFromString(@"RCTWebSocketExecutor");
+ self.bridge.executorClass = (self.bridge.executorClass != cls) ? cls : nil;
[self.bridge reload];
} else if (buttonIndex == 2) {
+ Class cls = [RCTWebViewExecutor class];
+ self.bridge.executorClass = (self.bridge.executorClass != cls) ? cls : Nil;
+ [self.bridge reload];
+ } else if (buttonIndex == 3) {
_liveReload = !_liveReload;
[self _pollAndReload];
}
@@ -60,7 +67,7 @@
- (void)_pollAndReload
{
if (_liveReload) {
- RCTSourceCode *sourceCodeModule = self.bridge.modules[NSStringFromClass([RCTSourceCode class])];
+ RCTSourceCode *sourceCodeModule = self.bridge.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
NSURL *url = sourceCodeModule.scriptURL;
NSURL *longPollURL = [[NSURL alloc] initWithString:@"/onchange" relativeToURL:url];
[self performSelectorInBackground:@selector(_checkForUpdates:) withObject:longPollURL];
diff --git a/React/Base/RCTEventDispatcher.m b/React/Base/RCTEventDispatcher.m
index a83ce58cc..c82eca01c 100644
--- a/React/Base/RCTEventDispatcher.m
+++ b/React/Base/RCTEventDispatcher.m
@@ -25,14 +25,9 @@
return self;
}
-+ (NSArray *)JSMethods
-{
- return @[
- @"RCTNativeAppEventEmitter.emit",
- @"RCTDeviceEventEmitter.emit",
- @"RCTEventEmitter.receiveEvent",
- ];
-}
+RCT_IMPORT_METHOD(RCTNativeAppEventEmitter, emit);
+RCT_IMPORT_METHOD(RCTDeviceEventEmitter, emit);
+RCT_IMPORT_METHOD(RCTEventEmitter, receiveEvent);
- (void)sendAppEventWithName:(NSString *)name body:(id)body
{
diff --git a/React/Base/RCTJavaScriptLoader.m b/React/Base/RCTJavaScriptLoader.m
index 1d61946b9..518916c92 100755
--- a/React/Base/RCTJavaScriptLoader.m
+++ b/React/Base/RCTJavaScriptLoader.m
@@ -104,15 +104,20 @@
if ([response isKindOfClass:[NSHTTPURLResponse class]] && [(NSHTTPURLResponse *)response statusCode] != 200) {
NSDictionary *userInfo;
NSDictionary *errorDetails = RCTJSONParse(rawText, nil);
- if ([errorDetails isKindOfClass:[NSDictionary class]]) {
+ if ([errorDetails isKindOfClass:[NSDictionary class]] &&
+ [errorDetails[@"errors"] isKindOfClass:[NSArray class]]) {
+ NSMutableArray *fakeStack = [[NSMutableArray alloc] init];
+ for (NSDictionary *err in errorDetails[@"errors"]) {
+ [fakeStack addObject: @{
+ @"methodName": err[@"description"] ?: @"",
+ @"file": err[@"filename"] ?: @"",
+ @"lineNumber": err[@"lineNumber"] ?: @0
+ }];
+ }
userInfo = @{
- NSLocalizedDescriptionKey: errorDetails[@"message"] ?: @"No message provided",
- @"stack": @[@{
- @"methodName": errorDetails[@"description"] ?: @"",
- @"file": errorDetails[@"filename"] ?: @"",
- @"lineNumber": errorDetails[@"lineNumber"] ?: @0
- }]
- };
+ NSLocalizedDescriptionKey: errorDetails[@"message"] ?: @"No message provided",
+ @"stack": fakeStack,
+ };
} else {
userInfo = @{NSLocalizedDescriptionKey: rawText};
}
@@ -123,7 +128,7 @@
onComplete(error);
return;
}
- RCTSourceCode *sourceCodeModule = _bridge.modules[NSStringFromClass([RCTSourceCode class])];
+ RCTSourceCode *sourceCodeModule = _bridge.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
sourceCodeModule.scriptURL = scriptURL;
sourceCodeModule.scriptText = rawText;
diff --git a/React/Base/RCTLog.h b/React/Base/RCTLog.h
index c30da141b..bc19448ac 100644
--- a/React/Base/RCTLog.h
+++ b/React/Base/RCTLog.h
@@ -42,8 +42,7 @@ typedef NS_ENUM(NSInteger, RCTLogLevel) {
/**
* A block signature to be used for custom logging functions. In most cases you
* will want to pass these arguments to the RCTFormatLog function in order to
- * generate a string, or use the RCTSimpleLogFunction() constructor to register
- * a simple function that does not use all of the arguments.
+ * generate a string.
*/
typedef void (^RCTLogFunction)(
RCTLogLevel level,
@@ -65,13 +64,6 @@ NSString *RCTFormatLog(
NSString *message
);
-/**
- * A method to generate a log function from a block with a much simpler
- * template. The message passed to the simpler block is equivalent to the
- * output of the RCTFormatLog() function.
- */
-RCTLogFunction RCTSimpleLogFunction(void (^logFunction)(RCTLogLevel level, NSString *message));
-
/**
* The default logging function used by RCTLogXX.
*/
diff --git a/React/Base/RCTLog.m b/React/Base/RCTLog.m
index d2de897f1..d5e495d8c 100644
--- a/React/Base/RCTLog.m
+++ b/React/Base/RCTLog.m
@@ -116,12 +116,11 @@ NSString *RCTFormatLog(
});
[log appendString:[formatter stringFromDate:timestamp]];
}
- [log appendString:@"[react]"];
if (level) {
[log appendFormat:@"[%s]", RCTLogLevels[level - 1]];
}
if (thread) {
- NSString *threadName = thread.name;
+ NSString *threadName = [thread isMainThread] ? @"main" : thread.name;
if (threadName.length == 0) {
#if DEBUG
#pragma clang diagnostic push
@@ -149,19 +148,6 @@ NSString *RCTFormatLog(
return log;
}
-RCTLogFunction RCTSimpleLogFunction(void (^logFunction)(RCTLogLevel level, NSString *message))
-{
- return ^(RCTLogLevel level,
- NSString *fileName,
- NSNumber *lineNumber,
- NSString *message) {
-
- logFunction(level, RCTFormatLog(
- [NSDate date], [NSThread currentThread], level, fileName, lineNumber, message
- ));
- };
-}
-
void _RCTLogFormat(RCTLogLevel level, const char *fileName, int lineNumber, NSString *format, ...)
{
if (RCTCurrentLogFunction && level >= RCTCurrentLogThreshold) {
@@ -193,9 +179,7 @@ void _RCTLogFormat(RCTLogLevel level, const char *fileName, int lineNumber, NSSt
}
// Log to JS executor
- if ([RCTBridge hasValidJSExecutor]) {
- [RCTBridge logMessage:message level:level ? @(RCTLogLevels[level - 1]) : @"info"];
- }
+ [RCTBridge logMessage:message level:level ? @(RCTLogLevels[level - 1]) : @"info"];
#endif
diff --git a/React/Base/RCTRedBox.m b/React/Base/RCTRedBox.m
index de3c7dda8..f5e8fbbb4 100644
--- a/React/Base/RCTRedBox.m
+++ b/React/Base/RCTRedBox.m
@@ -172,6 +172,7 @@
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"];
cell.textLabel.textColor = [[UIColor whiteColor] colorWithAlphaComponent:0.9];
cell.textLabel.font = [UIFont fontWithName:@"Menlo-Regular" size:14];
+ cell.textLabel.numberOfLines = 2;
cell.detailTextLabel.textColor = [[UIColor whiteColor] colorWithAlphaComponent:0.7];
cell.detailTextLabel.font = [UIFont fontWithName:@"Menlo-Regular" size:11];
cell.detailTextLabel.lineBreakMode = NSLineBreakByTruncatingMiddle;
@@ -196,7 +197,7 @@
CGRect boundingRect = [_lastErrorMessage boundingRectWithSize:CGSizeMake(tableView.frame.size.width - 30, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];
return ceil(boundingRect.size.height) + 40;
} else {
- return 44;
+ return 50;
}
}
diff --git a/React/Base/RCTRootView.m b/React/Base/RCTRootView.m
index 6c15e509c..02d7ef447 100644
--- a/React/Base/RCTRootView.m
+++ b/React/Base/RCTRootView.m
@@ -162,13 +162,8 @@ NSString *const RCTReloadViewsNotification = @"RCTReloadViewsNotification";
}
}
-+ (NSArray *)JSMethods
-{
- return @[
- @"AppRegistry.runApplication",
- @"ReactIOS.unmountComponentAtNodeAndRemoveContainer"
- ];
-}
+RCT_IMPORT_METHOD(AppRegistry, runApplication)
+RCT_IMPORT_METHOD(ReactIOS, unmountComponentAtNodeAndRemoveContainer)
- (void)bundleFinishedLoading
{
@@ -176,9 +171,9 @@ NSString *const RCTReloadViewsNotification = @"RCTReloadViewsNotification";
_registered = YES;
NSString *moduleName = _moduleName ?: @"";
NSDictionary *appParameters = @{
- @"rootTag": _contentView.reactTag,
- @"initialProps": self.initialProperties ?: @{},
- };
+ @"rootTag": _contentView.reactTag,
+ @"initialProps": self.initialProperties ?: @{},
+ };
[_bridge.uiManager registerRootView:_contentView];
[_bridge enqueueJSCall:@"AppRegistry.runApplication"
args:@[moduleName, appParameters]];
diff --git a/React/Base/RCTTouchHandler.m b/React/Base/RCTTouchHandler.m
index b5d3b1e72..d409ec3e5 100644
--- a/React/Base/RCTTouchHandler.m
+++ b/React/Base/RCTTouchHandler.m
@@ -200,10 +200,7 @@ typedef NS_ENUM(NSInteger, RCTTouchEventType) {
reactTouch[@"timestamp"] = @(nativeTouch.timestamp * 1000); // in ms, for JS
}
-+ (NSArray *)JSMethods
-{
- return @[@"RCTEventEmitter.receiveTouches"];
-}
+RCT_IMPORT_METHOD(RCTEventEmitter, receiveTouches);
/**
* Constructs information about touch events to send across the serialized
diff --git a/React/Base/RCTUtils.h b/React/Base/RCTUtils.h
index 4a6336e20..d20ba8a5f 100644
--- a/React/Base/RCTUtils.h
+++ b/React/Base/RCTUtils.h
@@ -45,9 +45,6 @@ void RCTSwapInstanceMethods(Class cls, SEL original, SEL replacement);
BOOL RCTClassOverridesClassMethod(Class cls, SEL selector);
BOOL RCTClassOverridesInstanceMethod(Class cls, SEL selector);
-// Enumerate all classes that conform to NSObject protocol
-void RCTEnumerateClasses(void (^block)(Class cls));
-
// Creates a standardized error object
// TODO(#6472857): create NSErrors and automatically convert them over the bridge.
NSDictionary *RCTMakeError(NSString *message, id toStringify, NSDictionary *extraData);
diff --git a/React/Base/RCTUtils.m b/React/Base/RCTUtils.m
index 300f6d721..cea45c324 100644
--- a/React/Base/RCTUtils.m
+++ b/React/Base/RCTUtils.m
@@ -183,31 +183,6 @@ BOOL RCTClassOverridesInstanceMethod(Class cls, SEL selector)
return NO;
}
-void RCTEnumerateClasses(void (^block)(Class cls))
-{
- static Class *classes;
- static unsigned int classCount;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- classes = objc_copyClassList(&classCount);
- });
-
- for (unsigned int i = 0; i < classCount; i++)
- {
- Class cls = classes[i];
- Class superclass = cls;
- while (superclass)
- {
- if (class_conformsToProtocol(superclass, @protocol(NSObject)))
- {
- block(cls);
- break;
- }
- superclass = class_getSuperclass(superclass);
- }
- }
-}
-
NSDictionary *RCTMakeError(NSString *message, id toStringify, NSDictionary *extraData)
{
if (toStringify) {
diff --git a/React/Executors/RCTWebViewExecutor.m b/React/Executors/RCTWebViewExecutor.m
index a31a41a8e..e50fff904 100644
--- a/React/Executors/RCTWebViewExecutor.m
+++ b/React/Executors/RCTWebViewExecutor.m
@@ -125,6 +125,8 @@ static void RCTReportError(RCTJavaScriptCallback callback, NSString *fmt, ...)
RCTAssert(onComplete != nil, @"");
_onApplicationScriptLoaded = onComplete;
+ script = [script stringByReplacingOccurrencesOfString:@"" withString:@""];
if (_objectsToInject.count > 0) {
NSMutableString *scriptWithInjections = [[NSMutableString alloc] initWithString:@"/* BEGIN NATIVELY INJECTED OBJECTS */\n"];
[_objectsToInject enumerateKeysAndObjectsUsingBlock:^(NSString *objectName, NSString *blockScript, BOOL *stop) {
diff --git a/React/Modules/RCTAlertManager.m b/React/Modules/RCTAlertManager.m
index bda7c357e..ae11ce52b 100644
--- a/React/Modules/RCTAlertManager.m
+++ b/React/Modules/RCTAlertManager.m
@@ -23,6 +23,8 @@
NSMutableArray *_alertButtonKeys;
}
+RCT_EXPORT_MODULE()
+
- (instancetype)init
{
if ((self = [super init])) {
@@ -47,10 +49,9 @@
* Buttons are displayed in the order they are specified. If "cancel" is used as
* the button key, it will be differently highlighted, according to iOS UI conventions.
*/
-- (void)alertWithArgs:(NSDictionary *)args callback:(RCTResponseSenderBlock)callback
+RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args
+ callback:(RCTResponseSenderBlock)callback)
{
- RCT_EXPORT();
-
NSString *title = args[@"title"];
NSString *message = args[@"message"];
NSArray *buttons = args[@"buttons"];
diff --git a/React/Modules/RCTAppState.m b/React/Modules/RCTAppState.m
index ab44a70a3..9743e5042 100644
--- a/React/Modules/RCTAppState.m
+++ b/React/Modules/RCTAppState.m
@@ -35,6 +35,8 @@ static NSString *RCTCurrentAppBackgroundState()
@synthesize bridge = _bridge;
+RCT_EXPORT_MODULE()
+
#pragma mark - Lifecycle
- (instancetype)init
@@ -79,11 +81,9 @@ static NSString *RCTCurrentAppBackgroundState()
/**
* Get the current background/foreground state of the app
*/
-- (void)getCurrentAppState:(RCTResponseSenderBlock)callback
- error:(__unused RCTResponseSenderBlock)error
+RCT_EXPORT_METHOD(getCurrentAppState:(RCTResponseSenderBlock)callback
+ error:(__unused RCTResponseSenderBlock)error)
{
- RCT_EXPORT();
-
callback(@[@{@"app_state": _lastKnownState}]);
}
diff --git a/React/Modules/RCTAsyncLocalStorage.m b/React/Modules/RCTAsyncLocalStorage.m
index 95fb383e4..8e6d414cf 100644
--- a/React/Modules/RCTAsyncLocalStorage.m
+++ b/React/Modules/RCTAsyncLocalStorage.m
@@ -88,6 +88,8 @@ static dispatch_queue_t RCTFileQueue(void)
NSString *_storageDirectory;
}
+RCT_EXPORT_MODULE()
+
- (NSString *)_filePathForKey:(NSString *)key
{
NSString *safeFileName = RCTMD5Hash(key);
@@ -186,10 +188,9 @@ static dispatch_queue_t RCTFileQueue(void)
#pragma mark - Exported JS Functions
-- (void)multiGet:(NSArray *)keys callback:(RCTResponseSenderBlock)callback
+RCT_EXPORT_METHOD(multiGet:(NSArray *)keys
+ callback:(RCTResponseSenderBlock)callback)
{
- RCT_EXPORT();
-
if (!callback) {
RCTLogError(@"Called getItem without a callback.");
return;
@@ -212,10 +213,9 @@ static dispatch_queue_t RCTFileQueue(void)
});
}
-- (void)multiSet:(NSArray *)kvPairs callback:(RCTResponseSenderBlock)callback
+RCT_EXPORT_METHOD(multiSet:(NSArray *)kvPairs
+ callback:(RCTResponseSenderBlock)callback)
{
- RCT_EXPORT();
-
dispatch_async(RCTFileQueue(), ^{
id errorOut = [self _ensureSetup];
if (errorOut) {
@@ -234,10 +234,9 @@ static dispatch_queue_t RCTFileQueue(void)
});
}
-- (void)multiRemove:(NSArray *)keys callback:(RCTResponseSenderBlock)callback
+RCT_EXPORT_METHOD(multiRemove:(NSArray *)keys
+ callback:(RCTResponseSenderBlock)callback)
{
- RCT_EXPORT();
-
dispatch_async(RCTFileQueue(), ^{
id errorOut = [self _ensureSetup];
if (errorOut) {
@@ -261,10 +260,8 @@ static dispatch_queue_t RCTFileQueue(void)
});
}
-- (void)clear:(RCTResponseSenderBlock)callback
+RCT_EXPORT_METHOD(clear:(RCTResponseSenderBlock)callback)
{
- RCT_EXPORT();
-
dispatch_async(RCTFileQueue(), ^{
id errorOut = [self _ensureSetup];
if (!errorOut) {
@@ -282,10 +279,8 @@ static dispatch_queue_t RCTFileQueue(void)
});
}
-- (void)getAllKeys:(RCTResponseSenderBlock)callback
+RCT_EXPORT_METHOD(getAllKeys:(RCTResponseSenderBlock)callback)
{
- RCT_EXPORT();
-
dispatch_async(RCTFileQueue(), ^{
id errorOut = [self _ensureSetup];
if (errorOut) {
diff --git a/React/Modules/RCTExceptionsManager.m b/React/Modules/RCTExceptionsManager.m
index 01b701f54..1ce5c7383 100644
--- a/React/Modules/RCTExceptionsManager.m
+++ b/React/Modules/RCTExceptionsManager.m
@@ -16,6 +16,8 @@
__weak id _delegate;
}
+RCT_EXPORT_MODULE()
+
- (instancetype)initWithDelegate:(id)delegate
{
if ((self = [super init])) {
@@ -29,10 +31,9 @@
return [self initWithDelegate:nil];
}
-- (void)reportUnhandledExceptionWithMessage:(NSString *)message stack:(NSArray *)stack
+RCT_EXPORT_METHOD(reportUnhandledException:(NSString *)message
+ stack:(NSArray *)stack)
{
- RCT_EXPORT(reportUnhandledException);
-
if (_delegate) {
[_delegate unhandledJSExceptionWithMessage:message stack:stack];
} else {
@@ -40,10 +41,9 @@
}
}
-- (void)updateExceptionMessage:(NSString *)message stack:(NSArray *)stack
+RCT_EXPORT_METHOD(updateExceptionMessage:(NSString *)message
+ stack:(NSArray *)stack)
{
- RCT_EXPORT(updateExceptionMessage);
-
[[RCTRedBox sharedInstance] updateErrorMessage:message withStack:stack];
}
diff --git a/React/Modules/RCTSourceCode.m b/React/Modules/RCTSourceCode.m
index 9ccc0269d..76e9190bc 100644
--- a/React/Modules/RCTSourceCode.m
+++ b/React/Modules/RCTSourceCode.m
@@ -14,9 +14,11 @@
@implementation RCTSourceCode
-- (void)getScriptText:(RCTResponseSenderBlock)successCallback failureCallback:(RCTResponseSenderBlock)failureCallback
+RCT_EXPORT_MODULE()
+
+RCT_EXPORT_METHOD(getScriptText:(RCTResponseSenderBlock)successCallback
+ failureCallback:(RCTResponseSenderBlock)failureCallback)
{
- RCT_EXPORT();
if (self.scriptText && self.scriptURL) {
successCallback(@[@{@"text": self.scriptText, @"url":[self.scriptURL absoluteString]}]);
} else {
diff --git a/React/Modules/RCTStatusBarManager.m b/React/Modules/RCTStatusBarManager.m
index 258ef4e8c..ad8ee1df6 100644
--- a/React/Modules/RCTStatusBarManager.m
+++ b/React/Modules/RCTStatusBarManager.m
@@ -24,10 +24,11 @@ static BOOL RCTViewControllerBasedStatusBarAppearance()
return value;
}
-- (void)setStyle:(UIStatusBarStyle)statusBarStyle animated:(BOOL)animated
-{
- RCT_EXPORT();
+RCT_EXPORT_MODULE()
+RCT_EXPORT_METHOD(setStyle:(UIStatusBarStyle)statusBarStyle
+ animated:(BOOL)animated)
+{
dispatch_async(dispatch_get_main_queue(), ^{
if (RCTViewControllerBasedStatusBarAppearance()) {
@@ -40,10 +41,9 @@ static BOOL RCTViewControllerBasedStatusBarAppearance()
});
}
-- (void)setHidden:(BOOL)hidden withAnimation:(UIStatusBarAnimation)animation
+RCT_EXPORT_METHOD(setHidden:(BOOL)hidden
+ withAnimation:(UIStatusBarAnimation)animation)
{
- RCT_EXPORT();
-
dispatch_async(dispatch_get_main_queue(), ^{
if (RCTViewControllerBasedStatusBarAppearance()) {
diff --git a/React/Modules/RCTTiming.m b/React/Modules/RCTTiming.m
index 5fd46e21c..8c7ef1f23 100644
--- a/React/Modules/RCTTiming.m
+++ b/React/Modules/RCTTiming.m
@@ -63,10 +63,9 @@
@synthesize bridge = _bridge;
-+ (NSArray *)JSMethods
-{
- return @[@"RCTJSTimers.callTimers"];
-}
+RCT_EXPORT_MODULE()
+
+RCT_IMPORT_METHOD(RCTJSTimers, callTimers)
- (instancetype)init
{
@@ -166,34 +165,29 @@
* calculating the timer's target time. We calculate this by passing in
* Date.now() from JS and then subtracting that from the current time here.
*/
-- (void)createTimer:(NSNumber *)callbackID
- duration:(double)jsDuration
- jsSchedulingTime:(double)jsSchedulingTime
- repeats:(BOOL)repeats
+RCT_EXPORT_METHOD(createTimer:(NSNumber *)callbackID
+ duration:(NSTimeInterval)jsDuration
+ jsSchedulingTime:(NSDate *)jsSchedulingTime
+ repeats:(BOOL)repeats)
{
- RCT_EXPORT();
-
if (jsDuration == 0 && repeats == NO) {
// For super fast, one-off timers, just enqueue them immediately rather than waiting a frame.
[_bridge enqueueJSCall:@"RCTJSTimers.callTimers" args:@[@[callbackID]]];
return;
}
- NSTimeInterval interval = jsDuration / 1000;
- NSTimeInterval jsCreationTimeSinceUnixEpoch = jsSchedulingTime / 1000;
- NSTimeInterval currentTimeSinceUnixEpoch = [[NSDate date] timeIntervalSince1970];
- NSTimeInterval jsSchedulingOverhead = currentTimeSinceUnixEpoch - jsCreationTimeSinceUnixEpoch;
+ NSTimeInterval jsSchedulingOverhead = -jsSchedulingTime.timeIntervalSinceNow;
if (jsSchedulingOverhead < 0) {
RCTLogWarn(@"jsSchedulingOverhead (%ims) should be positive", (int)(jsSchedulingOverhead * 1000));
}
- NSTimeInterval targetTime = interval - jsSchedulingOverhead;
- if (interval < 0.018) { // Make sure short intervals run each frame
- interval = 0;
+ NSTimeInterval targetTime = jsDuration - jsSchedulingOverhead;
+ if (jsDuration < 0.018) { // Make sure short intervals run each frame
+ jsDuration = 0;
}
RCTTimer *timer = [[RCTTimer alloc] initWithCallbackID:callbackID
- interval:interval
+ interval:jsDuration
targetTime:targetTime
repeats:repeats];
dispatch_async(dispatch_get_main_queue(), ^{
@@ -202,10 +196,8 @@
});
}
-- (void)deleteTimer:(NSNumber *)timerID
+RCT_EXPORT_METHOD(deleteTimer:(NSNumber *)timerID)
{
- RCT_EXPORT();
-
if (timerID) {
dispatch_async(dispatch_get_main_queue(), ^{
_timers[timerID] = nil;
diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m
index c21adc1d0..1fe672d8b 100644
--- a/React/Modules/RCTUIManager.m
+++ b/React/Modules/RCTUIManager.m
@@ -197,6 +197,8 @@ static UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimatio
@synthesize bridge = _bridge;
+RCT_EXPORT_MODULE()
+
/**
* Declared in RCTBridge.
*/
@@ -209,6 +211,7 @@ extern NSString *RCTBridgeModuleNameForClass(Class cls);
static NSString *RCTViewNameForModuleName(NSString *moduleName)
{
NSString *name = moduleName;
+ RCTCAssert(name.length, @"Invalid moduleName '%@'", moduleName);
if ([name hasSuffix:@"Manager"]) {
name = [name substringToIndex:name.length - @"Manager".length];
}
@@ -511,10 +514,8 @@ static NSString *RCTViewNameForModuleName(NSString *moduleName)
* A method to be called from JS, which takes a container ID and then releases
* all subviews for that container upon receipt.
*/
-- (void)removeSubviewsFromContainerWithID:(NSNumber *)containerID
+RCT_EXPORT_METHOD(removeSubviewsFromContainerWithID:(NSNumber *)containerID)
{
- RCT_EXPORT();
-
id container = _shadowViewRegistry[containerID];
RCTAssert(container != nil, @"container view (for ID %@) not found", containerID);
@@ -568,10 +569,8 @@ static NSString *RCTViewNameForModuleName(NSString *moduleName)
}
}
-- (void)removeRootView:(NSNumber *)rootReactTag
+RCT_EXPORT_METHOD(removeRootView:(NSNumber *)rootReactTag)
{
- RCT_EXPORT();
-
RCTShadowView *rootShadowView = _shadowViewRegistry[rootReactTag];
RCTAssert(rootShadowView.superview == nil, @"root view cannot have superview (ID %@)", rootReactTag);
[self _purgeChildren:rootShadowView.reactSubviews fromRegistry:_shadowViewRegistry];
@@ -586,10 +585,8 @@ static NSString *RCTViewNameForModuleName(NSString *moduleName)
}];
}
-- (void)replaceExistingNonRootView:(NSNumber *)reactTag withView:(NSNumber *)newReactTag
+RCT_EXPORT_METHOD(replaceExistingNonRootView:(NSNumber *)reactTag withView:(NSNumber *)newReactTag)
{
- RCT_EXPORT();
-
RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
RCTAssert(shadowView != nil, @"shadowView (for ID %@) not found", reactTag);
@@ -608,15 +605,13 @@ static NSString *RCTViewNameForModuleName(NSString *moduleName)
removeAtIndices:removeAtIndices];
}
-- (void)manageChildren:(NSNumber *)containerReactTag
- moveFromIndices:(NSArray *)moveFromIndices
- moveToIndices:(NSArray *)moveToIndices
- addChildReactTags:(NSArray *)addChildReactTags
- addAtIndices:(NSArray *)addAtIndices
- removeAtIndices:(NSArray *)removeAtIndices
+RCT_EXPORT_METHOD(manageChildren:(NSNumber *)containerReactTag
+ moveFromIndices:(NSArray *)moveFromIndices
+ moveToIndices:(NSArray *)moveToIndices
+ addChildReactTags:(NSArray *)addChildReactTags
+ addAtIndices:(NSArray *)addAtIndices
+ removeAtIndices:(NSArray *)removeAtIndices)
{
- RCT_EXPORT();
-
[self _manageChildren:containerReactTag
moveFromIndices:moveFromIndices
moveToIndices:moveToIndices
@@ -626,7 +621,6 @@ static NSString *RCTViewNameForModuleName(NSString *moduleName)
registry:_shadowViewRegistry];
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
-
[uiManager _manageChildren:containerReactTag
moveFromIndices:moveFromIndices
moveToIndices:moveToIndices
@@ -735,12 +729,10 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
[shadowView updateLayout];
}
-- (void)createAndRegisterViewWithReactTag:(NSNumber *)reactTag
- viewName:(NSString *)viewName
- props:(NSDictionary *)props
+RCT_EXPORT_METHOD(createView:(NSNumber *)reactTag
+ viewName:(NSString *)viewName
+ props:(NSDictionary *)props)
{
- RCT_EXPORT(createView);
-
RCTViewManager *manager = _viewManagers[viewName];
if (manager == nil) {
RCTLogWarn(@"No manager class found for view with module name \"%@\"", viewName);
@@ -792,12 +784,12 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
}];
}
// TODO: remove viewName param as it isn't needed
-- (void)updateView:(NSNumber *)reactTag viewName:(__unused NSString *)_ props:(NSDictionary *)props
+RCT_EXPORT_METHOD(updateView:(NSNumber *)reactTag
+ viewName:(__unused NSString *)_
+ props:(NSDictionary *)props)
{
- RCT_EXPORT();
-
RCTViewManager *viewManager = _viewManagerRegistry[reactTag];
- NSString *viewName = RCTViewNameForModuleName([[viewManager class] moduleName]);
+ NSString *viewName = RCTViewNameForModuleName(RCTBridgeModuleNameForClass([viewManager class]));
RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
RCTSetShadowViewProps(props, shadowView, _defaultShadowViews[viewName], viewManager);
@@ -808,10 +800,8 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
}];
}
-- (void)becomeResponder:(NSNumber *)reactTag
+RCT_EXPORT_METHOD(focus:(NSNumber *)reactTag)
{
- RCT_EXPORT(focus);
-
if (!reactTag) return;
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
UIView *newResponder = viewRegistry[reactTag];
@@ -821,10 +811,8 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
}];
}
-- (void)resignResponder:(NSNumber *)reactTag
+RCT_EXPORT_METHOD(blur:(NSNumber *)reactTag)
{
- RCT_EXPORT(blur);
-
if (!reactTag) return;
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
UIView *currentResponder = viewRegistry[reactTag];
@@ -889,10 +877,9 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
});
}
-- (void)measure:(NSNumber *)reactTag callback:(RCTResponseSenderBlock)callback
+RCT_EXPORT_METHOD(measure:(NSNumber *)reactTag
+ callback:(RCTResponseSenderBlock)callback)
{
- RCT_EXPORT();
-
if (!callback) {
RCTLogError(@"Called measure with no callback");
return;
@@ -965,13 +952,11 @@ static void RCTMeasureLayout(RCTShadowView *view,
* anything on the main UI thread. Invokes supplied callback with (x, y, width,
* height).
*/
-- (void)measureLayout:(NSNumber *)reactTag
- relativeTo:(NSNumber *)ancestorReactTag
- errorCallback:(RCTResponseSenderBlock)errorCallback
- callback:(RCTResponseSenderBlock)callback
+RCT_EXPORT_METHOD(measureLayout:(NSNumber *)reactTag
+ relativeTo:(NSNumber *)ancestorReactTag
+ errorCallback:(RCTResponseSenderBlock)errorCallback
+ callback:(RCTResponseSenderBlock)callback)
{
- RCT_EXPORT();
-
RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
RCTShadowView *ancestorShadowView = _shadowViewRegistry[ancestorReactTag];
RCTMeasureLayout(shadowView, ancestorShadowView, callback);
@@ -984,12 +969,10 @@ static void RCTMeasureLayout(RCTShadowView *view,
* anything on the main UI thread. Invokes supplied callback with (x, y, width,
* height).
*/
-- (void)measureLayoutRelativeToParent:(NSNumber *)reactTag
- errorCallback:(RCTResponseSenderBlock)errorCallback
- callback:(RCTResponseSenderBlock)callback
+RCT_EXPORT_METHOD(measureLayoutRelativeToParent:(NSNumber *)reactTag
+ errorCallback:(RCTResponseSenderBlock)errorCallback
+ callback:(RCTResponseSenderBlock)callback)
{
- RCT_EXPORT();
-
RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
RCTMeasureLayout(shadowView, shadowView.reactSuperview, callback);
}
@@ -1001,13 +984,11 @@ static void RCTMeasureLayout(RCTShadowView *view,
* Only layouts for views that are within the rect passed in are returned. Invokes the error callback if the
* passed in parent view does not exist. Invokes the supplied callback with the array of computed layouts.
*/
-- (void)measureViewsInRect:(NSDictionary *)rect
- parentView:(NSNumber *)reactTag
- errorCallback:(RCTResponseSenderBlock)errorCallback
- callback:(RCTResponseSenderBlock)callback
+RCT_EXPORT_METHOD(measureViewsInRect:(NSDictionary *)rect
+ parentView:(NSNumber *)reactTag
+ errorCallback:(RCTResponseSenderBlock)errorCallback
+ callback:(RCTResponseSenderBlock)callback)
{
- RCT_EXPORT();
-
RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
if (!shadowView) {
RCTLogError(@"Attempting to measure view that does not exist (tag #%@)", reactTag);
@@ -1047,10 +1028,8 @@ static void RCTMeasureLayout(RCTShadowView *view,
callback(@[results]);
}
-- (void)setMainScrollViewTag:(NSNumber *)reactTag
+RCT_EXPORT_METHOD(setMainScrollViewTag:(NSNumber *)reactTag)
{
- RCT_EXPORT();
-
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
// - There should be at most one designated "main scroll view"
// - There should be at most one designated "`nativeMainScrollDelegate`"
@@ -1073,10 +1052,10 @@ static void RCTMeasureLayout(RCTShadowView *view,
}];
}
-- (void)scrollToOffsetWithView:(NSNumber *)reactTag scrollToOffsetX:(NSNumber *)offsetX offsetY:(NSNumber *)offsetY
+RCT_EXPORT_METHOD(scrollTo:(NSNumber *)reactTag
+ withOffsetX:(NSNumber *)offsetX
+ offsetY:(NSNumber *)offsetY)
{
- RCT_EXPORT(scrollTo);
-
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
UIView *view = viewRegistry[reactTag];
if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
@@ -1087,10 +1066,10 @@ static void RCTMeasureLayout(RCTShadowView *view,
}];
}
-- (void)scrollWithoutAnimationToOffsetWithView:(NSNumber *)reactTag scrollToOffsetX:(NSNumber *)offsetX offsetY:(NSNumber *)offsetY
+RCT_EXPORT_METHOD(scrollWithoutAnimationTo:(NSNumber *)reactTag
+ offsetX:(NSNumber *)offsetX
+ offsetY:(NSNumber *)offsetY)
{
- RCT_EXPORT(scrollWithoutAnimationTo);
-
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
UIView *view = viewRegistry[reactTag];
if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
@@ -1101,10 +1080,9 @@ static void RCTMeasureLayout(RCTShadowView *view,
}];
}
-- (void)zoomToRectWithView:(NSNumber *)reactTag rect:(NSDictionary *)rectDict
+RCT_EXPORT_METHOD(zoomToRect:(NSNumber *)reactTag
+ withRect:(NSDictionary *)rectDict)
{
- RCT_EXPORT(zoomToRect);
-
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
UIView *view = viewRegistry[reactTag];
if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
@@ -1119,10 +1097,8 @@ static void RCTMeasureLayout(RCTShadowView *view,
* JS sets what *it* considers to be the responder. Later, scroll views can use
* this in order to determine if scrolling is appropriate.
*/
-- (void)setJSResponder:(NSNumber *)reactTag
+RCT_EXPORT_METHOD(setJSResponder:(NSNumber *)reactTag)
{
- RCT_EXPORT();
-
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
_jsResponder = viewRegistry[reactTag];
if (!_jsResponder) {
@@ -1131,10 +1107,8 @@ static void RCTMeasureLayout(RCTShadowView *view,
}];
}
-- (void)clearJSResponder
+RCT_EXPORT_METHOD(clearJSResponder)
{
- RCT_EXPORT();
-
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
_jsResponder = nil;
}];
@@ -1404,12 +1378,10 @@ static void RCTMeasureLayout(RCTShadowView *view,
return allJSConstants;
}
-- (void)configureNextLayoutAnimation:(NSDictionary *)config
- withCallback:(RCTResponseSenderBlock)callback
- errorCallback:(RCTResponseSenderBlock)errorCallback
+RCT_EXPORT_METHOD(configureNextLayoutAnimation:(NSDictionary *)config
+ withCallback:(RCTResponseSenderBlock)callback
+ errorCallback:(RCTResponseSenderBlock)errorCallback)
{
- RCT_EXPORT();
-
if (_nextLayoutAnimation) {
RCTLogWarn(@"Warning: Overriding previous layout animation with new one before the first began:\n%@ -> %@.",
_nextLayoutAnimation, config);
@@ -1417,13 +1389,12 @@ static void RCTMeasureLayout(RCTShadowView *view,
if (config[@"delete"] != nil) {
RCTLogError(@"LayoutAnimation only supports create and update right now. Config: %@", config);
}
- _nextLayoutAnimation = [[RCTLayoutAnimation alloc] initWithDictionary:config callback:callback];
+ _nextLayoutAnimation = [[RCTLayoutAnimation alloc] initWithDictionary:config
+ callback:callback];
}
-- (void)startOrResetInteractionTiming
+RCT_EXPORT_METHOD(startOrResetInteractionTiming)
{
- RCT_EXPORT();
-
NSSet *rootViewTags = [_rootViewTags copy];
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
for (NSNumber *reactTag in rootViewTags) {
@@ -1435,11 +1406,9 @@ static void RCTMeasureLayout(RCTShadowView *view,
}];
}
-- (void)endAndResetInteractionTiming:(RCTResponseSenderBlock)onSuccess
- onError:(RCTResponseSenderBlock)onError
+RCT_EXPORT_METHOD(endAndResetInteractionTiming:(RCTResponseSenderBlock)onSuccess
+ onError:(RCTResponseSenderBlock)onError)
{
- RCT_EXPORT();
-
NSSet *rootViewTags = [_rootViewTags copy];
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
NSMutableDictionary *timingData = [[NSMutableDictionary alloc] init];
@@ -1449,7 +1418,7 @@ static void RCTMeasureLayout(RCTShadowView *view,
timingData[reactTag.stringValue] = [rootView endAndResetInteractionTiming];
}
}
- onSuccess(@[ timingData ]);
+ onSuccess(@[timingData]);
}];
}
@@ -1466,7 +1435,7 @@ static UIView *_jsResponder;
- (RCTUIManager *)uiManager
{
- return self.modules[NSStringFromClass([RCTUIManager class])];
+ return self.modules[RCTBridgeModuleNameForClass([RCTUIManager class])];
}
@end
diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj
index 6f99acaab..5c9c13355 100644
--- a/React/React.xcodeproj/project.pbxproj
+++ b/React/React.xcodeproj/project.pbxproj
@@ -146,7 +146,6 @@
13E067501A70F44B002CDEE1 /* RCTView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTView.m; sourceTree = ""; };
13E067531A70F44B002CDEE1 /* UIView+React.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+React.h"; sourceTree = ""; };
13E067541A70F44B002CDEE1 /* UIView+React.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+React.m"; sourceTree = ""; };
- 13EFFCCF1A98E6FE002607DC /* RCTJSMethodRegistrar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTJSMethodRegistrar.h; sourceTree = ""; };
14200DA81AC179B3008EE6BA /* RCTJavaScriptLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTJavaScriptLoader.h; sourceTree = ""; };
14200DA91AC179B3008EE6BA /* RCTJavaScriptLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTJavaScriptLoader.m; sourceTree = ""; };
14435CE11AAC4AE100FC20F4 /* RCTMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMap.h; sourceTree = ""; };
@@ -366,7 +365,6 @@
83CBBA661A601EF300E9B192 /* RCTEventDispatcher.m */,
83CBBA4C1A601E3B00E9B192 /* RCTInvalidating.h */,
83CBBA631A601ECA00E9B192 /* RCTJavaScriptExecutor.h */,
- 13EFFCCF1A98E6FE002607DC /* RCTJSMethodRegistrar.h */,
13A1F71C1A75392D00D3D453 /* RCTKeyCommands.h */,
13A1F71D1A75392D00D3D453 /* RCTKeyCommands.m */,
83CBBA4D1A601E3B00E9B192 /* RCTLog.h */,
@@ -452,7 +450,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "if nc -w 5 -z localhost 8081 ; then\n if ! curl -4 -s \"http://localhost:8081/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port 8081 already in use, packager is either not running or not running correctly\"\n exit 2\n fi\nelse\n open $SRCROOT/../packager/launchPackager.command || echo \"Can't start packager automatically\"\nfi";
+ shellScript = "if nc -w 5 -z localhost 8081 ; then\n if ! curl -s \"http://localhost:8081/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port 8081 already in use, packager is either not running or not running correctly\"\n exit 2\n fi\nelse\n open $SRCROOT/../packager/launchPackager.command || echo \"Can't start packager automatically\"\nfi";
};
/* End PBXShellScriptBuildPhase section */
diff --git a/React/Views/RCTDatePickerManager.m b/React/Views/RCTDatePickerManager.m
index 4029998c2..36397d6e5 100644
--- a/React/Views/RCTDatePickerManager.m
+++ b/React/Views/RCTDatePickerManager.m
@@ -27,6 +27,8 @@ RCT_ENUM_CONVERTER(UIDatePickerMode, (@{
@implementation RCTDatePickerManager
+RCT_EXPORT_MODULE()
+
- (UIView *)view
{
UIDatePicker *picker = [[UIDatePicker alloc] init];
diff --git a/React/Views/RCTMapManager.m b/React/Views/RCTMapManager.m
index 9bd0f7eab..24d8bee16 100644
--- a/React/Views/RCTMapManager.m
+++ b/React/Views/RCTMapManager.m
@@ -54,6 +54,8 @@
@implementation RCTMapManager
+RCT_EXPORT_MODULE()
+
- (UIView *)view
{
RCTMap *map = [[RCTMap alloc] init];
diff --git a/React/Views/RCTNavItemManager.m b/React/Views/RCTNavItemManager.m
index 549859ae0..b6d38ac00 100644
--- a/React/Views/RCTNavItemManager.m
+++ b/React/Views/RCTNavItemManager.m
@@ -14,6 +14,8 @@
@implementation RCTNavItemManager
+RCT_EXPORT_MODULE()
+
- (UIView *)view
{
return [[RCTNavItem alloc] init];
diff --git a/React/Views/RCTNavigatorManager.m b/React/Views/RCTNavigatorManager.m
index 7df00c764..730380bf9 100644
--- a/React/Views/RCTNavigatorManager.m
+++ b/React/Views/RCTNavigatorManager.m
@@ -17,6 +17,8 @@
@implementation RCTNavigatorManager
+RCT_EXPORT_MODULE()
+
- (UIView *)view
{
return [[RCTNavigator alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
@@ -34,12 +36,10 @@ RCT_EXPORT_VIEW_PROPERTY(requestedTopOfStack, NSInteger)
}
// TODO: remove error callbacks
-- (void)requestSchedulingJavaScriptNavigation:(NSNumber *)reactTag
- errorCallback:(RCTResponseSenderBlock)errorCallback
- callback:(__unused RCTResponseSenderBlock)callback
+RCT_EXPORT_METHOD(requestSchedulingJavaScriptNavigation:(NSNumber *)reactTag
+ errorCallback:(RCTResponseSenderBlock)errorCallback
+ callback:(__unused RCTResponseSenderBlock)callback)
{
- RCT_EXPORT();
-
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
RCTNavigator *navigator = viewRegistry[reactTag];
if ([navigator isKindOfClass:[RCTNavigator class]]) {
diff --git a/React/Views/RCTPickerManager.m b/React/Views/RCTPickerManager.m
index cffacca68..3bbc60b94 100644
--- a/React/Views/RCTPickerManager.m
+++ b/React/Views/RCTPickerManager.m
@@ -15,6 +15,8 @@
@implementation RCTPickerManager
+RCT_EXPORT_MODULE()
+
- (UIView *)view
{
return [[RCTPicker alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
diff --git a/React/Views/RCTScrollViewManager.m b/React/Views/RCTScrollViewManager.m
index 066d28adc..ededf5f7f 100644
--- a/React/Views/RCTScrollViewManager.m
+++ b/React/Views/RCTScrollViewManager.m
@@ -17,6 +17,8 @@
@implementation RCTScrollViewManager
+RCT_EXPORT_MODULE()
+
- (UIView *)view
{
return [[RCTScrollView alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
@@ -63,11 +65,9 @@ RCT_DEPRECATED_VIEW_PROPERTY(throttleScrollCallbackMS, scrollEventThrottle)
};
}
-- (void)getContentSize:(NSNumber *)reactTag
- callback:(RCTResponseSenderBlock)callback
+RCT_EXPORT_METHOD(getContentSize:(NSNumber *)reactTag
+ callback:(RCTResponseSenderBlock)callback)
{
- RCT_EXPORT();
-
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
UIView *view = viewRegistry[reactTag];
diff --git a/React/Views/RCTSliderManager.m b/React/Views/RCTSliderManager.m
index 0228eab68..58b763b92 100644
--- a/React/Views/RCTSliderManager.m
+++ b/React/Views/RCTSliderManager.m
@@ -15,6 +15,8 @@
@implementation RCTSliderManager
+RCT_EXPORT_MODULE()
+
- (UIView *)view
{
UISlider *slider = [[UISlider alloc] init];
diff --git a/React/Views/RCTSwitchManager.m b/React/Views/RCTSwitchManager.m
index e2118023d..eb0d626e6 100644
--- a/React/Views/RCTSwitchManager.m
+++ b/React/Views/RCTSwitchManager.m
@@ -16,6 +16,8 @@
@implementation RCTSwitchManager
+RCT_EXPORT_MODULE()
+
- (UIView *)view
{
RCTSwitch *switcher = [[RCTSwitch alloc] init];
diff --git a/React/Views/RCTTabBarItemManager.m b/React/Views/RCTTabBarItemManager.m
index 7f4ce9642..8bbe782b7 100644
--- a/React/Views/RCTTabBarItemManager.m
+++ b/React/Views/RCTTabBarItemManager.m
@@ -14,6 +14,8 @@
@implementation RCTTabBarItemManager
+RCT_EXPORT_MODULE()
+
- (UIView *)view
{
return [[RCTTabBarItem alloc] init];
diff --git a/React/Views/RCTTabBarManager.m b/React/Views/RCTTabBarManager.m
index 7ff9ef075..c7dfe09e1 100644
--- a/React/Views/RCTTabBarManager.m
+++ b/React/Views/RCTTabBarManager.m
@@ -16,6 +16,8 @@
@synthesize bridge = _bridge;
+RCT_EXPORT_MODULE()
+
- (UIView *)view
{
return [[RCTTabBar alloc] initWithEventDispatcher:_bridge.eventDispatcher];
diff --git a/React/Views/RCTTextFieldManager.m b/React/Views/RCTTextFieldManager.m
index 041474643..3cfdd53a1 100644
--- a/React/Views/RCTTextFieldManager.m
+++ b/React/Views/RCTTextFieldManager.m
@@ -17,6 +17,8 @@
@implementation RCTTextFieldManager
+RCT_EXPORT_MODULE()
+
- (UIView *)view
{
return [[RCTTextField alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
diff --git a/React/Views/RCTUIActivityIndicatorViewManager.m b/React/Views/RCTUIActivityIndicatorViewManager.m
index 09a5cb6f9..e2c9b3d35 100644
--- a/React/Views/RCTUIActivityIndicatorViewManager.m
+++ b/React/Views/RCTUIActivityIndicatorViewManager.m
@@ -24,6 +24,8 @@ RCT_ENUM_CONVERTER(UIActivityIndicatorViewStyle, (@{
@implementation RCTUIActivityIndicatorViewManager
+RCT_EXPORT_MODULE(UIActivityIndicatorViewManager)
+
- (UIView *)view
{
return [[UIActivityIndicatorView alloc] init];
diff --git a/React/Views/RCTViewManager.h b/React/Views/RCTViewManager.h
index 32babecc9..17d89dff6 100644
--- a/React/Views/RCTViewManager.h
+++ b/React/Views/RCTViewManager.h
@@ -30,14 +30,6 @@ typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, RCTSparseArray *v
*/
@property (nonatomic, strong) RCTBridge *bridge;
-/**
- * The module name exposed to React JS. If omitted, this will be inferred
- * automatically by using the view module's class name. It is better to not
- * override this, and just follow standard naming conventions for your view
- * module subclasses.
- */
-+ (NSString *)moduleName;
-
/**
* This method instantiates a native view to be managed by the module. Override
* this to return a custom view instance, which may be preconfigured with default
diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m
index 50d55a8a7..9758a0944 100644
--- a/React/Views/RCTViewManager.m
+++ b/React/Views/RCTViewManager.m
@@ -21,18 +21,7 @@
@synthesize bridge = _bridge;
-+ (NSString *)moduleName
-{
- // Default implementation, works in most cases
- NSString *name = NSStringFromClass(self);
- if ([name hasPrefix:@"RK"]) {
- name = [name stringByReplacingCharactersInRange:(NSRange){0,@"RK".length} withString:@"RCT"];
- }
- if ([name hasPrefix:@"RCTUI"]) {
- name = [name substringFromIndex:@"RCT".length];
- }
- return name;
-}
+RCT_EXPORT_MODULE()
- (UIView *)view
{
diff --git a/React/Views/RCTWebViewManager.m b/React/Views/RCTWebViewManager.m
index 7525ee236..e25a7da68 100644
--- a/React/Views/RCTWebViewManager.m
+++ b/React/Views/RCTWebViewManager.m
@@ -16,6 +16,8 @@
@implementation RCTWebViewManager
+RCT_EXPORT_MODULE()
+
- (UIView *)view
{
return [[RCTWebView alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
@@ -41,10 +43,8 @@ RCT_EXPORT_VIEW_PROPERTY(shouldInjectAJAXHandler, BOOL);
};
}
-- (void)goBack:(NSNumber *)reactTag
+RCT_EXPORT_METHOD(goBack:(NSNumber *)reactTag)
{
- RCT_EXPORT();
-
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
RCTWebView *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RCTWebView class]]) {
@@ -54,10 +54,8 @@ RCT_EXPORT_VIEW_PROPERTY(shouldInjectAJAXHandler, BOOL);
}];
}
-- (void)goForward:(NSNumber *)reactTag
+RCT_EXPORT_METHOD(goForward:(NSNumber *)reactTag)
{
- RCT_EXPORT();
-
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
id view = viewRegistry[reactTag];
if (![view isKindOfClass:[RCTWebView class]]) {
@@ -68,10 +66,8 @@ RCT_EXPORT_VIEW_PROPERTY(shouldInjectAJAXHandler, BOOL);
}
-- (void)reload:(NSNumber *)reactTag
+RCT_EXPORT_METHOD(reload:(NSNumber *)reactTag)
{
- RCT_EXPORT();
-
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
RCTWebView *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RCTWebView class]]) {
diff --git a/package.json b/package.json
index eaf176a38..f372f7882 100644
--- a/package.json
+++ b/package.json
@@ -39,7 +39,6 @@
"start": "./packager/packager.sh"
},
"bin": {
- "react-native": "local-cli/wrong-react-native.js",
"react-native-start": "packager/packager.sh"
},
"dependencies": {
@@ -62,7 +61,9 @@
"underscore": "1.7.0",
"worker-farm": "1.1.0",
"ws": "0.4.31",
- "yargs": "1.3.2"
+ "yargs": "1.3.2",
+ "bluebird": "^2.9.21",
+ "image-size": "0.3.5"
},
"devDependencies": {
"jest-cli": "0.2.1",
diff --git a/packager/getFlowTypeCheckMiddleware.js b/packager/getFlowTypeCheckMiddleware.js
new file mode 100644
index 000000000..e0f98bde2
--- /dev/null
+++ b/packager/getFlowTypeCheckMiddleware.js
@@ -0,0 +1,86 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+'use strict';
+
+var exec = require('child_process').exec;
+
+function getFlowTypeCheckMiddleware(options) {
+ return function(req, res, next) {
+ if (options.skipflow) {
+ return next();
+ }
+ if (options.flowroot || options.projectRoots.length === 1) {
+ var flowroot = options.flowroot || options.projectRoots[0];
+ } else {
+ console.warn('flow: No suitable root');
+ return next();
+ }
+ exec('command -v flow >/dev/null 2>&1', function(error, stdout) {
+ if (error) {
+ console.warn('flow: Skipping because not installed. Install with ' +
+ '`brew install flow`.');
+ return next();
+ } else {
+ return doFlowTypecheck(res, flowroot, next);
+ }
+ });
+ };
+}
+
+function doFlowTypecheck(res, flowroot, next) {
+ var flowCmd = 'cd "' + flowroot + '" && flow --json --timeout 20';
+ var start = Date.now();
+ console.log('flow: Running static typechecks.');
+ exec(flowCmd, function(flowError, stdout) {
+ if (!flowError) {
+ console.log('flow: Typechecks passed (' + (Date.now() - start) + 'ms).');
+ return next();
+ } else {
+ try {
+ var flowResponse = JSON.parse(stdout);
+ var errors = [];
+ var errorNum = 1;
+ flowResponse.errors.forEach(function(err) {
+ // flow errors are paired across callsites, so we indent and prefix to
+ // group them
+ var indent = '';
+ err.message.forEach(function(msg) {
+ errors.push({
+ description: indent + 'E' + errorNum + ': ' + msg.descr,
+ filename: msg.path,
+ lineNumber: msg.line,
+ column: msg.start,
+ });
+ indent = ' ';
+ });
+ errorNum++;
+ });
+ var message = 'Flow found type errors. If you think these are wrong, ' +
+ 'make sure flow is up to date, or disable with --skipflow.';
+ } catch (e) {
+ var message =
+ 'Flow failed to provide parseable output:\n\n`' + stdout + '`';
+ console.error(message, '\nException: `', e, '`\n\n');
+ }
+ var error = {
+ status: 500,
+ message: message,
+ type: 'FlowError',
+ errors: errors,
+ };
+ console.error('flow: Error running command `' + flowCmd + '`:\n', error);
+ res.writeHead(error.status, {
+ 'Content-Type': 'application/json; charset=UTF-8',
+ });
+ res.end(JSON.stringify(error));
+ }
+ });
+}
+
+module.exports = getFlowTypeCheckMiddleware;
diff --git a/packager/packager.js b/packager/packager.js
index 55004b7cf..b098f0a31 100644
--- a/packager/packager.js
+++ b/packager/packager.js
@@ -13,6 +13,8 @@ var path = require('path');
var exec = require('child_process').exec;
var http = require('http');
+var getFlowTypeCheckMiddleware = require('./getFlowTypeCheckMiddleware');
+
if (!fs.existsSync(path.resolve(__dirname, '..', 'node_modules'))) {
console.log(
'\n' +
@@ -40,6 +42,9 @@ var options = parseCommandLine([{
}, {
command: 'assetRoots',
description: 'specify the root directories of app assets'
+}, {
+ command: 'skipflow',
+ description: 'Disable flow checks'
}]);
if (options.projectRoots) {
@@ -203,6 +208,7 @@ function runServer(
.use(openStackFrameInEditor)
.use(getDevToolsLauncher(options))
.use(statusPageMiddleware)
+ .use(getFlowTypeCheckMiddleware(options))
.use(getAppMiddleware(options));
options.projectRoots.forEach(function(root) {
@@ -213,5 +219,5 @@ function runServer(
.use(connect.compress())
.use(connect.errorHandler());
- return http.createServer(app).listen(options.port, readyCallback);
+ return http.createServer(app).listen(options.port, '::', readyCallback);
}
diff --git a/packager/react-packager/src/DependencyResolver/ModuleDescriptor.js b/packager/react-packager/src/DependencyResolver/ModuleDescriptor.js
index c56593cfa..c46a57e60 100644
--- a/packager/react-packager/src/DependencyResolver/ModuleDescriptor.js
+++ b/packager/react-packager/src/DependencyResolver/ModuleDescriptor.js
@@ -30,8 +30,13 @@ function ModuleDescriptor(fields) {
this.isPolyfill = fields.isPolyfill || false;
+ this.isAsset_DEPRECATED = fields.isAsset_DEPRECATED || false;
this.isAsset = fields.isAsset || false;
+ if (this.isAsset_DEPRECATED && this.isAsset) {
+ throw new Error('Cannot be an asset and a deprecated asset');
+ }
+
this.altId = fields.altId;
this._fields = fields;
diff --git a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js
index b6a978c63..f42f6f8a1 100644
--- a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js
+++ b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js
@@ -92,7 +92,7 @@ describe('DependencyGraph', function() {
{ id: 'image!a',
path: '/root/imgs/a.png',
dependencies: [],
- isAsset: true
+ isAsset_DEPRECATED: true
},
]);
});
@@ -183,7 +183,7 @@ describe('DependencyGraph', function() {
id: 'image!a',
path: '/root/imgs/a.png',
dependencies: [],
- isAsset: true
+ isAsset_DEPRECATED: true
},
]);
});
@@ -954,7 +954,7 @@ describe('DependencyGraph', function() {
{ id: 'image!foo',
path: '/root/foo.png',
dependencies: [],
- isAsset: true,
+ isAsset_DEPRECATED: true,
},
]);
});
diff --git a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js
index 3348907f1..fbc7de712 100644
--- a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js
+++ b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js
@@ -596,7 +596,7 @@ DependecyGraph.prototype._processAsset_DEPRECATED = function(file) {
this._assetMap_DEPRECATED[name] = new ModuleDescriptor({
id: 'image!' + name,
path: path.resolve(file),
- isAsset: true,
+ isAsset_DEPRECATED: true,
dependencies: [],
});
}
diff --git a/packager/react-packager/src/JSTransformer/index.js b/packager/react-packager/src/JSTransformer/index.js
index abfae2482..962eb7fe9 100644
--- a/packager/react-packager/src/JSTransformer/index.js
+++ b/packager/react-packager/src/JSTransformer/index.js
@@ -125,7 +125,7 @@ function formatError(err, filename, source) {
function formatGenericError(err, filename) {
var msg = 'TransformError: ' + filename + ': ' + err.message;
var error = new TransformError();
- var stack = err.stack.split('\n').slice(0, -1);
+ var stack = (err.stack || '').split('\n').slice(0, -1);
stack.push(msg);
error.stack = stack.join('\n');
error.message = msg;
diff --git a/packager/react-packager/src/Packager/__tests__/Packager-test.js b/packager/react-packager/src/Packager/__tests__/Packager-test.js
index 8f61df972..f4675c471 100644
--- a/packager/react-packager/src/Packager/__tests__/Packager-test.js
+++ b/packager/react-packager/src/Packager/__tests__/Packager-test.js
@@ -43,14 +43,21 @@ describe('Packager', function() {
};
});
- var packager = new Packager({projectRoots: []});
+ var packager = new Packager({projectRoots: ['/root']});
var modules = [
{id: 'foo', path: '/root/foo.js', dependencies: []},
{id: 'bar', path: '/root/bar.js', dependencies: []},
- { id: 'image!img',
+ {
+ id: 'image!img',
path: '/root/img/img.png',
- isAsset: true,
+ isAsset_DEPRECATED: true,
dependencies: [],
+ },
+ {
+ id: 'new_image.png',
+ path: '/root/img/new_image.png',
+ isAsset: true,
+ dependencies: []
}
];
@@ -74,6 +81,10 @@ describe('Packager', function() {
return 'lol ' + code + ' lol';
});
+ require('image-size').mockImpl(function(path, cb) {
+ cb(null, { width: 50, height: 100 });
+ });
+
return packager.package('/root/foo.js', true, 'source_map_url')
.then(function(p) {
expect(p.addModule.mock.calls[0]).toEqual([
@@ -96,6 +107,24 @@ describe('Packager', function() {
'/root/img/img.png'
]);
+ var imgModule = {
+ isStatic: true,
+ path: '/root/img/new_image.png',
+ uri: 'img/new_image.png',
+ width: 50,
+ height: 100,
+ };
+
+ expect(p.addModule.mock.calls[3]).toEqual([
+ 'lol module.exports = ' +
+ JSON.stringify(imgModule) +
+ '; lol',
+ 'module.exports = ' +
+ JSON.stringify(imgModule) +
+ ';',
+ '/root/img/new_image.png'
+ ]);
+
expect(p.finalize.mock.calls[0]).toEqual([
{runMainModule: true}
]);
diff --git a/packager/react-packager/src/Packager/index.js b/packager/react-packager/src/Packager/index.js
index bf5a635da..cfdd842db 100644
--- a/packager/react-packager/src/Packager/index.js
+++ b/packager/react-packager/src/Packager/index.js
@@ -18,6 +18,7 @@ var _ = require('underscore');
var Package = require('./Package');
var Activity = require('../Activity');
var declareOpts = require('../lib/declareOpts');
+var imageSize = require('image-size');
var validateOpts = declareOpts({
projectRoots: {
@@ -88,6 +89,8 @@ function Packager(options) {
transformModulePath: opts.transformModulePath,
nonPersistent: opts.nonPersistent,
});
+
+ this._projectRoots = opts.projectRoots;
}
Packager.prototype.kill = function() {
@@ -138,8 +141,13 @@ Packager.prototype.getDependencies = function(main, isDev) {
Packager.prototype._transformModule = function(module) {
var transform;
- if (module.isAsset) {
- transform = Promise.resolve(generateAssetModule(module));
+ if (module.isAsset_DEPRECATED) {
+ transform = Promise.resolve(generateAssetModule_DEPRECATED(module));
+ } else if (module.isAsset) {
+ transform = generateAssetModule(
+ module,
+ getPathRelativeToRoot(this._projectRoots, module.path)
+ );
} else {
transform = this._transformer.loadFileAndTransform(
path.resolve(module.path)
@@ -166,7 +174,7 @@ Packager.prototype.getGraphDebugInfo = function() {
return this._resolver.getDebugInfo();
};
-function generateAssetModule(module) {
+function generateAssetModule_DEPRECATED(module) {
var code = 'module.exports = ' + JSON.stringify({
uri: module.id.replace(/^[^!]+!/, ''),
isStatic: true,
@@ -179,4 +187,39 @@ function generateAssetModule(module) {
};
}
+var sizeOf = Promise.promisify(imageSize);
+
+function generateAssetModule(module, relPath) {
+ return sizeOf(module.path).then(function(dimensions) {
+ var img = {
+ isStatic: true,
+ path: module.path, //TODO(amasad): this should be path inside tar file.
+ uri: relPath,
+ width: dimensions.width,
+ height: dimensions.height,
+ };
+
+ var code = 'module.exports = ' + JSON.stringify(img) + ';';
+
+ return {
+ code: code,
+ sourceCode: code,
+ sourcePath: module.path,
+ };
+ });
+}
+
+function getPathRelativeToRoot(roots, absPath) {
+ for (var i = 0; i < roots.length; i++) {
+ var relPath = path.relative(roots[i], absPath);
+ if (relPath[0] !== '.') {
+ return relPath;
+ }
+ }
+
+ throw new Error(
+ 'Expected root module to be relative to one of the project roots'
+ );
+}
+
module.exports = Packager;
diff --git a/packager/react-packager/src/Server/index.js b/packager/react-packager/src/Server/index.js
index 655075815..617359bf8 100644
--- a/packager/react-packager/src/Server/index.js
+++ b/packager/react-packager/src/Server/index.js
@@ -320,6 +320,12 @@ function handleError(res, error) {
});
if (error.type === 'TransformError' || error.type === 'NotFoundError') {
+ error.errors = [{
+ description: error.description,
+ filename: error.filename,
+ lineNumber: error.lineNumber,
+ }];
+ console.error(error);
res.end(JSON.stringify(error));
} else {
console.error(error.stack || error);