diff --git a/Examples/2048/AppDelegate.m b/Examples/2048/AppDelegate.m
index 76172139c..32d9dd85c 100644
--- a/Examples/2048/AppDelegate.m
+++ b/Examples/2048/AppDelegate.m
@@ -20,7 +20,7 @@
//
// To run on device, change `localhost` to the IP address of your computer, and make sure your computer and
// iOS device are on the same Wi-Fi network.
- jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/2048/Game2048.includeRequire.runModule.bundle?dev=true"];
+ jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/2048/Game2048.includeRequire.runModule.bundle"];
// OPTION 2
// Load from pre-bundled file on disk. To re-generate the static bundle, run
diff --git a/Examples/Movies/AppDelegate.m b/Examples/Movies/AppDelegate.m
index c53f8edc4..c01fc2ca9 100644
--- a/Examples/Movies/AppDelegate.m
+++ b/Examples/Movies/AppDelegate.m
@@ -20,7 +20,7 @@
//
// To run on device, change `localhost` to the IP address of your computer, and make sure your computer and
// iOS device are on the same Wi-Fi network.
- jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/Movies/MoviesApp.includeRequire.runModule.bundle?dev=true"];
+ jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/Movies/MoviesApp.includeRequire.runModule.bundle"];
// OPTION 2
// Load from pre-bundled file on disk. To re-generate the static bundle, run
diff --git a/Examples/TicTacToe/AppDelegate.m b/Examples/TicTacToe/AppDelegate.m
index d2add4fac..52e682752 100644
--- a/Examples/TicTacToe/AppDelegate.m
+++ b/Examples/TicTacToe/AppDelegate.m
@@ -20,7 +20,7 @@
//
// To run on device, change `localhost` to the IP address of your computer, and make sure your computer and
// iOS device are on the same Wi-Fi network.
- jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/TicTacToe/TicTacToeApp.includeRequire.runModule.bundle?dev=true"];
+ jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/TicTacToe/TicTacToeApp.includeRequire.runModule.bundle"];
// OPTION 2
// Load from pre-bundled file on disk. To re-generate the static bundle, run
diff --git a/Examples/UIExplorer/AppStateExample.js b/Examples/UIExplorer/AppStateExample.js
new file mode 100644
index 000000000..ea6400b91
--- /dev/null
+++ b/Examples/UIExplorer/AppStateExample.js
@@ -0,0 +1,60 @@
+/**
+ * Copyright 2004-present Facebook. All Rights Reserved.
+ */
+'use strict';
+
+var React = require('react-native');
+var {
+ AppState,
+ StyleSheet,
+ Text,
+ TouchableHighlight,
+ View,
+} = React;
+
+var Button = React.createClass({
+ render: function() {
+ return (
+
+
+ {this.props.label}
+
+
+ );
+ }
+});
+
+var styles = StyleSheet.create({
+ button: {
+ padding: 10,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ buttonLabel: {
+ color: 'blue',
+ },
+});
+
+exports.title = 'AppState';
+exports.description = 'App background status and badge value';
+exports.examples = [
+{
+ title: 'Set Badge Number',
+ render: function() {
+ return (
+
+
+ );
+ },
+}];
diff --git a/Examples/UIExplorer/AppStateIOSExample.js b/Examples/UIExplorer/AppStateIOSExample.js
new file mode 100644
index 000000000..845b2e049
--- /dev/null
+++ b/Examples/UIExplorer/AppStateIOSExample.js
@@ -0,0 +1,69 @@
+/**
+ * Copyright 2004-present Facebook. All Rights Reserved.
+ *
+ * @providesModule AppStateIOSExample
+ */
+'use strict';
+
+var React = require('react-native');
+var {
+ AppStateIOS,
+ Text,
+ View
+} = React;
+
+var AppStateSubscription = React.createClass({
+ getInitialState() {
+ return {
+ appState: AppStateIOS.currentState,
+ previousAppStates: [],
+ };
+ },
+ componentDidMount: function() {
+ AppStateIOS.addEventListener('change', this._handleAppStateChange);
+ },
+ componentWillUnmount: function() {
+ AppStateIOS.removeEventListener('change', this._handleAppStateChange);
+ },
+ _handleAppStateChange: function(appState) {
+ var previousAppStates = this.state.previousAppStates.slice();
+ previousAppStates.push(this.state.appState);
+ this.setState({
+ appState,
+ previousAppStates,
+ });
+ },
+ render() {
+ if (this.props.showCurrentOnly) {
+ return (
+
+ {this.state.appState}
+
+ );
+ }
+ return (
+
+ {JSON.stringify(this.state.previousAppStates)}
+
+ );
+ }
+});
+
+exports.title = 'AppStateIOS';
+exports.description = 'iOS app background status';
+exports.examples = [
+ {
+ title: 'AppStateIOS.currentState',
+ description: 'Can be null on app initialization',
+ render() { return {AppStateIOS.currentState}; }
+ },
+ {
+ title: 'Subscribed AppStateIOS:',
+ description: 'This changes according to the current state, so you can only ever see it rendered as "active"',
+ render() { return ; }
+ },
+ {
+ title: 'Previous states:',
+ render() { return ; }
+ },
+];
diff --git a/Examples/UIExplorer/AsyncStorageExample.js b/Examples/UIExplorer/AsyncStorageExample.js
new file mode 100644
index 000000000..3a2579fa7
--- /dev/null
+++ b/Examples/UIExplorer/AsyncStorageExample.js
@@ -0,0 +1,103 @@
+/**
+ * Copyright 2004-present Facebook. All Rights Reserved.
+ */
+'use strict';
+
+var React = require('react-native');
+var {
+ AsyncStorage,
+ PickerIOS,
+ Text,
+ View
+} = React;
+var PickerItemIOS = PickerIOS.Item;
+
+var STORAGE_KEY = '@AsyncStorageExample:key';
+var COLORS = ['red', 'orange', 'yellow', 'green', 'blue'];
+
+var BasicStorageExample = React.createClass({
+ componentDidMount() {
+ AsyncStorage.getItem(STORAGE_KEY, (error, value) => {
+ if (error) {
+ this._appendMessage('AsyncStorage error: ' + error.message);
+ } else if (value !== null) {
+ this.setState({selectedValue: value});
+ this._appendMessage('Recovered selection from disk: ' + value);
+ } else {
+ this._appendMessage('Initialized with no selection on disk.');
+ }
+ });
+ },
+ getInitialState() {
+ return {
+ selectedValue: COLORS[0],
+ messages: [],
+ };
+ },
+
+ render() {
+ var color = this.state.selectedValue;
+ return (
+
+
+ {COLORS.map((value) => (
+
+ ))}
+
+
+ {'Selected: '}
+
+ {this.state.selectedValue}
+
+
+ {' '}
+
+ Press here to remove from storage.
+
+ {' '}
+ Messages:
+ {this.state.messages.map((m) => {m})}
+
+ );
+ },
+
+ _onValueChange(selectedValue) {
+ this.setState({selectedValue});
+ AsyncStorage.setItem(STORAGE_KEY, selectedValue, (error) => {
+ if (error) {
+ this._appendMessage('AsyncStorage error: ' + error.message);
+ } else {
+ this._appendMessage('Saved selection to disk: ' + selectedValue);
+ }
+ });
+ },
+
+ _removeStorage() {
+ AsyncStorage.removeItem(STORAGE_KEY, (error) => {
+ if (error) {
+ this._appendMessage('AsyncStorage error: ' + error.message);
+ } else {
+ this._appendMessage('Selection removed from disk.');
+ }
+ });
+ },
+
+ _appendMessage(message) {
+ this.setState({messages: this.state.messages.concat(message)});
+ },
+});
+
+exports.title = 'AsyncStorage';
+exports.description = 'Asynchronous local disk storage.';
+exports.examples = [
+ {
+ title: 'Basics - getItem, setItem, removeItem',
+ render() { return ; }
+ },
+];
diff --git a/Examples/UIExplorer/UIExplorerApp.js b/Examples/UIExplorer/UIExplorerApp.js
index 7c73d4ee3..95d02347e 100644
--- a/Examples/UIExplorer/UIExplorerApp.js
+++ b/Examples/UIExplorer/UIExplorerApp.js
@@ -16,6 +16,7 @@ var {
var UIExplorerApp = React.createClass({
+
render: function() {
return (
+ tintColor='#008888'
+ />
);
}
});
diff --git a/Examples/UIExplorer/UIExplorerList.js b/Examples/UIExplorer/UIExplorerList.js
index 63c983838..ab8aed798 100644
--- a/Examples/UIExplorer/UIExplorerList.js
+++ b/Examples/UIExplorer/UIExplorerList.js
@@ -37,9 +37,12 @@ var EXAMPLES = [
require('./TabBarExample'),
require('./SwitchExample'),
require('./SliderExample'),
+ require('./AsyncStorageExample'),
require('./CameraRollExample.ios'),
require('./MapViewExample'),
+ require('./AppStateIOSExample'),
require('./AdSupportIOSExample'),
+ require('./AppStateExample'),
];
var UIExplorerList = React.createClass({
diff --git a/Libraries/AppState/AppState.js b/Libraries/AppState/AppState.js
new file mode 100644
index 000000000..43b9db1a5
--- /dev/null
+++ b/Libraries/AppState/AppState.js
@@ -0,0 +1,46 @@
+/**
+ * Copyright 2004-present Facebook. All Rights Reserved.
+ *
+ * @providesModule AppState
+ */
+'use strict';
+
+var NativeModules = require('NativeModulesDeprecated');
+var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
+var RKAppState = NativeModules.RKAppState;
+var RKReachability = NativeModules.RKReachability;
+var Subscribable = require('Subscribable');
+
+var keyMirror = require('keyMirror');
+
+var AppState = {
+
+ setApplicationIconBadgeNumber: function(number) {
+ RKAppState.setApplicationIconBadgeNumber(number);
+ },
+
+ getApplicationIconBadgeNumber: function(callback) {
+ RKAppState.getApplicationIconBadgeNumber(callback);
+ },
+
+};
+
+// This check avoids redboxing if native RKReachability library isn't included in app
+// TODO: Move reachability API into separate JS module to prevent need for this
+if (RKReachability) {
+ AppState.networkReachability = new Subscribable(
+ RCTDeviceEventEmitter,
+ 'reachabilityDidChange',
+ (resp) => resp.network_reachability,
+ RKReachability.getCurrentReachability
+ );
+}
+
+AppState.NetworkReachability = keyMirror({
+ wifi: true,
+ cell: true,
+ none: true,
+ unknown: true,
+});
+
+module.exports = AppState;
diff --git a/Libraries/AppStateIOS/AppStateIOS.android.js b/Libraries/AppStateIOS/AppStateIOS.android.js
new file mode 100644
index 000000000..4bc7e691c
--- /dev/null
+++ b/Libraries/AppStateIOS/AppStateIOS.android.js
@@ -0,0 +1,24 @@
+/**
+ * Copyright 2004-present Facebook. All Rights Reserved.
+ *
+ * @providesModule AppStateIOS
+ */
+'use strict';
+
+var warning = require('warning');
+
+class AppStateIOS {
+
+ static addEventListener(type, handler) {
+ warning('Cannot listen to AppStateIOS events on Android.');
+ }
+
+ static removeEventListener(type, handler) {
+ warning('Cannot remove AppStateIOS listener on Android.');
+ }
+
+}
+
+AppStateIOS.currentState = null;
+
+module.exports = AppStateIOS;
diff --git a/Libraries/AppStateIOS/AppStateIOS.ios.js b/Libraries/AppStateIOS/AppStateIOS.ios.js
new file mode 100644
index 000000000..8f03654b5
--- /dev/null
+++ b/Libraries/AppStateIOS/AppStateIOS.ios.js
@@ -0,0 +1,55 @@
+/**
+ * Copyright 2004-present Facebook. All Rights Reserved.
+ *
+ * @providesModule AppStateIOS
+ */
+'use strict';
+
+var NativeModules = require('NativeModules');
+var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
+var RKAppState = NativeModules.RKAppState;
+
+var logError = require('logError');
+
+var DEVICE_APPSTATE_EVENT = 'appStateDidChange';
+
+var _appStateHandlers = {};
+
+class AppStateIOS {
+
+ static addEventListener(type, handler) {
+ _appStateHandlers[handler] = RCTDeviceEventEmitter.addListener(
+ DEVICE_APPSTATE_EVENT,
+ (appStateData) => {
+ handler(appStateData.app_state);
+ }
+ );
+ }
+
+ static removeEventListener(type, handler) {
+ if (!_appStateHandlers[handler]) {
+ return;
+ }
+ _appStateHandlers[handler].remove();
+ _appStateHandlers[handler] = null;
+ }
+
+}
+
+AppStateIOS.currentState = null;
+
+RCTDeviceEventEmitter.addListener(
+ DEVICE_APPSTATE_EVENT,
+ (appStateData) => {
+ AppStateIOS.currentState = appStateData.app_state;
+ }
+);
+
+RKAppState.getCurrentAppState(
+ (appStateData) => {
+ AppStateIOS.currentState = appStateData.app_state;
+ },
+ logError
+);
+
+module.exports = AppStateIOS;
diff --git a/Libraries/Components/ListView/ListView.js b/Libraries/Components/ListView/ListView.js
index 1eb7eef5d..d4a7593c8 100644
--- a/Libraries/Components/ListView/ListView.js
+++ b/Libraries/Components/ListView/ListView.js
@@ -401,8 +401,8 @@ var ListView = React.createClass({
var header = this.props.renderHeader && this.props.renderHeader();
var totalIndex = header ? 1 : 0;
var visibilityChanged = false;
- var changedRows = {};
- for (var sectionIdx = 0; sectionIdx < allRowIDs.length; sectionIdx++) {
+ var changedRows = {};
+ for (var sectionIdx = 0; sectionIdx < allRowIDs.length; sectionIdx++) {
var rowIDs = allRowIDs[sectionIdx];
if (rowIDs.length === 0) {
continue;
diff --git a/Libraries/Components/ScrollView/ScrollView.js b/Libraries/Components/ScrollView/ScrollView.js
index 09f7b94d6..157cc8e1f 100644
--- a/Libraries/Components/ScrollView/ScrollView.js
+++ b/Libraries/Components/ScrollView/ScrollView.js
@@ -310,8 +310,8 @@ var validAttributes = {
alwaysBounceVertical: true,
automaticallyAdjustContentInsets: true,
centerContent: true,
- contentInset: insetsDiffer,
- contentOffset: pointsDiffer,
+ contentInset: {diff: insetsDiffer},
+ contentOffset: {diff: pointsDiffer},
decelerationRate: true,
horizontal: true,
keyboardDismissMode: true,
@@ -321,11 +321,11 @@ var validAttributes = {
pagingEnabled: true,
removeClippedSubviews: true,
scrollEnabled: true,
- scrollIndicatorInsets: insetsDiffer,
+ scrollIndicatorInsets: {diff: insetsDiffer},
scrollsToTop: true,
showsHorizontalScrollIndicator: true,
showsVerticalScrollIndicator: true,
- stickyHeaderIndices: deepDiffer,
+ stickyHeaderIndices: {diff: deepDiffer},
throttleScrollCallbackMS: true,
zoomScale: true,
};
diff --git a/Libraries/Components/TextInput/TextInput.ios.js b/Libraries/Components/TextInput/TextInput.ios.js
index 20eda688b..e04ed88ee 100644
--- a/Libraries/Components/TextInput/TextInput.ios.js
+++ b/Libraries/Components/TextInput/TextInput.ios.js
@@ -14,7 +14,6 @@ var React = require('React');
var ReactChildren = require('ReactChildren');
var ReactIOSViewAttributes = require('ReactIOSViewAttributes');
var StyleSheet = require('StyleSheet');
-var Subscribable = require('Subscribable');
var Text = require('Text');
var TextInputState = require('TextInputState');
var TimerMixin = require('TimerMixin');
@@ -195,7 +194,7 @@ var TextInput = React.createClass({
* `NativeMethodsMixin` will look for this when invoking `setNativeProps`. We
* make `this` look like an actual native component class.
*/
- mixins: [NativeMethodsMixin, TimerMixin, Subscribable.Mixin],
+ mixins: [NativeMethodsMixin, TimerMixin],
viewConfig: {
uiViewClassName: 'RCTTextField',
@@ -232,18 +231,25 @@ var TextInput = React.createClass({
}
return;
}
- this.addListenerOn(this.context.focusEmitter, 'focus', (el) => {
- if (this === el) {
- this.requestAnimationFrame(this.focus);
- } else if (this.isFocused()) {
- this.blur();
+ this._focusSubscription = this.context.focusEmitter.addListener(
+ 'focus',
+ (el) => {
+ if (this === el) {
+ this.requestAnimationFrame(this.focus);
+ } else if (this.isFocused()) {
+ this.blur();
+ }
}
- });
+ );
if (this.props.autoFocus) {
this.context.onFocusRequested(this);
}
},
+ componentWillUnmount: function() {
+ this._focusSubscription && this._focusSubscription.remove();
+ },
+
componentWillReceiveProps: function(newProps) {
if (newProps.value !== this.props.value) {
if (!this.isFocused()) {
diff --git a/Libraries/Image/Image.ios.js b/Libraries/Image/Image.ios.js
index f8ab583a9..f23f2c71b 100644
--- a/Libraries/Image/Image.ios.js
+++ b/Libraries/Image/Image.ios.js
@@ -57,7 +57,7 @@ var Image = React.createClass({
* resource (which should be wrapped in the `ix` function).
*/
uri: PropTypes.string,
- }).isRequired,
+ }),
/**
* accessible - Whether this element should be revealed as an accessible
* element.
diff --git a/Libraries/Network/RCTNetwork.xcodeproj/project.pbxproj b/Libraries/Network/RCTNetwork.xcodeproj/project.pbxproj
index bfaa0413d..252d73cda 100644
--- a/Libraries/Network/RCTNetwork.xcodeproj/project.pbxproj
+++ b/Libraries/Network/RCTNetwork.xcodeproj/project.pbxproj
@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
+ 1372B7371AB03E7B00659ED6 /* RCTReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 1372B7361AB03E7B00659ED6 /* RCTReachability.m */; };
58B512081A9E6CE300147676 /* RCTDataManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B512071A9E6CE300147676 /* RCTDataManager.m */; };
/* End PBXBuildFile section */
@@ -23,6 +24,8 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ 1372B7351AB03E7B00659ED6 /* RCTReachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTReachability.h; sourceTree = ""; };
+ 1372B7361AB03E7B00659ED6 /* RCTReachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTReachability.m; sourceTree = ""; };
58B511DB1A9E6C8500147676 /* libRCTNetwork.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTNetwork.a; sourceTree = BUILT_PRODUCTS_DIR; };
58B512061A9E6CE300147676 /* RCTDataManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDataManager.h; sourceTree = ""; };
58B512071A9E6CE300147676 /* RCTDataManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDataManager.m; sourceTree = ""; };
@@ -44,6 +47,8 @@
children = (
58B512061A9E6CE300147676 /* RCTDataManager.h */,
58B512071A9E6CE300147676 /* RCTDataManager.m */,
+ 1372B7351AB03E7B00659ED6 /* RCTReachability.h */,
+ 1372B7361AB03E7B00659ED6 /* RCTReachability.m */,
58B511DC1A9E6C8500147676 /* Products */,
);
sourceTree = "";
@@ -112,6 +117,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 1372B7371AB03E7B00659ED6 /* RCTReachability.m in Sources */,
58B512081A9E6CE300147676 /* RCTDataManager.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
diff --git a/Libraries/Network/RCTReachability.h b/Libraries/Network/RCTReachability.h
new file mode 100644
index 000000000..c0bca96c7
--- /dev/null
+++ b/Libraries/Network/RCTReachability.h
@@ -0,0 +1,11 @@
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+#import
+
+#import "RCTBridgeModule.h"
+
+@interface RCTReachability : NSObject
+
+- (instancetype)initWithHost:(NSString *)host NS_DESIGNATED_INITIALIZER;
+
+@end
diff --git a/Libraries/Network/RCTReachability.m b/Libraries/Network/RCTReachability.m
new file mode 100644
index 000000000..921d339bb
--- /dev/null
+++ b/Libraries/Network/RCTReachability.m
@@ -0,0 +1,85 @@
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+#import "RCTReachability.h"
+
+#import "RCTBridge.h"
+#import "RCTEventDispatcher.h"
+
+static NSString *const RCTReachabilityStateUnknown = @"unknown";
+static NSString *const RCTReachabilityStateNone = @"none";
+static NSString *const RCTReachabilityStateWifi = @"wifi";
+static NSString *const RCTReachabilityStateCell = @"cell";
+
+@implementation RCTReachability
+{
+ SCNetworkReachabilityRef _reachability;
+ NSString *_status;
+}
+
+@synthesize bridge = _bridge;
+
+static void RCTReachabilityCallback(__unused SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info)
+{
+ RCTReachability *self = (__bridge id)info;
+ NSString *status = RCTReachabilityStateUnknown;
+ if ((flags & kSCNetworkReachabilityFlagsReachable) == 0 ||
+ (flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0) {
+ status = RCTReachabilityStateNone;
+ }
+
+#if TARGET_OS_IPHONE
+
+ else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
+ status = RCTReachabilityStateCell;
+ }
+
+#endif
+
+ else {
+ status = RCTReachabilityStateWifi;
+ }
+
+ if (![status isEqualToString:self->_status]) {
+ self->_status = status;
+ [self->_bridge.eventDispatcher sendDeviceEventWithName:@"reachabilityDidChange"
+ body:@{@"network_reachability": status}];
+ }
+}
+
+#pragma mark - Lifecycle
+
+- (instancetype)initWithHost:(NSString *)host
+{
+ if ((self = [super init])) {
+ _status = RCTReachabilityStateUnknown;
+ _reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [host UTF8String]);
+ SCNetworkReachabilityContext context = { 0, ( __bridge void *)self, NULL, NULL, NULL };
+ SCNetworkReachabilitySetCallback(_reachability, RCTReachabilityCallback, &context);
+ SCNetworkReachabilityScheduleWithRunLoop(_reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
+ }
+ return self;
+}
+
+- (instancetype)init
+{
+ return [self initWithHost:@"http://apple.com"];
+}
+
+- (void)dealloc
+{
+ SCNetworkReachabilityUnscheduleFromRunLoop(_reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
+ CFRelease(_reachability);
+}
+
+#pragma mark - Public API
+
+// TODO: remove error callback - not needed except by Subscribable interface
+- (void)getCurrentReachability:(RCTResponseSenderBlock)getSuccess
+ withErrorCallback:(__unused RCTResponseSenderBlock)getError
+{
+ RCT_EXPORT();
+
+ getSuccess(@[@{@"network_reachability": _status}]);
+}
+
+@end
diff --git a/Libraries/ReactIOS/renderApplication.js b/Libraries/ReactIOS/renderApplication.js
index 05d694c09..64e26126c 100644
--- a/Libraries/ReactIOS/renderApplication.js
+++ b/Libraries/ReactIOS/renderApplication.js
@@ -5,6 +5,7 @@
*/
'use strict';
+var PushNotificationIOS = require('PushNotificationIOS');
var React = require('React');
var invariant = require('invariant');
@@ -14,7 +15,16 @@ function renderApplication(RootComponent, initialProps, rootTag) {
rootTag,
'Expect to have a valid rootTag, instead got ', rootTag
);
- React.render(, rootTag);
+ var pushNotification = initialProps.launchOptions &&
+ initialProps.launchOptions.remoteNotification &&
+ new PushNotificationIOS(initialProps.launchOptions.remoteNotification);
+ React.render(
+ ,
+ rootTag
+ );
}
module.exports = renderApplication;
diff --git a/Libraries/Storage/AsyncStorage.ios.js b/Libraries/Storage/AsyncStorage.ios.js
new file mode 100644
index 000000000..a29288f36
--- /dev/null
+++ b/Libraries/Storage/AsyncStorage.ios.js
@@ -0,0 +1,193 @@
+/**
+ * Copyright 2004-present Facebook. All Rights Reserved.
+ *
+ * @providesModule AsyncStorage
+ * @flow-weak
+ */
+'use strict';
+
+var NativeModules = require('NativeModulesDeprecated');
+var RKAsyncLocalStorage = NativeModules.RKAsyncLocalStorage;
+var RKAsyncRocksDBStorage = NativeModules.RKAsyncRocksDBStorage;
+
+// We use RocksDB if available.
+var RKAsyncStorage = RKAsyncRocksDBStorage || RKAsyncLocalStorage;
+
+/**
+ * AsyncStorage is a simple, asynchronous, persistent, global, key-value storage
+ * system. It should be used instead of LocalStorage.
+ *
+ * It is recommended that you use an abstraction on top of AsyncStorage instead
+ * of AsyncStorage directly for anything more than light usage since it
+ * operates globally.
+ *
+ * This JS code is a simple facad over the native iOS implementation to provide
+ * a clear JS API, real Error objects, and simple non-multi functions.
+ */
+var AsyncStorage = {
+ /**
+ * Fetches `key` and passes the result to `callback`, along with an `Error` if
+ * there is any.
+ */
+ getItem: function(
+ key: string,
+ callback: (error: ?Error, result: ?string) => void
+ ): void {
+ RKAsyncStorage.multiGet([key], function(errors, result) {
+ // Unpack result to get value from [[key,value]]
+ var value = (result && result[0] && result[0][1]) ? result[0][1] : null;
+ callback((errors && convertError(errors[0])) || null, value);
+ });
+ },
+
+ /**
+ * Sets `value` for `key` and calls `callback` on completion, along with an
+ * `Error` if there is any.
+ */
+ setItem: function(
+ key: string,
+ value: string,
+ callback: ?(error: ?Error) => void
+ ): void {
+ RKAsyncStorage.multiSet([[key,value]], function(errors) {
+ callback && callback((errors && convertError(errors[0])) || null);
+ });
+ },
+
+ removeItem: function(
+ key: string,
+ callback: ?(error: ?Error) => void
+ ): void {
+ RKAsyncStorage.multiRemove([key], function(errors) {
+ callback && callback((errors && convertError(errors[0])) || null);
+ });
+ },
+
+ /**
+ * Merges existing value with input value, assuming they are stringified json.
+ *
+ * Not supported by all native implementations.
+ */
+ mergeItem: function(
+ key: string,
+ value: string,
+ callback: ?(error: ?Error) => void
+ ): void {
+ RKAsyncStorage.multiMerge([[key,value]], function(errors) {
+ callback && callback((errors && convertError(errors[0])) || null);
+ });
+ },
+
+ /**
+ * Erases *all* AsyncStorage for all clients, libraries, etc. You probably
+ * don't want to call this - use removeItem or multiRemove to clear only your
+ * own keys instead.
+ */
+ clear: function(callback: ?(error: ?Error) => void) {
+ RKAsyncStorage.clear(function(error) {
+ callback && callback(convertError(error));
+ });
+ },
+
+ /**
+ * Gets *all* keys known to the system, for all callers, libraries, etc.
+ */
+ getAllKeys: function(callback: (error: ?Error) => void) {
+ RKAsyncStorage.getAllKeys(function(error, keys) {
+ callback(convertError(error), keys);
+ });
+ },
+
+ /**
+ * The following batched functions are useful for executing a lot of
+ * operations at once, allowing for native optimizations and provide the
+ * convenience of a single callback after all operations are complete.
+ *
+ * These functions return arrays of errors, potentially one for every key.
+ * For key-specific errors, the Error object will have a key property to
+ * indicate which key caused the error.
+ */
+
+ /**
+ * multiGet invokes callback with an array of key-value pair arrays that
+ * matches the input format of multiSet.
+ *
+ * multiGet(['k1', 'k2'], cb) -> cb([['k1', 'val1'], ['k2', 'val2']])
+ */
+ multiGet: function(
+ keys: Array,
+ callback: (errors: ?Array, result: ?Array>) => void
+ ): void {
+ RKAsyncStorage.multiGet(keys, function(errors, result) {
+ callback(
+ (errors && errors.map((error) => convertError(error))) || null,
+ result
+ );
+ });
+ },
+
+ /**
+ * multiSet and multiMerge take arrays of key-value array pairs that match
+ * the output of multiGet, e.g.
+ *
+ * multiSet([['k1', 'val1'], ['k2', 'val2']], cb);
+ */
+ multiSet: function(
+ keyValuePairs: Array>,
+ callback: ?(errors: ?Array) => void
+ ): void {
+ RKAsyncStorage.multiSet(keyValuePairs, function(errors) {
+ callback && callback(
+ (errors && errors.map((error) => convertError(error))) || null
+ );
+ });
+ },
+
+ /**
+ * Delete all the keys in the `keys` array.
+ */
+ multiRemove: function(
+ keys: Array,
+ callback: ?(errors: ?Array) => void
+ ): void {
+ RKAsyncStorage.multiRemove(keys, function(errors) {
+ callback && callback(
+ (errors && errors.map((error) => convertError(error))) || null
+ );
+ });
+ },
+
+ /**
+ * Merges existing values with input values, assuming they are stringified
+ * json.
+ *
+ * Not supported by all native implementations.
+ */
+ multiMerge: function(
+ keyValuePairs: Array>,
+ callback: ?(errors: ?Array) => void
+ ): void {
+ RKAsyncStorage.multiMerge(keyValuePairs, function(errors) {
+ callback && callback(
+ (errors && errors.map((error) => convertError(error))) || null
+ );
+ });
+ },
+};
+
+// Not all native implementations support merge.
+if (!RKAsyncStorage.multiMerge) {
+ delete AsyncStorage.mergeItem;
+ delete AsyncStorage.multiMerge;
+}
+
+function convertError(error) {
+ if (!error) {
+ return null;
+ }
+ var out = new Error(error.message);
+ out.key = error.key; // flow doesn't like this :(
+ return out;
+}
+
+module.exports = AsyncStorage;
diff --git a/Libraries/Utilities/PushNotificationIOS.js b/Libraries/Utilities/PushNotificationIOS.js
new file mode 100644
index 000000000..0cd8a6db6
--- /dev/null
+++ b/Libraries/Utilities/PushNotificationIOS.js
@@ -0,0 +1,74 @@
+/**
+ * Copyright 2004-present Facebook. All Rights Reserved.
+ *
+ * @providesModule PushNotificationIOS
+ */
+'use strict';
+
+var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
+
+var _notifHandlers = {};
+
+var DEVICE_NOTIF_EVENT = 'remoteNotificationReceived';
+
+class PushNotificationIOS {
+
+ static addEventListener(type, handler) {
+ _notifHandlers[handler] = RCTDeviceEventEmitter.addListener(
+ DEVICE_NOTIF_EVENT,
+ (notifData) => {
+ handler(new PushNotificationIOS(notifData));
+ }
+ );
+ }
+
+ static removeEventListener(type, handler) {
+ if (!_notifHandlers[handler]) {
+ return;
+ }
+ _notifHandlers[handler].remove();
+ _notifHandlers[handler] = null;
+ }
+
+ constructor(nativeNotif) {
+ this._data = {};
+
+ // Extract data from Apple's `aps` dict as defined:
+
+ // https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/ApplePushService.html
+
+ Object.keys(nativeNotif).forEach((notifKey) => {
+ var notifVal = nativeNotif[notifKey];
+ if (notifKey === 'aps') {
+ this._alert = notifVal.alert;
+ this._sound = notifVal.sound;
+ this._badgeCount = notifVal.badge;
+ } else {
+ this._data[notifKey] = notifVal;
+ }
+ });
+ }
+
+ getMessage() {
+ // alias because "alert" is an ambiguous name
+ return this._alert;
+ }
+
+ getSound() {
+ return this._sound;
+ }
+
+ getAlert() {
+ return this._alert;
+ }
+
+ getBadgeCount() {
+ return this._badgeCount;
+ }
+
+ getData() {
+ return this._data;
+ }
+}
+
+module.exports = PushNotificationIOS;
diff --git a/Libraries/react-native/react-native.js b/Libraries/react-native/react-native.js
index 4d4eee73d..48217795f 100644
--- a/Libraries/react-native/react-native.js
+++ b/Libraries/react-native/react-native.js
@@ -8,20 +8,23 @@
var ReactNative = {
...require('React'),
Animation: require('Animation'),
+ ActivityIndicatorIOS: require('ActivityIndicatorIOS'),
AppRegistry: require('AppRegistry'),
+ AppState: require('AppState'),
+ AppStateIOS: require('AppStateIOS'),
+ AsyncStorage: require('AsyncStorage'),
CameraRoll: require('CameraRoll'),
DatePickerIOS: require('DatePickerIOS'),
ExpandingText: require('ExpandingText'),
- MapView: require('MapView'),
Image: require('Image'),
LayoutAnimation: require('LayoutAnimation'),
ListView: require('ListView'),
ListViewDataSource: require('ListViewDataSource'),
+ MapView: require('MapView'),
NavigatorIOS: require('NavigatorIOS'),
PickerIOS: require('PickerIOS'),
PixelRatio: require('PixelRatio'),
ScrollView: require('ScrollView'),
- ActivityIndicatorIOS: require('ActivityIndicatorIOS'),
Slider: require('Slider'),
StatusBarIOS: require('StatusBarIOS'),
StyleSheet: require('StyleSheet'),
diff --git a/ReactKit/Base/RCTRedBox.m b/ReactKit/Base/RCTRedBox.m
index c192598ca..93a73087e 100644
--- a/ReactKit/Base/RCTRedBox.m
+++ b/ReactKit/Base/RCTRedBox.m
@@ -104,6 +104,8 @@
- (void)dismiss
{
self.hidden = YES;
+ [self resignFirstResponder];
+ [[[[UIApplication sharedApplication] delegate] window] makeKeyWindow];
}
- (void)reload
diff --git a/ReactKit/Base/RCTUtils.h b/ReactKit/Base/RCTUtils.h
index de203e4ae..adf35cb9b 100644
--- a/ReactKit/Base/RCTUtils.h
+++ b/ReactKit/Base/RCTUtils.h
@@ -41,6 +41,11 @@ 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);
+NSDictionary *RCTMakeAndLogError(NSString *message, id toStringify, NSDictionary *extraData);
+
#ifdef __cplusplus
}
#endif
diff --git a/ReactKit/Base/RCTUtils.m b/ReactKit/Base/RCTUtils.m
index 40007a69b..217368e17 100644
--- a/ReactKit/Base/RCTUtils.m
+++ b/ReactKit/Base/RCTUtils.m
@@ -194,3 +194,22 @@ void RCTEnumerateClasses(void (^block)(Class cls))
}
}
}
+
+NSDictionary *RCTMakeError(NSString *message, id toStringify, NSDictionary *extraData)
+{
+ if (toStringify) {
+ message = [NSString stringWithFormat:@"%@%@", message, toStringify];
+ }
+ NSMutableDictionary *error = [@{@"message": message} mutableCopy];
+ if (extraData) {
+ [error addEntriesFromDictionary:extraData];
+ }
+ return error;
+}
+
+NSDictionary *RCTMakeAndLogError(NSString *message, id toStringify, NSDictionary *extraData)
+{
+ id error = RCTMakeError(message, toStringify, extraData);
+ RCTLogError(@"\nError: %@", error);
+ return error;
+}
diff --git a/ReactKit/Modules/RCTAppState.h b/ReactKit/Modules/RCTAppState.h
new file mode 100644
index 000000000..027379492
--- /dev/null
+++ b/ReactKit/Modules/RCTAppState.h
@@ -0,0 +1,7 @@
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+#import "RCTBridgeModule.h"
+
+@interface RCTAppState : NSObject
+
+@end
diff --git a/ReactKit/Modules/RCTAppState.m b/ReactKit/Modules/RCTAppState.m
new file mode 100644
index 000000000..c4a145c1e
--- /dev/null
+++ b/ReactKit/Modules/RCTAppState.m
@@ -0,0 +1,105 @@
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+#import "RCTAppState.h"
+
+#import "RCTAssert.h"
+#import "RCTBridge.h"
+#import "RCTEventDispatcher.h"
+
+static NSString *RCTCurrentAppBackgroundState()
+{
+ static NSDictionary *states;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ states = @{
+ @(UIApplicationStateActive): @"active",
+ @(UIApplicationStateBackground): @"background",
+ @(UIApplicationStateInactive): @"inactive"
+ };
+ });
+
+ return states[@([[UIApplication sharedApplication] applicationState])] ?: @"unknown";
+}
+
+@implementation RCTAppState
+{
+ NSString *_lastKnownState;
+}
+
+@synthesize bridge = _bridge;
+
+#pragma mark - Lifecycle
+
+- (instancetype)init
+{
+ if ((self = [super init])) {
+
+ _lastKnownState = RCTCurrentAppBackgroundState();
+
+ for (NSString *name in @[UIApplicationDidBecomeActiveNotification,
+ UIApplicationDidEnterBackgroundNotification,
+ UIApplicationDidFinishLaunchingNotification]) {
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(handleAppStateDidChange)
+ name:name
+ object:nil];
+ }
+ }
+ return self;
+}
+
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+#pragma mark - App Notification Methods
+
+- (void)handleAppStateDidChange
+{
+ NSString *newState = RCTCurrentAppBackgroundState();
+ if (![newState isEqualToString:_lastKnownState]) {
+ _lastKnownState = newState;
+ [_bridge.eventDispatcher sendDeviceEventWithName:@"appStateDidChange"
+ body:@{@"app_state": _lastKnownState}];
+ }
+}
+
+#pragma mark - Public API
+
+/**
+ * Get the current background/foreground state of the app
+ */
+- (void)getCurrentAppState:(RCTResponseSenderBlock)callback
+ error:(__unused RCTResponseSenderBlock)error
+{
+ RCT_EXPORT();
+
+ callback(@[@{@"app_state": _lastKnownState}]);
+}
+
+/**
+ * Update the application icon badge number on the home screen
+ */
+- (void)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();
+
+ callback(@[
+ @([UIApplication sharedApplication].applicationIconBadgeNumber)
+ ]);
+}
+
+@end
diff --git a/ReactKit/Modules/RCTAsyncLocalStorage.h b/ReactKit/Modules/RCTAsyncLocalStorage.h
new file mode 100644
index 000000000..54a320749
--- /dev/null
+++ b/ReactKit/Modules/RCTAsyncLocalStorage.h
@@ -0,0 +1,24 @@
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+#import "RCTBridgeModule.h"
+
+/**
+ * A simple, asynchronous, persistent, key-value storage system designed as a
+ * backend to the AsyncStorage JS module, which is modeled after LocalStorage.
+ *
+ * Current implementation stores small values in serialized dictionary and
+ * larger values in separate files. Since we use a serial file queue
+ * `RKFileQueue`, reading/writing from multiple threads should be perceived as
+ * being atomic, unless someone bypasses the `RCTAsyncLocalStorage` API.
+ *
+ * Keys and values must always be strings or an error is returned.
+ */
+@interface RCTAsyncLocalStorage : NSObject
+
+- (void)multiGet:(NSArray *)keys callback:(RCTResponseSenderBlock)callback;
+- (void)multiSet:(NSArray *)kvPairs callback:(RCTResponseSenderBlock)callback;
+- (void)multiRemove:(NSArray *)keys callback:(RCTResponseSenderBlock)callback;
+- (void)clear:(RCTResponseSenderBlock)callback;
+- (void)getAllKeys:(RCTResponseSenderBlock)callback;
+
+@end
diff --git a/ReactKit/Modules/RCTAsyncLocalStorage.m b/ReactKit/Modules/RCTAsyncLocalStorage.m
new file mode 100644
index 000000000..de8b7989e
--- /dev/null
+++ b/ReactKit/Modules/RCTAsyncLocalStorage.m
@@ -0,0 +1,292 @@
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+#import "RCTAsyncLocalStorage.h"
+
+#import
+
+#import
+#import
+
+#import "RCTLog.h"
+#import "RCTUtils.h"
+
+static NSString *const kStorageDir = @"RCTAsyncLocalStorage_V1";
+static NSString *const kManifestFilename = @"manifest.json";
+static const NSUInteger kInlineValueThreshold = 100;
+
+#pragma mark - Static helper functions
+
+static id RCTErrorForKey(NSString *key)
+{
+ if (![key isKindOfClass:[NSString class]]) {
+ return RCTMakeAndLogError(@"Invalid key - must be a string. Key: ", key, @{@"key": key});
+ } else if (key.length < 1) {
+ return RCTMakeAndLogError(@"Invalid key - must be at least one character. Key: ", key, @{@"key": key});
+ } else {
+ return nil;
+ }
+}
+
+static void RCTAppendError(id error, NSMutableArray **errors)
+{
+ if (error && errors) {
+ if (!*errors) {
+ *errors = [NSMutableArray new];
+ }
+ [*errors addObject:error];
+ }
+}
+
+static id RCTReadFile(NSString *filePath, NSString *key, NSDictionary **errorOut)
+{
+ if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
+ NSError *error;
+ NSStringEncoding encoding;
+ NSString *entryString = [NSString stringWithContentsOfFile:filePath usedEncoding:&encoding error:&error];
+ if (error) {
+ *errorOut = RCTMakeError(@"Failed to read storage file.", error, @{@"key": key});
+ } else if (encoding != NSUTF8StringEncoding) {
+ *errorOut = RCTMakeError(@"Incorrect encoding of storage file: ", @(encoding), @{@"key": key});
+ } else {
+ return entryString;
+ }
+ }
+ return nil;
+}
+
+static dispatch_queue_t RCTFileQueue(void)
+{
+ static dispatch_queue_t fileQueue = NULL;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ // All JS is single threaded, so a serial queue is our only option.
+ fileQueue = dispatch_queue_create("com.facebook.rkFile", DISPATCH_QUEUE_SERIAL);
+ dispatch_set_target_queue(fileQueue,
+ dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
+ });
+
+ return fileQueue;
+}
+
+#pragma mark - RCTAsyncLocalStorage
+
+@implementation RCTAsyncLocalStorage
+{
+ BOOL _haveSetup;
+ // The manifest is a dictionary of all keys with small values inlined. Null values indicate values that are stored
+ // in separate files (as opposed to nil values which don't exist). The manifest is read off disk at startup, and
+ // written to disk after all mutations.
+ NSMutableDictionary *_manifest;
+ NSString *_manifestPath;
+ NSString *_storageDirectory;
+}
+
+- (NSString *)_filePathForKey:(NSString *)key
+{
+ NSString *safeFileName = RCTMD5Hash(key);
+ return [_storageDirectory stringByAppendingPathComponent:safeFileName];
+}
+
+- (id)_ensureSetup
+{
+ if (_haveSetup) {
+ return nil;
+ }
+ NSString *documentDirectory =
+ [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
+ NSURL *homeURL = [NSURL fileURLWithPath:documentDirectory isDirectory:YES];
+ _storageDirectory = [[homeURL URLByAppendingPathComponent:kStorageDir isDirectory:YES] path];
+ NSError *error;
+ [[NSFileManager defaultManager] createDirectoryAtPath:_storageDirectory
+ withIntermediateDirectories:YES
+ attributes:nil
+ error:&error];
+ if (error) {
+ return RCTMakeError(@"Failed to create storage directory.", error, nil);
+ }
+ _manifestPath = [_storageDirectory stringByAppendingPathComponent:kManifestFilename];
+ NSDictionary *errorOut;
+ NSString *serialized = RCTReadFile(_manifestPath, nil, &errorOut);
+ _manifest = serialized ? [RCTJSONParse(serialized, &error) mutableCopy] : [NSMutableDictionary new];
+ if (error) {
+ RCTLogWarn(@"Failed to parse manifest - creating new one.\n\n%@", error);
+ _manifest = [NSMutableDictionary new];
+ }
+ _haveSetup = YES;
+ return nil;
+}
+
+- (id)_writeManifest:(NSMutableArray **)errors
+{
+ NSError *error;
+ NSString *serialized = RCTJSONStringify(_manifest, &error);
+ [serialized writeToFile:_manifestPath atomically:YES encoding:NSUTF8StringEncoding error:&error];
+ id errorOut;
+ if (error) {
+ errorOut = RCTMakeError(@"Failed to write manifest file.", error, nil);
+ RCTAppendError(errorOut, errors);
+ }
+ return errorOut;
+}
+
+- (id)_appendItemForKey:(NSString *)key toArray:(NSMutableArray *)result
+{
+ id errorOut = RCTErrorForKey(key);
+ if (errorOut) {
+ return errorOut;
+ }
+ id value = _manifest[key]; // nil means missing, null means there is a data file, anything else is an inline value.
+ if (value == [NSNull null]) {
+ NSString *filePath = [self _filePathForKey:key];
+ value = RCTReadFile(filePath, key, &errorOut);
+ }
+ [result addObject:@[key, value ?: [NSNull null]]]; // Insert null if missing or failure.
+ return errorOut;
+}
+
+- (id)_writeEntry:(NSArray *)entry
+{
+ if (![entry isKindOfClass:[NSArray class]] || entry.count != 2) {
+ return RCTMakeAndLogError(@"Entries must be arrays of the form [key: string, value: string], got: ", entry, nil);
+ }
+ if (![entry[1] isKindOfClass:[NSString class]]) {
+ return RCTMakeAndLogError(@"Values must be strings, got: ", entry[1], entry[0]);
+ }
+ NSString *key = entry[0];
+ id errorOut = RCTErrorForKey(key);
+ if (errorOut) {
+ return errorOut;
+ }
+ NSString *value = entry[1];
+ NSString *filePath = [self _filePathForKey:key];
+ NSError *error;
+ if (value.length <= kInlineValueThreshold) {
+ if (_manifest[key] && _manifest[key] != [NSNull null]) {
+ // If the value already existed but wasn't inlined, remove the old file.
+ [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
+ }
+ _manifest[key] = value;
+ return nil;
+ }
+ [value writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
+ if (error) {
+ errorOut = RCTMakeError(@"Failed to write value.", error, @{@"key": key});
+ } else {
+ _manifest[key] = [NSNull null]; // Mark existence of file with null, any other value is inline data.
+ }
+ return errorOut;
+}
+
+#pragma mark - Exported JS Functions
+
+- (void)multiGet:(NSArray *)keys callback:(RCTResponseSenderBlock)callback
+{
+ RCT_EXPORT();
+
+ if (!callback) {
+ RCTLogError(@"Called getItem without a callback.");
+ return;
+ }
+
+ dispatch_async(RCTFileQueue(), ^{
+ id errorOut = [self _ensureSetup];
+ if (errorOut) {
+ callback(@[@[errorOut], [NSNull null]]);
+ return;
+ }
+ NSMutableArray *errors;
+ NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:keys.count];
+ for (NSString *key in keys) {
+ id keyError = [self _appendItemForKey:key toArray:result];
+ RCTAppendError(keyError, &errors);
+ }
+ [self _writeManifest:&errors];
+ callback(@[errors ?: [NSNull null], result]);
+ });
+}
+
+- (void)multiSet:(NSArray *)kvPairs callback:(RCTResponseSenderBlock)callback
+{
+ RCT_EXPORT();
+
+ dispatch_async(RCTFileQueue(), ^{
+ id errorOut = [self _ensureSetup];
+ if (errorOut) {
+ callback(@[@[errorOut]]);
+ return;
+ }
+ NSMutableArray *errors;
+ for (NSArray *entry in kvPairs) {
+ id keyError = [self _writeEntry:entry];
+ RCTAppendError(keyError, &errors);
+ }
+ [self _writeManifest:&errors];
+ if (callback) {
+ callback(@[errors ?: [NSNull null]]);
+ }
+ });
+}
+
+- (void)multiRemove:(NSArray *)keys callback:(RCTResponseSenderBlock)callback
+{
+ RCT_EXPORT();
+
+ dispatch_async(RCTFileQueue(), ^{
+ id errorOut = [self _ensureSetup];
+ if (errorOut) {
+ callback(@[@[errorOut]]);
+ return;
+ }
+ NSMutableArray *errors;
+ for (NSString *key in keys) {
+ id keyError = RCTErrorForKey(key);
+ if (!keyError) {
+ NSString *filePath = [self _filePathForKey:key];
+ [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
+ [_manifest removeObjectForKey:key];
+ }
+ RCTAppendError(keyError, &errors);
+ }
+ [self _writeManifest:&errors];
+ if (callback) {
+ callback(@[errors ?: [NSNull null]]);
+ }
+ });
+}
+
+- (void)clear:(RCTResponseSenderBlock)callback
+{
+ RCT_EXPORT();
+
+ dispatch_async(RCTFileQueue(), ^{
+ id errorOut = [self _ensureSetup];
+ if (!errorOut) {
+ NSError *error;
+ for (NSString *key in _manifest) {
+ NSString *filePath = [self _filePathForKey:key];
+ [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
+ }
+ [_manifest removeAllObjects];
+ errorOut = [self _writeManifest:nil];
+ }
+ if (callback) {
+ callback(@[errorOut ?: [NSNull null]]);
+ }
+ });
+}
+
+- (void)getAllKeys:(RCTResponseSenderBlock)callback
+{
+ RCT_EXPORT();
+
+ dispatch_async(RCTFileQueue(), ^{
+ id errorOut = [self _ensureSetup];
+ if (errorOut) {
+ callback(@[errorOut, [NSNull null]]);
+ } else {
+ callback(@[[NSNull null], [_manifest allKeys]]);
+ }
+ });
+}
+
+@end
diff --git a/ReactKit/Modules/RCTExceptionsManager.h b/ReactKit/Modules/RCTExceptionsManager.h
index 02ea33202..340c7878b 100644
--- a/ReactKit/Modules/RCTExceptionsManager.h
+++ b/ReactKit/Modules/RCTExceptionsManager.h
@@ -4,6 +4,14 @@
#import "RCTBridgeModule.h"
-@interface RCTExceptionsManager : NSObject
+@protocol RCTExceptionsManagerDelegate
+
+- (void)unhandledJSExceptionWithMessage:(NSString *)message stack:(NSArray *)stack;
+
+@end
+
+@interface RCTExceptionsManager : NSObject
+
+- (instancetype)initWithDelegate:(id)delegate NS_DESIGNATED_INITIALIZER;
@end
diff --git a/ReactKit/Modules/RCTExceptionsManager.m b/ReactKit/Modules/RCTExceptionsManager.m
index 29cacf6c8..4d1b5c0ee 100644
--- a/ReactKit/Modules/RCTExceptionsManager.m
+++ b/ReactKit/Modules/RCTExceptionsManager.m
@@ -5,12 +5,32 @@
#import "RCTRedBox.h"
@implementation RCTExceptionsManager
+{
+ __weak id _delegate;
+}
+
+- (instancetype)initWithDelegate:(id)delegate
+{
+ if ((self = [super init])) {
+ _delegate = delegate;
+ }
+ return self;
+}
+
+- (instancetype)init
+{
+ return [self initWithDelegate:nil];
+}
- (void)reportUnhandledExceptionWithMessage:(NSString *)message stack:(NSArray *)stack
{
RCT_EXPORT(reportUnhandledException);
- [[RCTRedBox sharedInstance] showErrorMessage:message withStack:stack];
+ if (_delegate) {
+ [_delegate unhandledJSExceptionWithMessage:message stack:stack];
+ } else {
+ [[RCTRedBox sharedInstance] showErrorMessage:message withStack:stack];
+ }
}
- (void)updateExceptionMessage:(NSString *)message stack:(NSArray *)stack
diff --git a/ReactKit/ReactKit.xcodeproj/project.pbxproj b/ReactKit/ReactKit.xcodeproj/project.pbxproj
index fc8e49ac3..73ecaffef 100644
--- a/ReactKit/ReactKit.xcodeproj/project.pbxproj
+++ b/ReactKit/ReactKit.xcodeproj/project.pbxproj
@@ -11,6 +11,7 @@
134FCB3D1A6E7F0800051CC8 /* RCTContextExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3A1A6E7F0800051CC8 /* RCTContextExecutor.m */; };
134FCB3E1A6E7F0800051CC8 /* RCTWebViewExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3C1A6E7F0800051CC8 /* RCTWebViewExecutor.m */; };
13723B501A82FD3C00F88898 /* RCTStatusBarManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13723B4F1A82FD3C00F88898 /* RCTStatusBarManager.m */; };
+ 1372B70A1AB030C200659ED6 /* RCTAppState.m in Sources */ = {isa = PBXBuildFile; fileRef = 1372B7091AB030C200659ED6 /* RCTAppState.m */; };
137327E71AA5CF210034F82E /* RCTTabBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 137327E01AA5CF210034F82E /* RCTTabBar.m */; };
137327E81AA5CF210034F82E /* RCTTabBarItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 137327E21AA5CF210034F82E /* RCTTabBarItem.m */; };
137327E91AA5CF210034F82E /* RCTTabBarItemManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137327E41AA5CF210034F82E /* RCTTabBarItemManager.m */; };
@@ -34,14 +35,15 @@
13E067561A70F44B002CDEE1 /* RCTViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E0674E1A70F44B002CDEE1 /* RCTViewManager.m */; };
13E067571A70F44B002CDEE1 /* RCTView.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E067501A70F44B002CDEE1 /* RCTView.m */; };
13E067591A70F44B002CDEE1 /* UIView+ReactKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E067541A70F44B002CDEE1 /* UIView+ReactKit.m */; };
+ 14435CE51AAC4AE100FC20F4 /* RCTMap.m in Sources */ = {isa = PBXBuildFile; fileRef = 14435CE21AAC4AE100FC20F4 /* RCTMap.m */; };
+ 14435CE61AAC4AE100FC20F4 /* RCTMapManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14435CE41AAC4AE100FC20F4 /* RCTMapManager.m */; };
14F3620D1AABD06A001CE568 /* RCTSwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F362081AABD06A001CE568 /* RCTSwitch.m */; };
14F3620E1AABD06A001CE568 /* RCTSwitchManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F3620A1AABD06A001CE568 /* RCTSwitchManager.m */; };
14F484561AABFCE100FDF6B9 /* RCTSliderManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F484551AABFCE100FDF6B9 /* RCTSliderManager.m */; };
- 14435CE51AAC4AE100FC20F4 /* RCTMap.m in Sources */ = {isa = PBXBuildFile; fileRef = 14435CE21AAC4AE100FC20F4 /* RCTMap.m */; };
- 14435CE61AAC4AE100FC20F4 /* RCTMapManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14435CE41AAC4AE100FC20F4 /* RCTMapManager.m */; };
58114A161AAE854800E7D092 /* RCTPicker.m in Sources */ = {isa = PBXBuildFile; fileRef = 58114A131AAE854800E7D092 /* RCTPicker.m */; };
58114A171AAE854800E7D092 /* RCTPickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58114A151AAE854800E7D092 /* RCTPickerManager.m */; };
58C571C11AA56C1900CDF9C8 /* RCTDatePickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58C571BF1AA56C1900CDF9C8 /* RCTDatePickerManager.m */; };
+ 58114A501AAE93D500E7D092 /* RCTAsyncLocalStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 58114A4E1AAE93D500E7D092 /* RCTAsyncLocalStorage.m */; };
830A229E1A66C68A008503DA /* RCTRootView.m in Sources */ = {isa = PBXBuildFile; fileRef = 830A229D1A66C68A008503DA /* RCTRootView.m */; };
830BA4551A8E3BDA00D53203 /* RCTCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 830BA4541A8E3BDA00D53203 /* RCTCache.m */; };
832348161A77A5AA00B55238 /* Layout.c in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FC71A68125100A75B9A /* Layout.c */; };
@@ -79,6 +81,8 @@
134FCB3C1A6E7F0800051CC8 /* RCTWebViewExecutor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWebViewExecutor.m; sourceTree = ""; };
13723B4E1A82FD3C00F88898 /* RCTStatusBarManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTStatusBarManager.h; sourceTree = ""; };
13723B4F1A82FD3C00F88898 /* RCTStatusBarManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTStatusBarManager.m; sourceTree = ""; };
+ 1372B7081AB030C200659ED6 /* RCTAppState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAppState.h; sourceTree = ""; };
+ 1372B7091AB030C200659ED6 /* RCTAppState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAppState.m; sourceTree = ""; };
137327DF1AA5CF210034F82E /* RCTTabBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTabBar.h; sourceTree = ""; };
137327E01AA5CF210034F82E /* RCTTabBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTabBar.m; sourceTree = ""; };
137327E11AA5CF210034F82E /* RCTTabBarItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTabBarItem.h; sourceTree = ""; };
@@ -131,22 +135,24 @@
13E067531A70F44B002CDEE1 /* UIView+ReactKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+ReactKit.h"; sourceTree = ""; };
13E067541A70F44B002CDEE1 /* UIView+ReactKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+ReactKit.m"; sourceTree = ""; };
13EFFCCF1A98E6FE002607DC /* RCTJSMethodRegistrar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTJSMethodRegistrar.h; sourceTree = ""; };
+ 14435CE11AAC4AE100FC20F4 /* RCTMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMap.h; sourceTree = ""; };
+ 14435CE21AAC4AE100FC20F4 /* RCTMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMap.m; sourceTree = ""; };
+ 14435CE31AAC4AE100FC20F4 /* RCTMapManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMapManager.h; sourceTree = ""; };
+ 14435CE41AAC4AE100FC20F4 /* RCTMapManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMapManager.m; sourceTree = ""; };
14F362071AABD06A001CE568 /* RCTSwitch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSwitch.h; sourceTree = ""; };
14F362081AABD06A001CE568 /* RCTSwitch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSwitch.m; sourceTree = ""; };
14F362091AABD06A001CE568 /* RCTSwitchManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSwitchManager.h; sourceTree = ""; };
14F3620A1AABD06A001CE568 /* RCTSwitchManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSwitchManager.m; sourceTree = ""; };
14F484541AABFCE100FDF6B9 /* RCTSliderManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSliderManager.h; sourceTree = ""; };
14F484551AABFCE100FDF6B9 /* RCTSliderManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSliderManager.m; sourceTree = ""; };
- 14435CE11AAC4AE100FC20F4 /* RCTMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMap.h; sourceTree = ""; };
- 14435CE21AAC4AE100FC20F4 /* RCTMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMap.m; sourceTree = ""; };
- 14435CE31AAC4AE100FC20F4 /* RCTMapManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMapManager.h; sourceTree = ""; };
- 14435CE41AAC4AE100FC20F4 /* RCTMapManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMapManager.m; sourceTree = ""; };
58114A121AAE854800E7D092 /* RCTPicker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPicker.h; sourceTree = ""; };
58114A131AAE854800E7D092 /* RCTPicker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPicker.m; sourceTree = ""; };
58114A141AAE854800E7D092 /* RCTPickerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPickerManager.h; sourceTree = ""; };
58114A151AAE854800E7D092 /* RCTPickerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPickerManager.m; sourceTree = ""; };
58C571BF1AA56C1900CDF9C8 /* RCTDatePickerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDatePickerManager.m; sourceTree = ""; };
58C571C01AA56C1900CDF9C8 /* RCTDatePickerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDatePickerManager.h; sourceTree = ""; };
+ 58114A4E1AAE93D500E7D092 /* RCTAsyncLocalStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAsyncLocalStorage.m; sourceTree = ""; };
+ 58114A4F1AAE93D500E7D092 /* RCTAsyncLocalStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAsyncLocalStorage.h; sourceTree = ""; };
830213F31A654E0800B993E6 /* RCTBridgeModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTBridgeModule.h; sourceTree = ""; };
830A229C1A66C68A008503DA /* RCTRootView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRootView.h; sourceTree = ""; };
830A229D1A66C68A008503DA /* RCTRootView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRootView.m; sourceTree = ""; };
@@ -211,14 +217,16 @@
13B07FE01A69315300A75B9A /* Modules */ = {
isa = PBXGroup;
children = (
+ 1372B7081AB030C200659ED6 /* RCTAppState.h */,
+ 1372B7091AB030C200659ED6 /* RCTAppState.m */,
13B07FE71A69327A00A75B9A /* RCTAlertManager.h */,
13B07FE81A69327A00A75B9A /* RCTAlertManager.m */,
83C9110E1AAE6521001323A3 /* RCTAnimationManager.h */,
83C9110F1AAE6521001323A3 /* RCTAnimationManager.m */,
+ 58114A4F1AAE93D500E7D092 /* RCTAsyncLocalStorage.h */,
+ 58114A4E1AAE93D500E7D092 /* RCTAsyncLocalStorage.m */,
13B07FE91A69327A00A75B9A /* RCTExceptionsManager.h */,
13B07FEA1A69327A00A75B9A /* RCTExceptionsManager.m */,
- 5F5F0D971A9E456B001279FA /* RCTLocationObserver.h */,
- 5F5F0D981A9E456B001279FA /* RCTLocationObserver.m */,
13723B4E1A82FD3C00F88898 /* RCTStatusBarManager.h */,
13723B4F1A82FD3C00F88898 /* RCTStatusBarManager.m */,
13B07FED1A69327A00A75B9A /* RCTTiming.h */,
@@ -439,6 +447,7 @@
13B07FF01A69327A00A75B9A /* RCTExceptionsManager.m in Sources */,
83CBBA5A1A601E9000E9B192 /* RCTRedBox.m in Sources */,
83CBBA511A601E3B00E9B192 /* RCTAssert.m in Sources */,
+ 58114A501AAE93D500E7D092 /* RCTAsyncLocalStorage.m in Sources */,
832348161A77A5AA00B55238 /* Layout.c in Sources */,
14F3620D1AABD06A001CE568 /* RCTSwitch.m in Sources */,
14F3620E1AABD06A001CE568 /* RCTSwitchManager.m in Sources */,
@@ -450,6 +459,7 @@
13B080261A694A8400A75B9A /* RCTWrapperViewController.m in Sources */,
13B080051A6947C200A75B9A /* RCTScrollView.m in Sources */,
13B07FF21A69327A00A75B9A /* RCTTiming.m in Sources */,
+ 1372B70A1AB030C200659ED6 /* RCTAppState.m in Sources */,
13B0801F1A69489C00A75B9A /* RCTTextFieldManager.m in Sources */,
134FCB3D1A6E7F0800051CC8 /* RCTContextExecutor.m in Sources */,
13E067591A70F44B002CDEE1 /* UIView+ReactKit.m in Sources */,
diff --git a/ReactKit/Views/RCTPicker.h b/ReactKit/Views/RCTPicker.h
index cbc55c7f3..8b5b3c864 100644
--- a/ReactKit/Views/RCTPicker.h
+++ b/ReactKit/Views/RCTPicker.h
@@ -8,4 +8,7 @@
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
+@property (nonatomic, copy) NSArray *items;
+@property (nonatomic, assign) NSInteger selectedIndex;
+
@end
diff --git a/package.json b/package.json
index 13a3467e8..534a9691a 100644
--- a/package.json
+++ b/package.json
@@ -23,25 +23,15 @@
"scripts": {
"test": "jest",
"lint": "node linter.js Examples/",
- "start": "./packager/packager.sh"
+ "start": "./packager/packager.sh",
+ "postinstall": "cd packager && npm install"
},
"dependencies": {
- "absolute-path": "0.0.0",
"connect": "2.8.3",
- "debug": "~2.1.0",
- "joi": "~5.1.0",
"jstransform": "10.0.1",
- "module-deps": "3.5.6",
- "optimist": "0.6.1",
- "q": "1.0.1",
- "react-tools": "0.13.0-rc2",
- "sane": "1.0.1",
"source-map": "0.1.31",
"stacktrace-parser": "0.1.1",
- "uglify-js": "~2.4.16",
- "underscore": "1.7.0",
- "worker-farm": "1.1.0",
- "yargs": "1.3.2"
+ "react-tools": "0.13.0-rc2"
},
"devDependencies": {
"jest-cli": "0.2.1",
diff --git a/packager/blacklist.js b/packager/blacklist.js
index eb81f4524..468a60404 100644
--- a/packager/blacklist.js
+++ b/packager/blacklist.js
@@ -6,13 +6,11 @@
// Don't forget to everything listed here to `testConfig.json`
// modulePathIgnorePatterns.
var sharedBlacklist = [
- 'node_modules/JSAppServer',
- 'packager/react-packager',
+ __dirname,
'node_modules/parse/node_modules/xmlhttprequest/lib/XMLHttpRequest.js',
'node_modules/react-tools/src/utils/ImmutableObject.js',
'node_modules/react-tools/src/core/ReactInstanceHandles.js',
- 'node_modules/react-tools/src/event/EventPropagators.js',
- 'node_modules/jest-cli',
+ 'node_modules/react-tools/src/event/EventPropagators.js'
];
var webBlacklist = [
@@ -31,9 +29,9 @@ function escapeRegExp(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
}
-function blacklist(isWeb) {
+function blacklist(isWeb, additionalBlacklist) {
return new RegExp('(' +
- sharedBlacklist
+ (additionalBlacklist || []).concat(sharedBlacklist)
.concat(isWeb ? webBlacklist : iosBlacklist)
.map(escapeRegExp)
.join('|') +
diff --git a/packager/package.json b/packager/package.json
new file mode 100644
index 000000000..6033c216a
--- /dev/null
+++ b/packager/package.json
@@ -0,0 +1,45 @@
+{
+ "name": "react-native",
+ "version": "0.1.0",
+ "description": "Build native apps with React!",
+ "repository": {
+ "type": "git",
+ "url": "git@github.com:facebook/react-native.git"
+ },
+ "jest": {
+ "setupEnvScriptFile": "jestSupport/env.js",
+ "testPathIgnorePatterns": [
+ "/node_modules/"
+ ],
+ "testFileExtensions": [
+ "js"
+ ],
+ "unmockedModulePathPatterns": [
+ "source-map"
+ ]
+ },
+ "scripts": {
+ "test": "jest",
+ "lint": "node linter.js Examples/",
+ "start": "./packager/packager.sh"
+ },
+ "dependencies": {
+ "absolute-path": "0.0.0",
+ "debug": "~2.1.0",
+ "joi": "~5.1.0",
+ "module-deps": "3.5.6",
+ "optimist": "0.6.1",
+ "q": "1.0.1",
+ "sane": "1.0.1",
+ "source-map": "0.1.31",
+ "stacktrace-parser": "0.1.1",
+ "uglify-js": "~2.4.16",
+ "underscore": "1.7.0",
+ "worker-farm": "1.1.0",
+ "yargs": "1.3.2"
+ },
+ "devDependencies": {
+ "jest-cli": "0.2.1",
+ "eslint": "0.9.2"
+ }
+}