From 3f08fe4b7f6ce7f0cd6850e7598ebb5c3b104700 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Tue, 24 May 2016 10:26:33 -0700 Subject: [PATCH] Update RCTNetworking, RCTNetInfo and RCTLocationManager to use new events system Summary: Updated networking and geolocation to use the new events system. Reviewed By: javache Differential Revision: D3339945 fbshipit-source-id: f1332fb2aab8560e4783739e223c1f31d583cfcf --- Examples/UIExplorer/AppStateExample.js | 32 +++++- Examples/UIExplorer/AppStateIOSExample.js | 99 ------------------- Examples/UIExplorer/UIExplorerList.ios.js | 4 - .../RCTEventDispatcherTests.m | 2 - Examples/UIExplorer/XHRExample.ios.js | 11 ++- Libraries/Geolocation/Geolocation.js | 17 ++-- Libraries/Geolocation/RCTLocationObserver.h | 4 +- Libraries/Geolocation/RCTLocationObserver.m | 20 ++-- Libraries/LinkingIOS/RCTLinkingManager.m | 3 + Libraries/Network/NetInfo.js | 6 +- Libraries/Network/RCTNetInfo.h | 6 +- Libraries/Network/RCTNetInfo.m | 41 ++++---- Libraries/Network/RCTNetworking.android.js | 55 ++++++++--- Libraries/Network/RCTNetworking.h | 6 +- Libraries/Network/RCTNetworking.ios.js | 38 +++++-- Libraries/Network/RCTNetworking.m | 37 +++---- Libraries/Network/XMLHttpRequest.android.js | 62 ------------ Libraries/Network/XMLHttpRequest.ios.js | 47 --------- ...MLHttpRequestBase.js => XMLHttpRequest.js} | 27 +++-- ...estBase-test.js => XMLHttpRequest-test.js} | 8 +- .../RCTPushNotificationManager.m | 9 ++ Libraries/react-native/react-native.js | 14 +-- Libraries/react-native/react-native.js.flow | 2 + React/Modules/RCTAccessibilityManager.m | 3 + React/Modules/RCTDevMenu.m | 9 ++ React/Modules/RCTEventEmitter.m | 4 +- React/Modules/RCTUIManager.m | 3 + 27 files changed, 229 insertions(+), 340 deletions(-) delete mode 100644 Examples/UIExplorer/AppStateIOSExample.js delete mode 100644 Libraries/Network/XMLHttpRequest.android.js delete mode 100644 Libraries/Network/XMLHttpRequest.ios.js rename Libraries/Network/{XMLHttpRequestBase.js => XMLHttpRequest.js} (95%) rename Libraries/Network/__tests__/{XMLHttpRequestBase-test.js => XMLHttpRequest-test.js} (95%) diff --git a/Examples/UIExplorer/AppStateExample.js b/Examples/UIExplorer/AppStateExample.js index 814039414..a5f2e55ed 100644 --- a/Examples/UIExplorer/AppStateExample.js +++ b/Examples/UIExplorer/AppStateExample.js @@ -1,4 +1,11 @@ /** + * Copyright (c) 2013-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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * @@ -16,9 +23,9 @@ */ 'use strict'; -var React = require('react'); -var ReactNative = require('react-native'); -var { +const React = require('react'); +const ReactNative = require('react-native'); +const { AppState, Text, View @@ -29,13 +36,19 @@ var AppStateSubscription = React.createClass({ return { appState: AppState.currentState, previousAppStates: [], + memoryWarnings: 0, }; }, componentDidMount: function() { AppState.addEventListener('change', this._handleAppStateChange); + AppState.addEventListener('memoryWarning', this._handleMemoryWarning); }, componentWillUnmount: function() { AppState.removeEventListener('change', this._handleAppStateChange); + AppState.removeEventListener('memoryWarning', this._handleMemoryWarning); + }, + _handleMemoryWarning: function() { + this.setState({memoryWarnings: this.state.memoryWarnings + 1}); }, _handleAppStateChange: function(appState) { var previousAppStates = this.state.previousAppStates.slice(); @@ -46,6 +59,13 @@ var AppStateSubscription = React.createClass({ }); }, render() { + if (this.props.showMemoryWarnings) { + return ( + + {this.state.memoryWarnings} + + ); + } if (this.props.showCurrentOnly) { return ( @@ -78,4 +98,10 @@ exports.examples = [ title: 'Previous states:', render(): ReactElement { return ; } }, + { + platform: 'ios', + title: 'Memory Warnings', + description: 'In the IOS simulator, hit Shift+Command+M to simulate a memory warning.', + render(): ReactElement { return ; } + }, ]; diff --git a/Examples/UIExplorer/AppStateIOSExample.js b/Examples/UIExplorer/AppStateIOSExample.js deleted file mode 100644 index 10aaed8aa..000000000 --- a/Examples/UIExplorer/AppStateIOSExample.js +++ /dev/null @@ -1,99 +0,0 @@ -/** - * The examples provided by Facebook are for non-commercial testing and - * evaluation purposes only. - * - * Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * @providesModule AppStateIOSExample - * @flow - */ -'use strict'; - -var React = require('react'); -var ReactNative = require('react-native'); -var { - AppStateIOS, - Text, - View -} = ReactNative; - -var AppStateSubscription = React.createClass({ - getInitialState() { - return { - appState: AppStateIOS.currentState, - previousAppStates: [], - memoryWarnings: 0, - }; - }, - componentDidMount: function() { - AppStateIOS.addEventListener('change', this._handleAppStateChange); - AppStateIOS.addEventListener('memoryWarning', this._handleMemoryWarning); - }, - componentWillUnmount: function() { - AppStateIOS.removeEventListener('change', this._handleAppStateChange); - AppStateIOS.removeEventListener('memoryWarning', this._handleMemoryWarning); - }, - _handleMemoryWarning: function() { - this.setState({memoryWarnings: this.state.memoryWarnings + 1}); - }, - _handleAppStateChange: function(appState) { - var previousAppStates = this.state.previousAppStates.slice(); - previousAppStates.push(this.state.appState); - this.setState({ - appState, - previousAppStates, - }); - }, - render() { - if (this.props.showMemoryWarnings) { - return ( - - {this.state.memoryWarnings} - - ); - } - 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(): ReactElement { return ; } - }, - { - title: 'Previous states:', - render(): ReactElement { return ; } - }, - { - title: 'Memory Warnings', - description: 'In the simulator, hit Shift+Command+M to simulate a memory warning.', - render(): ReactElement { return ; } - }, -]; diff --git a/Examples/UIExplorer/UIExplorerList.ios.js b/Examples/UIExplorer/UIExplorerList.ios.js index c63763e1e..e9ddd367b 100644 --- a/Examples/UIExplorer/UIExplorerList.ios.js +++ b/Examples/UIExplorer/UIExplorerList.ios.js @@ -171,10 +171,6 @@ const APIExamples: Array = [ key: 'AnExApp', module: require('./AnimatedGratuitousApp/AnExApp'), }, - { - key: 'AppStateIOSExample', - module: require('./AppStateIOSExample'), - }, { key: 'AppStateExample', module: require('./AppStateExample'), diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTEventDispatcherTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTEventDispatcherTests.m index 4fe88ee93..e9559d558 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTEventDispatcherTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTEventDispatcherTests.m @@ -105,9 +105,7 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - [_eventDispatcher sendDeviceEventWithName:_eventName body:_body]; - #pragma clang diagnostic pop [_bridge verify]; diff --git a/Examples/UIExplorer/XHRExample.ios.js b/Examples/UIExplorer/XHRExample.ios.js index b84c8b0a0..0deaa4eed 100644 --- a/Examples/UIExplorer/XHRExample.ios.js +++ b/Examples/UIExplorer/XHRExample.ios.js @@ -1,4 +1,11 @@ /** + * Copyright (c) 2013-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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * @@ -21,7 +28,7 @@ var { AlertIOS, CameraRoll, Image, - LinkingIOS, + Linking, ProgressViewIOS, StyleSheet, Text, @@ -215,7 +222,7 @@ class FormUploader extends React.Component { return; } var url = xhr.responseText.slice(index).split('\n')[0]; - LinkingIOS.openURL(url); + Linking.openURL(url); }; var formdata = new FormData(); if (this.state.randomPhoto) { diff --git a/Libraries/Geolocation/Geolocation.js b/Libraries/Geolocation/Geolocation.js index f49a9ebf0..f2eed384b 100644 --- a/Libraries/Geolocation/Geolocation.js +++ b/Libraries/Geolocation/Geolocation.js @@ -11,15 +11,16 @@ */ 'use strict'; -var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); -var RCTLocationObserver = require('NativeModules').LocationObserver; +const NativeEventEmitter = require('NativeEventEmitter'); +const RCTLocationObserver = require('NativeModules').LocationObserver; -var invariant = require('fbjs/lib/invariant'); -var logError = require('logError'); -var warning = require('fbjs/lib/warning'); +const invariant = require('fbjs/lib/invariant'); +const logError = require('logError'); +const warning = require('fbjs/lib/warning'); + +const LocationEventEmitter = new NativeEventEmitter(RCTLocationObserver); var subscriptions = []; - var updatesEnabled = false; type GeoOptions = { @@ -80,11 +81,11 @@ var Geolocation = { } var watchID = subscriptions.length; subscriptions.push([ - RCTDeviceEventEmitter.addListener( + LocationEventEmitter.addListener( 'geolocationDidChange', success ), - error ? RCTDeviceEventEmitter.addListener( + error ? LocationEventEmitter.addListener( 'geolocationError', error ) : null, diff --git a/Libraries/Geolocation/RCTLocationObserver.h b/Libraries/Geolocation/RCTLocationObserver.h index 873eeff6a..a60715558 100644 --- a/Libraries/Geolocation/RCTLocationObserver.h +++ b/Libraries/Geolocation/RCTLocationObserver.h @@ -7,8 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import "RCTBridgeModule.h" +#import "RCTEventEmitter.h" -@interface RCTLocationObserver : NSObject +@interface RCTLocationObserver : RCTEventEmitter @end diff --git a/Libraries/Geolocation/RCTLocationObserver.m b/Libraries/Geolocation/RCTLocationObserver.m index 23179c5ca..319477c99 100644 --- a/Libraries/Geolocation/RCTLocationObserver.m +++ b/Libraries/Geolocation/RCTLocationObserver.m @@ -113,8 +113,6 @@ static NSDictionary *RCTPositionError(RCTPositionErrorCode code, RCT_EXPORT_MODULE() -@synthesize bridge = _bridge; - #pragma mark - Lifecycle - (void)dealloc @@ -128,8 +126,12 @@ RCT_EXPORT_MODULE() return dispatch_get_main_queue(); } -#pragma mark - Private API +- (NSArray *)supportedEvents +{ + return @[@"geolocationDidChange", @"geolocationError"]; +} +#pragma mark - Private API - (void)beginLocationUpdatesWithDesiredAccuracy:(CLLocationAccuracy)desiredAccuracy distanceFilter:(CLLocationDistance)distanceFilter { @@ -279,11 +281,7 @@ RCT_EXPORT_METHOD(getCurrentPosition:(RCTLocationOptions)options // Send event if (_observingLocation) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - [_bridge.eventDispatcher sendDeviceEventWithName:@"geolocationDidChange" - body:_lastLocationEvent]; -#pragma clang diagnostic pop + [self sendEventWithName:@"geolocationDidChange" body:_lastLocationEvent]; } // Fire all queued callbacks @@ -324,11 +322,7 @@ RCT_EXPORT_METHOD(getCurrentPosition:(RCTLocationOptions)options // Send event if (_observingLocation) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - [_bridge.eventDispatcher sendDeviceEventWithName:@"geolocationError" - body:jsError]; -#pragma clang diagnostic pop + [self sendEventWithName:@"geolocationError" body:jsError]; } // Fire all queued error callbacks diff --git a/Libraries/LinkingIOS/RCTLinkingManager.m b/Libraries/LinkingIOS/RCTLinkingManager.m index 96f9e75b4..ca4112fc7 100644 --- a/Libraries/LinkingIOS/RCTLinkingManager.m +++ b/Libraries/LinkingIOS/RCTLinkingManager.m @@ -88,8 +88,11 @@ continueUserActivity:(NSUserActivity *)userActivity - (void)handleOpenURLNotification:(NSNotification *)notification { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [_bridge.eventDispatcher sendDeviceEventWithName:@"openURL" body:notification.userInfo]; +#pragma clang diagnostic pop } RCT_EXPORT_METHOD(openURL:(NSURL *)URL diff --git a/Libraries/Network/NetInfo.js b/Libraries/Network/NetInfo.js index 768fb795f..70d513cc8 100644 --- a/Libraries/Network/NetInfo.js +++ b/Libraries/Network/NetInfo.js @@ -12,12 +12,14 @@ 'use strict'; const Map = require('Map'); +const NativeEventEmitter = require('NativeEventEmitter'); const NativeModules = require('NativeModules'); const Platform = require('Platform'); -const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); const RCTNetInfo = NativeModules.NetInfo; const deprecatedCallback = require('deprecatedCallback'); +const NetInfoEventEmitter = new NativeEventEmitter(RCTNetInfo); + const DEVICE_CONNECTIVITY_EVENT = 'networkStatusDidChange'; type ChangeEventName = $Enum<{ @@ -176,7 +178,7 @@ const NetInfo = { eventName: ChangeEventName, handler: Function ): {remove: () => void} { - const listener = RCTDeviceEventEmitter.addListener( + const listener = NetInfoEventEmitter.addListener( DEVICE_CONNECTIVITY_EVENT, (appStateData) => { handler(appStateData.network_info); diff --git a/Libraries/Network/RCTNetInfo.h b/Libraries/Network/RCTNetInfo.h index 6c2556e0a..9c280b2cf 100644 --- a/Libraries/Network/RCTNetInfo.h +++ b/Libraries/Network/RCTNetInfo.h @@ -9,10 +9,10 @@ #import -#import "RCTBridgeModule.h" +#import "RCTEventEmitter.h" -@interface RCTNetInfo : NSObject +@interface RCTNetInfo : RCTEventEmitter -- (instancetype)initWithHost:(NSString *)host NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithHost:(NSString *)host; @end diff --git a/Libraries/Network/RCTNetInfo.m b/Libraries/Network/RCTNetInfo.m index 28d0fc073..5981e9f7f 100644 --- a/Libraries/Network/RCTNetInfo.m +++ b/Libraries/Network/RCTNetInfo.m @@ -22,10 +22,9 @@ static NSString *const RCTReachabilityStateCell = @"cell"; { SCNetworkReachabilityRef _reachability; NSString *_status; + NSString *_host; } -@synthesize bridge = _bridge; - RCT_EXPORT_MODULE() static void RCTReachabilityCallback(__unused SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) @@ -51,11 +50,7 @@ static void RCTReachabilityCallback(__unused SCNetworkReachabilityRef target, SC if (![status isEqualToString:self->_status]) { self->_status = status; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - [self->_bridge.eventDispatcher sendDeviceEventWithName:@"networkStatusDidChange" - body:@{@"network_info": status}]; -#pragma clang diagnostic pop + [self sendEventWithName:@"networkStatusDidChange" body:@{@"network_info": status}]; } } @@ -66,34 +61,40 @@ static void RCTReachabilityCallback(__unused SCNetworkReachabilityRef target, SC RCTAssertParam(host); RCTAssert(![host hasPrefix:@"http"], @"Host value should just contain the domain, not the URL scheme."); - 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); + if ((self = [self init])) { + _host = [host copy]; } return self; } -- (instancetype)init +- (NSArray *)supportedEvents { - return [self initWithHost:@"apple.com"]; + return @[@"networkStatusDidChange"]; } -- (void)dealloc +- (void)startObserving { - SCNetworkReachabilityUnscheduleFromRunLoop(_reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); - CFRelease(_reachability); + _status = RCTReachabilityStateUnknown; + _reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, _host.UTF8String ?: "apple.com"); + SCNetworkReachabilityContext context = { 0, ( __bridge void *)self, NULL, NULL, NULL }; + SCNetworkReachabilitySetCallback(_reachability, RCTReachabilityCallback, &context); + SCNetworkReachabilityScheduleWithRunLoop(_reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); +} + +- (void)stopObserving +{ + if (_reachability) { + SCNetworkReachabilityUnscheduleFromRunLoop(_reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); + CFRelease(_reachability); + } } #pragma mark - Public API -// TODO: remove error callback - not needed except by Subscribable interface RCT_EXPORT_METHOD(getCurrentConnectivity:(RCTPromiseResolveBlock)resolve reject:(__unused RCTPromiseRejectBlock)reject) { - resolve(@{@"network_info": _status}); + resolve(@{@"network_info": _status ?: RCTReachabilityStateUnknown}); } @end diff --git a/Libraries/Network/RCTNetworking.android.js b/Libraries/Network/RCTNetworking.android.js index 38f694d54..23aa9e8bf 100644 --- a/Libraries/Network/RCTNetworking.android.js +++ b/Libraries/Network/RCTNetworking.android.js @@ -12,39 +12,66 @@ // Do not require the native RCTNetworking module directly! Use this wrapper module instead. // It will add the necessary requestId, so that you don't have to generate it yourself. -var RCTNetworkingNative = require('NativeModules').Networking; +const FormData = require('FormData'); +const NativeEventEmitter = require('NativeEventEmitter'); +const RCTNetworkingNative = require('NativeModules').Networking; -var _requestId = 1; -var generateRequestId = function() { +type Header = [string, string]; + +function convertHeadersMapToArray(headers: Object): Array
{ + const headerArray = []; + for (const name in headers) { + headerArray.push([name, headers[name]]); + } + return headerArray; +} + +let _requestId = 1; +function generateRequestId() { return _requestId++; -}; +} /** * This class is a wrapper around the native RCTNetworking module. It adds a necessary unique * requestId to each network request that can be used to abort that request later on. */ -class RCTNetworking { +class RCTNetworking extends NativeEventEmitter { - static sendRequest(method, url, headers, data, useIncrementalUpdates, timeout) { - var requestId = generateRequestId(); + constructor() { + super(RCTNetworkingNative); + } + + sendRequest(method, url, headers, data, incrementalUpdates, timeout, callback) { + if (typeof data === 'string') { + data = {string: data}; + } else if (data instanceof FormData) { + data = { + formData: data.getParts().map((part) => { + part.headers = convertHeadersMapToArray(part.headers); + return part; + }), + }; + } + const requestId = generateRequestId(); RCTNetworkingNative.sendRequest( method, url, requestId, - headers, + convertHeadersMapToArray(headers), data, - useIncrementalUpdates, - timeout); - return requestId; + incrementalUpdates, + timeout + ); + callback(requestId); } - static abortRequest(requestId) { + abortRequest(requestId) { RCTNetworkingNative.abortRequest(requestId); } - static clearCookies(callback) { + clearCookies(callback) { RCTNetworkingNative.clearCookies(callback); } } -module.exports = RCTNetworking; +module.exports = new RCTNetworking(); diff --git a/Libraries/Network/RCTNetworking.h b/Libraries/Network/RCTNetworking.h index cd8b99dde..a06a1eeab 100644 --- a/Libraries/Network/RCTNetworking.h +++ b/Libraries/Network/RCTNetworking.h @@ -7,12 +7,10 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import - -#import "RCTBridge.h" +#import "RCTEventEmitter.h" #import "RCTNetworkTask.h" -@interface RCTNetworking : NSObject +@interface RCTNetworking : RCTEventEmitter /** * Does a handler exist for the specified request? diff --git a/Libraries/Network/RCTNetworking.ios.js b/Libraries/Network/RCTNetworking.ios.js index 677f2c1c9..3e0e4c352 100644 --- a/Libraries/Network/RCTNetworking.ios.js +++ b/Libraries/Network/RCTNetworking.ios.js @@ -10,21 +10,39 @@ */ 'use strict'; -var RCTNetworkingNative = require('NativeModules').Networking; +const FormData = require('FormData'); +const NativeEventEmitter = require('NativeEventEmitter'); +const RCTNetworkingNative = require('NativeModules').Networking; -/** - * This class is a wrapper around the native RCTNetworking module. - */ -class RCTNetworking { +class RCTNetworking extends NativeEventEmitter { - static sendRequest(query, callback) { - RCTNetworkingNative.sendRequest(query, callback); + constructor() { + super(RCTNetworkingNative); } - static abortRequest(requestId) { - RCTNetworkingNative.cancelRequest(requestId); + sendRequest(method, url, headers, data, incrementalUpdates, timeout, callback) { + if (typeof data === 'string') { + data = {string: data}; + } else if (data instanceof FormData) { + data = {formData: data.getParts()}; + } + RCTNetworkingNative.sendRequest({ + method, + url, + data, + headers, + incrementalUpdates, + timeout + }, callback); } + abortRequest(requestId) { + RCTNetworkingNative.abortRequest(requestId); + } + + clearCookies(callback) { + console.warn('RCTNetworking.clearCookies is not supported on iOS'); + } } -module.exports = RCTNetworking; +module.exports = new RCTNetworking(); diff --git a/Libraries/Network/RCTNetworking.m b/Libraries/Network/RCTNetworking.m index 5c7e3b9ee..07c21f095 100644 --- a/Libraries/Network/RCTNetworking.m +++ b/Libraries/Network/RCTNetworking.m @@ -129,11 +129,18 @@ static NSString *RCTGenerateFormBoundary() NSArray> *_handlers; } -@synthesize bridge = _bridge; @synthesize methodQueue = _methodQueue; RCT_EXPORT_MODULE() +- (NSArray *)supportedEvents +{ + return @[@"didCompleteNetworkResponse", + @"didReceiveNetworkResponse", + @"didSendNetworkData", + @"didReceiveNetworkData"]; +} + - (id)handlerForRequest:(NSURLRequest *)request { if (!request.URL) { @@ -142,7 +149,7 @@ RCT_EXPORT_MODULE() if (!_handlers) { // Get handlers, sorted in reverse priority order (highest priority first) - _handlers = [[_bridge modulesConformingToProtocol:@protocol(RCTURLRequestHandler)] sortedArrayUsingComparator:^NSComparisonResult(id a, id b) { + _handlers = [[self.bridge modulesConformingToProtocol:@protocol(RCTURLRequestHandler)] sortedArrayUsingComparator:^NSComparisonResult(id a, id b) { float priorityA = [a respondsToSelector:@selector(handlerPriority)] ? [a handlerPriority] : 0; float priorityB = [b respondsToSelector:@selector(handlerPriority)] ? [b handlerPriority] : 0; if (priorityA > priorityB) { @@ -346,11 +353,7 @@ RCT_EXPORT_MODULE() } NSArray *responseJSON = @[task.requestID, responseText ?: @""]; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - [_bridge.eventDispatcher sendDeviceEventWithName:@"didReceiveNetworkData" - body:responseJSON]; -#pragma clang diagnostic pop + [self sendEventWithName:@"didReceiveNetworkData" body:responseJSON]; } - (void)sendRequest:(NSURLRequest *)request @@ -364,10 +367,7 @@ RCT_EXPORT_MODULE() RCTURLRequestProgressBlock uploadProgressBlock = ^(int64_t progress, int64_t total) { dispatch_async(_methodQueue, ^{ NSArray *responseJSON = @[task.requestID, @((double)progress), @((double)total)]; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - [_bridge.eventDispatcher sendDeviceEventWithName:@"didSendNetworkData" body:responseJSON]; -#pragma clang diagnostic pop + [self sendEventWithName:@"didSendNetworkData" body:responseJSON]; }); }; @@ -385,11 +385,7 @@ RCT_EXPORT_MODULE() } id responseURL = response.URL ? response.URL.absoluteString : [NSNull null]; NSArray *responseJSON = @[task.requestID, @(status), headers, responseURL]; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - [_bridge.eventDispatcher sendDeviceEventWithName:@"didReceiveNetworkResponse" - body:responseJSON]; -#pragma clang diagnostic pop + [self sendEventWithName:@"didReceiveNetworkResponse" body:responseJSON]; }); }; @@ -410,12 +406,7 @@ RCT_EXPORT_MODULE() error.code == kCFURLErrorTimedOut ? @YES : @NO ]; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - [_bridge.eventDispatcher sendDeviceEventWithName:@"didCompleteNetworkResponse" - body:responseJSON]; -#pragma clang diagnostic pop - + [self sendEventWithName:@"didCompleteNetworkResponse" body:responseJSON]; [_tasksByRequestID removeObjectForKey:task.requestID]; }); }; @@ -469,7 +460,7 @@ RCT_EXPORT_METHOD(sendRequest:(NSDictionary *)query }]; } -RCT_EXPORT_METHOD(cancelRequest:(nonnull NSNumber *)requestID) +RCT_EXPORT_METHOD(abortRequest:(nonnull NSNumber *)requestID) { [_tasksByRequestID[requestID] cancel]; [_tasksByRequestID removeObjectForKey:requestID]; diff --git a/Libraries/Network/XMLHttpRequest.android.js b/Libraries/Network/XMLHttpRequest.android.js deleted file mode 100644 index 099df7526..000000000 --- a/Libraries/Network/XMLHttpRequest.android.js +++ /dev/null @@ -1,62 +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 XMLHttpRequest - * @flow - */ -'use strict'; - -var FormData = require('FormData'); -var RCTNetworking = require('RCTNetworking'); -var XMLHttpRequestBase = require('XMLHttpRequestBase'); - -type Header = [string, string]; - -function convertHeadersMapToArray(headers: Object): Array
{ - var headerArray = []; - for (var name in headers) { - headerArray.push([name, headers[name]]); - } - return headerArray; -} - -class XMLHttpRequest extends XMLHttpRequestBase { - sendImpl( - method: ?string, - url: ?string, - headers: Object, - data: any, - useIncrementalUpdates: boolean, - timeout: number, - ): void { - var body; - if (typeof data === 'string') { - body = {string: data}; - } else if (data instanceof FormData) { - body = { - formData: data.getParts().map((part) => { - part.headers = convertHeadersMapToArray(part.headers); - return part; - }), - }; - } else { - body = data; - } - var requestId = RCTNetworking.sendRequest( - method, - url, - convertHeadersMapToArray(headers), - body, - useIncrementalUpdates, - timeout - ); - this.didCreateRequest(requestId); - } -} - -module.exports = XMLHttpRequest; diff --git a/Libraries/Network/XMLHttpRequest.ios.js b/Libraries/Network/XMLHttpRequest.ios.js deleted file mode 100644 index dd4ee0dd6..000000000 --- a/Libraries/Network/XMLHttpRequest.ios.js +++ /dev/null @@ -1,47 +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 XMLHttpRequest - * @flow - */ -'use strict'; - -var FormData = require('FormData'); -var RCTNetworking = require('RCTNetworking'); - -var XMLHttpRequestBase = require('XMLHttpRequestBase'); - -class XMLHttpRequest extends XMLHttpRequestBase { - sendImpl( - method: ?string, - url: ?string, - headers: Object, - data: any, - incrementalUpdates: boolean, - timeout: number, - ): void { - if (typeof data === 'string') { - data = {string: data}; - } else if (data instanceof FormData) { - data = {formData: data.getParts()}; - } - RCTNetworking.sendRequest( - { - method, - url, - data, - headers, - incrementalUpdates, - timeout - }, - this.didCreateRequest.bind(this) - ); - } -} - -module.exports = XMLHttpRequest; diff --git a/Libraries/Network/XMLHttpRequestBase.js b/Libraries/Network/XMLHttpRequest.js similarity index 95% rename from Libraries/Network/XMLHttpRequestBase.js rename to Libraries/Network/XMLHttpRequest.js index d5d5da5c5..7b742c62f 100644 --- a/Libraries/Network/XMLHttpRequestBase.js +++ b/Libraries/Network/XMLHttpRequest.js @@ -6,13 +6,12 @@ * 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 XMLHttpRequestBase + * @providesModule XMLHttpRequest * @flow */ 'use strict'; -var RCTNetworking = require('RCTNetworking'); -var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); +const RCTNetworking = require('RCTNetworking'); const EventTarget = require('event-target-shim'); const invariant = require('fbjs/lib/invariant'); @@ -212,19 +211,19 @@ class XMLHttpRequestBase extends EventTarget(...XHR_EVENTS) { didCreateRequest(requestId: number): void { this._requestId = requestId; - this._subscriptions.push(RCTDeviceEventEmitter.addListener( + this._subscriptions.push(RCTNetworking.addListener( 'didSendNetworkData', (args) => this.__didUploadProgress(...args) )); - this._subscriptions.push(RCTDeviceEventEmitter.addListener( + this._subscriptions.push(RCTNetworking.addListener( 'didReceiveNetworkResponse', (args) => this._didReceiveResponse(...args) )); - this._subscriptions.push(RCTDeviceEventEmitter.addListener( + this._subscriptions.push(RCTNetworking.addListener( 'didReceiveNetworkData', (args) => this._didReceiveData(...args) )); - this._subscriptions.push(RCTDeviceEventEmitter.addListener( + this._subscriptions.push(RCTNetworking.addListener( 'didCompleteNetworkResponse', (args) => this.__didCompleteResponse(...args) )); @@ -337,10 +336,18 @@ class XMLHttpRequestBase extends EventTarget(...XHR_EVENTS) { url: ?string, headers: Object, data: any, - incrementalEvents: boolean, - timeout: number + useIncrementalUpdates: boolean, + timeout: number, ): void { - throw new Error('Subclass must define sendImpl method'); + RCTNetworking.sendRequest( + method, + url, + headers, + data, + useIncrementalUpdates, + timeout, + this.didCreateRequest.bind(this), + ); } send(data: any): void { diff --git a/Libraries/Network/__tests__/XMLHttpRequestBase-test.js b/Libraries/Network/__tests__/XMLHttpRequest-test.js similarity index 95% rename from Libraries/Network/__tests__/XMLHttpRequestBase-test.js rename to Libraries/Network/__tests__/XMLHttpRequest-test.js index 9d967d4b3..a08d67c5d 100644 --- a/Libraries/Network/__tests__/XMLHttpRequestBase-test.js +++ b/Libraries/Network/__tests__/XMLHttpRequest-test.js @@ -12,13 +12,11 @@ jest .disableAutomock() .dontMock('event-target-shim') - .dontMock('XMLHttpRequestBase'); + .dontMock('XMLHttpRequest'); -const XMLHttpRequestBase = require('XMLHttpRequestBase'); +const XMLHttpRequest = require('XMLHttpRequest'); -class XMLHttpRequest extends XMLHttpRequestBase {} - -describe('XMLHttpRequestBase', function(){ +describe('XMLHttpRequest', function(){ var xhr; var handleTimeout; var handleError; diff --git a/Libraries/PushNotificationIOS/RCTPushNotificationManager.m b/Libraries/PushNotificationIOS/RCTPushNotificationManager.m index 0d6453a36..ac360e254 100644 --- a/Libraries/PushNotificationIOS/RCTPushNotificationManager.m +++ b/Libraries/PushNotificationIOS/RCTPushNotificationManager.m @@ -137,20 +137,29 @@ RCT_EXPORT_MODULE() - (void)handleLocalNotificationReceived:(NSNotification *)notification { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [_bridge.eventDispatcher sendDeviceEventWithName:@"localNotificationReceived" body:notification.userInfo]; +#pragma clang diagnostic pop } - (void)handleRemoteNotificationReceived:(NSNotification *)notification { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [_bridge.eventDispatcher sendDeviceEventWithName:@"remoteNotificationReceived" body:notification.userInfo]; +#pragma clang diagnostic pop } - (void)handleRemoteNotificationsRegistered:(NSNotification *)notification { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [_bridge.eventDispatcher sendDeviceEventWithName:@"remoteNotificationsRegistered" body:notification.userInfo]; +#pragma clang diagnostic pop } /** diff --git a/Libraries/react-native/react-native.js b/Libraries/react-native/react-native.js index d867bd595..6200bd42d 100644 --- a/Libraries/react-native/react-native.js +++ b/Libraries/react-native/react-native.js @@ -10,11 +10,11 @@ */ 'use strict'; -var warning = require('fbjs/lib/warning'); +const warning = require('fbjs/lib/warning'); if (__DEV__) { - var warningDedupe = {}; - var addonWarn = function(prevName, newPackageName) { + const warningDedupe = {}; + const addonWarn = function(prevName, newPackageName) { warning( warningDedupe[prevName], 'React.addons.' + prevName + ' is deprecated. Please import the "' + @@ -25,7 +25,7 @@ if (__DEV__) { } // Export React, plus some native additions. -var ReactNative = { +const ReactNative = { // Components get ActivityIndicatorIOS() { return require('ActivityIndicatorIOS'); }, get ART() { return require('ReactNativeART'); }, @@ -87,9 +87,11 @@ var ReactNative = { get ImagePickerIOS() { return require('ImagePickerIOS'); }, get IntentAndroid() { return require('IntentAndroid'); }, get InteractionManager() { return require('InteractionManager'); }, + get Keyboard() { return require('Keyboard'); }, get LayoutAnimation() { return require('LayoutAnimation'); }, get Linking() { return require('Linking'); }, get LinkingIOS() { return require('LinkingIOS'); }, + get NativeEventEmitter() { return require('NativeEventEmitter'); }, get NavigationExperimental() { return require('NavigationExperimental'); }, get NetInfo() { return require('NetInfo'); }, get PanResponder() { return require('PanResponder'); }, @@ -171,7 +173,7 @@ var ReactNative = { // Preserve getters with warnings on the internal ReactNative copy without // invoking them. -var ReactNativeInternal = require('ReactNative'); +const ReactNativeInternal = require('ReactNative'); function applyForwarding(key) { if (__DEV__) { Object.defineProperty( @@ -183,7 +185,7 @@ function applyForwarding(key) { } ReactNative[key] = ReactNativeInternal[key]; } -for (var key in ReactNativeInternal) { +for (const key in ReactNativeInternal) { applyForwarding(key); } diff --git a/Libraries/react-native/react-native.js.flow b/Libraries/react-native/react-native.js.flow index d300af0c4..066736dac 100644 --- a/Libraries/react-native/react-native.js.flow +++ b/Libraries/react-native/react-native.js.flow @@ -85,9 +85,11 @@ var ReactNative = Object.assign(Object.create(require('ReactNative')), { ImagePickerIOS: require('ImagePickerIOS'), IntentAndroid: require('IntentAndroid'), InteractionManager: require('InteractionManager'), + Keyboard: require('Keyboard'), LayoutAnimation: require('LayoutAnimation'), Linking: require('Linking'), LinkingIOS: require('LinkingIOS'), + NativeEventEmitter: require('NativeEventEmitter'), NavigationExperimental: require('NavigationExperimental'), NetInfo: require('NetInfo'), PanResponder: require('PanResponder'), diff --git a/React/Modules/RCTAccessibilityManager.m b/React/Modules/RCTAccessibilityManager.m index fd629cae3..3fcef1e5d 100644 --- a/React/Modules/RCTAccessibilityManager.m +++ b/React/Modules/RCTAccessibilityManager.m @@ -92,8 +92,11 @@ RCT_EXPORT_MODULE() BOOL newIsVoiceOverEnabled = UIAccessibilityIsVoiceOverRunning(); if (_isVoiceOverEnabled != newIsVoiceOverEnabled) { _isVoiceOverEnabled = newIsVoiceOverEnabled; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [_bridge.eventDispatcher sendDeviceEventWithName:@"voiceOverDidChange" body:@(_isVoiceOverEnabled)]; +#pragma clang diagnostic pop } } diff --git a/React/Modules/RCTDevMenu.m b/React/Modules/RCTDevMenu.m index e853659a5..f574f7b06 100644 --- a/React/Modules/RCTDevMenu.m +++ b/React/Modules/RCTDevMenu.m @@ -182,7 +182,10 @@ RCT_EXPORT_MODULE() selectedTitle:@"Hide Inspector" handler:^(__unused BOOL enabled) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [weakSelf.bridge.eventDispatcher sendDeviceEventWithName:@"toggleElementInspector" body:nil]; +#pragma clang diagnostic pop }]]; _webSocketExecutorName = [_defaults objectForKey:@"websocket-executor-name"] ?: @"JS Remotely"; @@ -214,8 +217,11 @@ RCT_EXPORT_MODULE() modifierFlags:UIKeyModifierCommand action:^(__unused UIKeyCommand *command) { [weakSelf.bridge.eventDispatcher +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" sendDeviceEventWithName:@"toggleElementInspector" body:nil]; +#pragma clang diagnostic pop }]; // Reload in normal mode @@ -388,7 +394,10 @@ RCT_EXPORT_MODULE() // Inspector can only be shown after JS has loaded if ([_settings[@"showInspector"] boolValue]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [self.bridge.eventDispatcher sendDeviceEventWithName:@"toggleElementInspector" body:nil]; +#pragma clang diagnostic pop } }); } diff --git a/React/Modules/RCTEventEmitter.m b/React/Modules/RCTEventEmitter.m index 5f399e830..3d4f70a5f 100644 --- a/React/Modules/RCTEventEmitter.m +++ b/React/Modules/RCTEventEmitter.m @@ -37,7 +37,9 @@ - (void)sendEventWithName:(NSString *)eventName body:(id)body { - RCTAssert(_bridge != nil, @"bridge is not set."); + RCTAssert(_bridge != nil, @"bridge is not set. This is probably because you've " + "explicitly synthesized the bridge in %@, even though it's inherited " + "from RCTEventEmitter.", [self class]); if (RCT_DEBUG && ![[self supportedEvents] containsObject:eventName]) { RCTLogError(@"`%@` is not a supported event type for %@. Supported events are: `%@`", diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index a819847d0..11112f92a 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -253,8 +253,11 @@ RCT_EXPORT_MODULE() !UIInterfaceOrientationIsPortrait(nextOrientation)) || (UIInterfaceOrientationIsLandscape(_currentInterfaceOrientation) && !UIInterfaceOrientationIsLandscape(nextOrientation))) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [_bridge.eventDispatcher sendDeviceEventWithName:@"didUpdateDimensions" body:RCTExportedDimensions(YES)]; +#pragma clang diagnostic pop } _currentInterfaceOrientation = nextOrientation;