diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/RCTRootViewIntegrationTests.m b/Examples/UIExplorer/UIExplorerIntegrationTests/RCTRootViewIntegrationTests.m index 7e3875d71..8695d27c2 100644 --- a/Examples/UIExplorer/UIExplorerIntegrationTests/RCTRootViewIntegrationTests.m +++ b/Examples/UIExplorer/UIExplorerIntegrationTests/RCTRootViewIntegrationTests.m @@ -66,9 +66,13 @@ typedef void (^ControlBlock)(RCTRootView*); - (void)rootViewDidChangeIntrinsicSize:(RCTRootView *)rootView { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [rootView.bridge.eventDispatcher sendAppEventWithName:@"rootViewDidChangeIntrinsicSize" body:@{@"width": @(rootView.intrinsicSize.width), @"height": @(rootView.intrinsicSize.height)}]; +#pragma clang diagnostic pop } @end diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTEventDispatcherTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTEventDispatcherTests.m index 6c43e2dd4..4fe88ee93 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTEventDispatcherTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTEventDispatcherTests.m @@ -103,8 +103,13 @@ [[_bridge expect] enqueueJSCall:_JSMethod args:[_testEvent arguments]]; +#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/UIExplorerUnitTests/RCTModuleInitNotificationRaceTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleInitNotificationRaceTests.m index 77a997a4b..2e9220d2d 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleInitNotificationRaceTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleInitNotificationRaceTests.m @@ -41,11 +41,16 @@ RCT_EXPORT_MODULE() +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" + - (NSArray *)customDirectEventTypes { return @[@"foo"]; } +#pragma clang diagnostic pop + @end diff --git a/Libraries/AppState/AppState.js b/Libraries/AppState/AppState.js index 8f9a7cf4b..1573a9012 100644 --- a/Libraries/AppState/AppState.js +++ b/Libraries/AppState/AppState.js @@ -11,18 +11,12 @@ */ 'use strict'; -var Map = require('Map'); -var NativeModules = require('NativeModules'); -var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); -var RCTAppState = NativeModules.AppState; +const NativeEventEmitter = require('NativeEventEmitter'); +const NativeModules = require('NativeModules'); +const RCTAppState = NativeModules.AppState; -var logError = require('logError'); -var invariant = require('fbjs/lib/invariant'); - -var _eventHandlers = { - change: new Map(), - memoryWarning: new Map(), -}; +const logError = require('logError'); +const invariant = require('fbjs/lib/invariant'); /** * `AppState` can tell you if the app is in the foreground or background, @@ -36,8 +30,9 @@ var _eventHandlers = { * - `active` - The app is running in the foreground * - `background` - The app is running in the background. The user is either * in another app or on the home screen - * - `inactive` - This is a transition state that currently never happens for - * typical React Native apps. + * - `inactive` - This is a state that occurs when transitioning between + * foreground & background, and during periods of inactivity such as + * entering the Multitasking view or in the event of an incoming call * * For more information, see * [Apple's documentation](https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html) @@ -75,13 +70,58 @@ var _eventHandlers = { * state will happen only momentarily. */ -var AppState = { +class AppState extends NativeEventEmitter { - /** + _eventHandlers: Object; + currentState: ?string; + + constructor() { + super(RCTAppState); + + this._eventHandlers = { + change: new Map(), + memoryWarning: new Map(), + }; + + // TODO: getCurrentAppState callback seems to be called at a really late stage + // after app launch. Trying to get currentState when mounting App component + // will likely to have the initial value here. + // Initialize to 'active' instead of null. + this.currentState = 'active'; + + // TODO: this is a terrible solution - in order to ensure `currentState` prop + // is up to date, we have to register an observer that updates it whenever + // the state changes, even if nobody cares. We should just deprecate the + // `currentState` property and get rid of this. + this.addListener( + 'appStateDidChange', + (appStateData) => { + this.currentState = appStateData.app_state; + } + ); + + // TODO: see above - this request just populates the value of `currentState` + // when the module is first initialized. Would be better to get rid of the prop + // and expose `getCurrentAppState` method directly. + RCTAppState.getCurrentAppState( + (appStateData) => { + this.currentState = appStateData.app_state; + }, + logError + ); + } + + /** * Add a handler to AppState changes by listening to the `change` event type * and providing the handler + * + * TODO: now that AppState is a subclass of NativeEventEmitter, we could deprecate + * `addEventListener` and `removeEventListener` and just use `addListener` and + * `listener.remove()` directly. That will be a breaking change though, as both + * the method and event names are different (addListener events are currently + * required to be globally unique). */ - addEventListener: function( + addEventListener( type: string, handler: Function ) { @@ -90,24 +130,24 @@ var AppState = { 'Trying to subscribe to unknown event: "%s"', type ); if (type === 'change') { - _eventHandlers[type].set(handler, RCTDeviceEventEmitter.addListener( + this._eventHandlers[type].set(handler, this.addListener( 'appStateDidChange', (appStateData) => { handler(appStateData.app_state); } )); } else if (type === 'memoryWarning') { - _eventHandlers[type].set(handler, RCTDeviceEventEmitter.addListener( + this._eventHandlers[type].set(handler, this.addListener( 'memoryWarning', handler )); } - }, + } /** * Remove a handler by passing the `change` event type and the handler */ - removeEventListener: function( + removeEventListener( type: string, handler: Function ) { @@ -115,28 +155,14 @@ var AppState = { ['change', 'memoryWarning'].indexOf(type) !== -1, 'Trying to remove listener for unknown event: "%s"', type ); - if (!_eventHandlers[type].has(handler)) { + if (!this._eventHandlers[type].has(handler)) { return; } - _eventHandlers[type].get(handler).remove(); - _eventHandlers[type].delete(handler); - }, - - currentState: ('active' : ?string), + this._eventHandlers[type].get(handler).remove(); + this._eventHandlers[type].delete(handler); + } }; -RCTDeviceEventEmitter.addListener( - 'appStateDidChange', - (appStateData) => { - AppState.currentState = appStateData.app_state; - } -); - -RCTAppState.getCurrentAppState( - (appStateData) => { - AppState.currentState = appStateData.app_state; - }, - logError -); +AppState = new AppState(); module.exports = AppState; diff --git a/Libraries/AppStateIOS/AppStateIOS.android.js b/Libraries/AppState/AppStateIOS.js similarity index 50% rename from Libraries/AppStateIOS/AppStateIOS.android.js rename to Libraries/AppState/AppStateIOS.js index 511174138..c2c29969e 100644 --- a/Libraries/AppStateIOS/AppStateIOS.android.js +++ b/Libraries/AppState/AppStateIOS.js @@ -11,20 +11,8 @@ */ 'use strict'; -var warning = require('fbjs/lib/warning'); +const AppState = require('AppState'); -class AppStateIOS { +console.warn('AppStateIOS is deprecated. Use AppState instead'); - static addEventListener(type, handler) { - warning(false, 'Cannot listen to AppStateIOS events on Android.'); - } - - static removeEventListener(type, handler) { - warning(false, 'Cannot remove AppStateIOS listener on Android.'); - } - -} - -AppStateIOS.currentState = null; - -module.exports = AppStateIOS; +module.exports = AppState; diff --git a/Libraries/AppStateIOS/AppStateIOS.ios.js b/Libraries/AppStateIOS/AppStateIOS.ios.js deleted file mode 100644 index 807f7701e..000000000 --- a/Libraries/AppStateIOS/AppStateIOS.ios.js +++ /dev/null @@ -1,147 +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 AppStateIOS - * @flow - */ -'use strict'; - -var NativeModules = require('NativeModules'); -var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); -var RCTAppState = NativeModules.AppState; - -var logError = require('logError'); -var invariant = require('fbjs/lib/invariant'); - -var _eventHandlers = { - change: new Map(), - memoryWarning: new Map(), -}; - -/** - * `AppStateIOS` can tell you if the app is in the foreground or background, - * and notify you when the state changes. - * - * AppStateIOS is frequently used to determine the intent and proper behavior when - * handling push notifications. - * - * ### iOS App States - * - * - `active` - The app is running in the foreground - * - `background` - The app is running in the background. The user is either - * in another app or on the home screen - * - `inactive` - This is a state that occurs when transitioning between - * foreground & background, and during periods of inactivity such as - * entering the Multitasking view or in the event of an incoming call - * - * For more information, see - * [Apple's documentation](https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html) - * - * ### Basic Usage - * - * To see the current state, you can check `AppStateIOS.currentState`, which - * will be kept up-to-date. However, `currentState` will be null at launch - * while `AppStateIOS` retrieves it over the bridge. - * - * ``` - * getInitialState: function() { - * return { - * currentAppState: AppStateIOS.currentState, - * }; - * }, - * componentDidMount: function() { - * AppStateIOS.addEventListener('change', this._handleAppStateChange); - * }, - * componentWillUnmount: function() { - * AppStateIOS.removeEventListener('change', this._handleAppStateChange); - * }, - * _handleAppStateChange: function(currentAppState) { - * this.setState({ currentAppState, }); - * }, - * render: function() { - * return ( - * Current state is: {this.state.currentAppState} - * ); - * }, - * ``` - * - * This example will only ever appear to say "Current state is: active" because - * the app is only visible to the user when in the `active` state, and the null - * state will happen only momentarily. - */ - -var AppStateIOS = { - - /** - * Add a handler to AppState changes by listening to the `change` event type - * and providing the handler - */ - addEventListener: function( - type: string, - handler: Function - ) { - invariant( - ['change', 'memoryWarning'].indexOf(type) !== -1, - 'Trying to subscribe to unknown event: "%s"', type - ); - if (type === 'change') { - _eventHandlers[type].set(handler, RCTDeviceEventEmitter.addListener( - 'appStateDidChange', - (appStateData) => { - handler(appStateData.app_state); - } - )); - } else if (type === 'memoryWarning') { - _eventHandlers[type].set(handler, RCTDeviceEventEmitter.addListener( - 'memoryWarning', - handler - )); - } - }, - - /** - * Remove a handler by passing the `change` event type and the handler - */ - removeEventListener: function( - type: string, - handler: Function - ) { - invariant( - ['change', 'memoryWarning'].indexOf(type) !== -1, - 'Trying to remove listener for unknown event: "%s"', type - ); - if (!_eventHandlers[type].has(handler)) { - return; - } - _eventHandlers[type].get(handler).remove(); - _eventHandlers[type].delete(handler); - }, - - // TODO: getCurrentAppState callback seems to be called at a really late stage - // after app launch. Trying to get currentState when mounting App component - // will likely to have the initial value here. - // Initialize to 'active' instead of null. - currentState: ('active' : ?string), - -}; - -RCTDeviceEventEmitter.addListener( - 'appStateDidChange', - (appStateData) => { - AppStateIOS.currentState = appStateData.app_state; - } -); - -RCTAppState.getCurrentAppState( - (appStateData) => { - AppStateIOS.currentState = appStateData.app_state; - }, - logError -); - -module.exports = AppStateIOS; diff --git a/Libraries/EventEmitter/RCTDeviceEventEmitter.js b/Libraries/EventEmitter/RCTDeviceEventEmitter.js index 8a6bbea47..ba5b5f16a 100644 --- a/Libraries/EventEmitter/RCTDeviceEventEmitter.js +++ b/Libraries/EventEmitter/RCTDeviceEventEmitter.js @@ -30,31 +30,35 @@ class RCTDeviceEventEmitter extends EventEmitter { super(sharedSubscriber); this.sharedSubscriber = sharedSubscriber; } + + _nativeEventModule(eventType: ?string) { + if (eventType) { + if (eventType.lastIndexOf('statusBar', 0) === 0) { + console.warn('`%s` event should be registered via the StatusBarIOS module', eventType); + return require('StatusBarIOS'); + } + if (eventType.lastIndexOf('keyboard', 0) === 0) { + console.warn('`%s` event should be registered via the Keyboard module', eventType); + return require('Keyboard'); + } + if (eventType === 'appStateDidChange' || eventType === 'memoryWarning') { + console.warn('`%s` event should be registered via the AppState module', eventType); + return require('AppState'); + } + } + return null; + } addListener(eventType: string, listener: Function, context: ?Object): EmitterSubscription { - if (eventType.lastIndexOf('statusBar', 0) === 0) { - console.warn('`%s` event should be registered via the StatusBarIOS module', eventType); - return require('StatusBarIOS').addListener(eventType, listener, context); - } - if (eventType.lastIndexOf('keyboard', 0) === 0) { - console.warn('`%s` event should be registered via the Keyboard module', eventType); - return require('Keyboard').addListener(eventType, listener, context); - } - return super.addListener(eventType, listener, context); + const eventModule = this._nativeEventModule(eventType); + return eventModule ? eventModule.addListener(eventType, listener, context) + : super.addListener(eventType, listener, context); } removeAllListeners(eventType: ?string) { - if (eventType) { - if (eventType.lastIndexOf('statusBar', 0) === 0) { - console.warn('statusBar events should be unregistered via the StatusBarIOS module'); - return require('StatusBarIOS').removeAllListeners(eventType); - } - if (eventType.lastIndexOf('keyboard', 0) === 0) { - console.warn('keyboard events should be unregistered via the Keyboard module'); - return require('Keyboard').removeAllListeners(eventType); - } - } - super.removeAllListeners(eventType); + const eventModule = this._nativeEventModule(eventType); + (eventModule && eventType) ? eventModule.removeAllListeners(eventType) + : super.removeAllListeners(eventType); } removeSubscription(subscription: EmitterSubscription) { diff --git a/Libraries/Geolocation/RCTLocationObserver.m b/Libraries/Geolocation/RCTLocationObserver.m index 85f4e86b2..23179c5ca 100644 --- a/Libraries/Geolocation/RCTLocationObserver.m +++ b/Libraries/Geolocation/RCTLocationObserver.m @@ -279,8 +279,11 @@ 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 } // Fire all queued callbacks @@ -321,8 +324,11 @@ 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 } // Fire all queued error callbacks diff --git a/Libraries/Network/RCTNetInfo.m b/Libraries/Network/RCTNetInfo.m index c5440ce5d..28d0fc073 100644 --- a/Libraries/Network/RCTNetInfo.m +++ b/Libraries/Network/RCTNetInfo.m @@ -51,8 +51,11 @@ 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 } } diff --git a/Libraries/Network/RCTNetworking.m b/Libraries/Network/RCTNetworking.m index 27f7e05f5..5c7e3b9ee 100644 --- a/Libraries/Network/RCTNetworking.m +++ b/Libraries/Network/RCTNetworking.m @@ -346,8 +346,11 @@ 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 } - (void)sendRequest:(NSURLRequest *)request @@ -361,7 +364,10 @@ 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 }); }; @@ -379,8 +385,11 @@ 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 }); }; @@ -401,8 +410,11 @@ 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 [_tasksByRequestID removeObjectForKey:task.requestID]; }); diff --git a/Libraries/RCTTest/RCTTestModule.m b/Libraries/RCTTest/RCTTestModule.m index a889b8522..2d86a1c6b 100644 --- a/Libraries/RCTTest/RCTTestModule.m +++ b/Libraries/RCTTest/RCTTestModule.m @@ -52,7 +52,10 @@ RCT_EXPORT_METHOD(verifySnapshot:(RCTResponseSenderBlock)callback) RCT_EXPORT_METHOD(sendAppEvent:(NSString *)name body:(nullable id)body) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [_bridge.eventDispatcher sendAppEventWithName:name body:body]; +#pragma clang diagnostic pop } RCT_REMAP_METHOD(shouldResolve, shouldResolve_resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) diff --git a/Libraries/Settings/RCTSettingsManager.m b/Libraries/Settings/RCTSettingsManager.m index c0c915df7..859506e67 100644 --- a/Libraries/Settings/RCTSettingsManager.m +++ b/Libraries/Settings/RCTSettingsManager.m @@ -62,9 +62,12 @@ RCT_EXPORT_MODULE() return; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [_bridge.eventDispatcher sendDeviceEventWithName:@"settingsUpdated" body:RCTJSONClean([_defaults dictionaryRepresentation])]; +#pragma clang diagnostic pop } /** diff --git a/Libraries/Text/RCTTextView.h b/Libraries/Text/RCTTextView.h index 5279eafb6..93a93d409 100644 --- a/Libraries/Text/RCTTextView.h +++ b/Libraries/Text/RCTTextView.h @@ -28,6 +28,7 @@ @property (nonatomic, assign) NSInteger mostRecentEventCount; @property (nonatomic, strong) NSNumber *maxLength; +@property (nonatomic, copy) RCTDirectEventBlock onChange; @property (nonatomic, copy) RCTDirectEventBlock onSelectionChange; - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; diff --git a/Libraries/Text/RCTTextView.m b/Libraries/Text/RCTTextView.m index fad4719d4..d32463333 100644 --- a/Libraries/Text/RCTTextView.m +++ b/Libraries/Text/RCTTextView.m @@ -472,7 +472,7 @@ static NSAttributedString *removeReactTagFromString(NSAttributedString *string) [self _setPlaceholderVisibility]; _nativeEventCount++; - if (!self.reactTag) { + if (!self.reactTag || !_onChange) { return; } @@ -490,8 +490,7 @@ static NSAttributedString *removeReactTagFromString(NSAttributedString *string) } _previousTextLength = textLength; _previousContentHeight = contentHeight; - - NSDictionary *event = @{ + _onChange(@{ @"text": self.text, @"contentSize": @{ @"height": @(contentHeight), @@ -499,8 +498,7 @@ static NSAttributedString *removeReactTagFromString(NSAttributedString *string) }, @"target": self.reactTag, @"eventCount": @(_nativeEventCount), - }; - [_eventDispatcher sendInputEventWithName:@"change" body:event]; + }); } - (void)textViewDidEndEditing:(UITextView *)textView diff --git a/React/Base/RCTEventDispatcher.h b/React/Base/RCTEventDispatcher.h index 11045f667..6b5b448c1 100644 --- a/React/Base/RCTEventDispatcher.h +++ b/React/Base/RCTEventDispatcher.h @@ -35,7 +35,6 @@ RCT_EXTERN const NSInteger RCTTextUpdateLagWarningThreshold; RCT_EXTERN NSString *RCTNormalizeInputEventName(NSString *eventName); @protocol RCTEvent - @required @property (nonatomic, strong, readonly) NSNumber *viewTag; @@ -60,25 +59,25 @@ RCT_EXTERN NSString *RCTNormalizeInputEventName(NSString *eventName); @interface RCTEventDispatcher : NSObject /** - * Send an application-specific event that does not relate to a specific - * view, e.g. a navigation or data update notification. + * Deprecated, do not use. */ -- (void)sendAppEventWithName:(NSString *)name body:(id)body; +- (void)sendAppEventWithName:(NSString *)name body:(id)body +__deprecated_msg("Subclass RCTEventEmitter instead"); /** - * Send a device or iOS event that does not relate to a specific view, - * e.g.rotation, location, keyboard show/hide, background/awake, etc. + * Deprecated, do not use. */ -- (void)sendDeviceEventWithName:(NSString *)name body:(id)body; +- (void)sendDeviceEventWithName:(NSString *)name body:(id)body +__deprecated_msg("Subclass RCTEventEmitter instead"); /** - * Send a user input event. The body dictionary must contain a "target" - * parameter, representing the React tag of the view sending the event + * Deprecated, do not use. */ -- (void)sendInputEventWithName:(NSString *)name body:(NSDictionary *)body; +- (void)sendInputEventWithName:(NSString *)name body:(NSDictionary *)body +__deprecated_msg("Use RCTDirectEventBlock or RCTBubblingEventBlock instead"); /** - * Send a text input/focus event. + * Send a text input/focus event. For internal use only. */ - (void)sendTextEventWithType:(RCTTextEventType)type reactTag:(NSNumber *)reactTag diff --git a/React/Base/RCTEventDispatcher.m b/React/Base/RCTEventDispatcher.m index 18a6c9f44..dbfd7a0df 100644 --- a/React/Base/RCTEventDispatcher.m +++ b/React/Base/RCTEventDispatcher.m @@ -126,7 +126,10 @@ RCT_EXPORT_MODULE() body[@"key"] = key; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [self sendInputEventWithName:events[type] body:body]; +#pragma clang diagnostic pop } - (void)sendEvent:(id)event diff --git a/React/Modules/RCTAppState.h b/React/Modules/RCTAppState.h index 4c2e758e2..99553d756 100644 --- a/React/Modules/RCTAppState.h +++ b/React/Modules/RCTAppState.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 RCTAppState : NSObject +@interface RCTAppState : RCTEventEmitter @end diff --git a/React/Modules/RCTAppState.m b/React/Modules/RCTAppState.m index 2cdac21bf..3f4c178b3 100644 --- a/React/Modules/RCTAppState.m +++ b/React/Modules/RCTAppState.m @@ -16,6 +16,8 @@ static NSString *RCTCurrentAppBackgroundState() { + RCTAssertMainThread(); + static NSDictionary *states; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ @@ -37,26 +39,22 @@ static NSString *RCTCurrentAppBackgroundState() NSString *_lastKnownState; } -@synthesize bridge = _bridge; - RCT_EXPORT_MODULE() +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} + #pragma mark - Lifecycle -- (instancetype)init +- (NSArray *)supportedEvents { - if ((self = [super init])) { - - // Needs to be called on the main thread, as it accesses UIApplication - _lastKnownState = RCTCurrentAppBackgroundState(); - } - return self; + return @[@"appStateDidChange", @"memoryWarning"]; } -- (void)setBridge:(RCTBridge *)bridge +- (void)startObserving { - _bridge = bridge; - for (NSString *name in @[UIApplicationDidBecomeActiveNotification, UIApplicationDidEnterBackgroundNotification, UIApplicationDidFinishLaunchingNotification, @@ -75,19 +73,18 @@ RCT_EXPORT_MODULE() object:nil]; } -- (void)handleMemoryWarning -{ - [_bridge.eventDispatcher sendDeviceEventWithName:@"memoryWarning" - body:nil]; -} - -- (void)dealloc +- (void)stopObserving { [[NSNotificationCenter defaultCenter] removeObserver:self]; } #pragma mark - App Notification Methods +- (void)handleMemoryWarning +{ + [self sendEventWithName:@"memoryWarning" body:nil]; +} + - (void)handleAppStateDidChange:(NSNotification *)notification { NSString *newState; @@ -102,8 +99,8 @@ RCT_EXPORT_MODULE() if (![newState isEqualToString:_lastKnownState]) { _lastKnownState = newState; - [_bridge.eventDispatcher sendDeviceEventWithName:@"appStateDidChange" - body:@{@"app_state": _lastKnownState}]; + [self sendEventWithName:@"appStateDidChange" + body:@{@"app_state": _lastKnownState}]; } } @@ -115,7 +112,7 @@ RCT_EXPORT_MODULE() RCT_EXPORT_METHOD(getCurrentAppState:(RCTResponseSenderBlock)callback error:(__unused RCTResponseSenderBlock)error) { - callback(@[@{@"app_state": _lastKnownState}]); + callback(@[@{@"app_state": RCTCurrentAppBackgroundState()}]); } @end diff --git a/React/Modules/RCTEventEmitter.m b/React/Modules/RCTEventEmitter.m index a3b22fa82..5f399e830 100644 --- a/React/Modules/RCTEventEmitter.m +++ b/React/Modules/RCTEventEmitter.m @@ -9,6 +9,7 @@ #import "RCTEventEmitter.h" #import "RCTAssert.h" +#import "RCTUtils.h" #import "RCTLog.h" @implementation RCTEventEmitter @@ -21,9 +22,16 @@ return @""; } ++ (void)initialize +{ + if (self != [RCTEventEmitter class]) { + RCTAssert(RCTClassOverridesInstanceMethod(self, @selector(supportedEvents)), + @"You must override the `supportedEvents` method of %@", self); + } +} + - (NSArray *)supportedEvents { - RCTAssert(NO, @"You must override the `supportedEvents` method of %@", [self class]); return nil; } @@ -32,7 +40,8 @@ RCTAssert(_bridge != nil, @"bridge is not set."); if (RCT_DEBUG && ![[self supportedEvents] containsObject:eventName]) { - RCTLogError(@"`%@` is not a supported event type for %@", eventName, [self class]); + RCTLogError(@"`%@` is not a supported event type for %@. Supported events are: `%@`", + eventName, [self class], [[self supportedEvents] componentsJoinedByString:@"`, `"]); } if (_listenerCount > 0) { [_bridge enqueueJSCall:@"RCTDeviceEventEmitter.emit" @@ -62,7 +71,8 @@ RCT_EXPORT_METHOD(addListener:(NSString *)eventName) { if (RCT_DEBUG && ![[self supportedEvents] containsObject:eventName]) { - RCTLogError(@"`%@` is not a supported event type for %@", eventName, [self class]); + RCTLogError(@"`%@` is not a supported event type for %@. Supported events are: `%@`", + eventName, [self class], [[self supportedEvents] componentsJoinedByString:@"`, `"]); } if (_listenerCount == 0) { [self startObserving]; diff --git a/React/Views/RCTComponentData.m b/React/Views/RCTComponentData.m index d00c178cf..9259540c1 100644 --- a/React/Views/RCTComponentData.m +++ b/React/Views/RCTComponentData.m @@ -180,7 +180,10 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) ((void (*)(id, SEL, id))objc_msgSend)(target, setter, [RCTConvert BOOL:json] ? ^(NSDictionary *body) { body = [NSMutableDictionary dictionaryWithDictionary:body]; ((NSMutableDictionary *)body)[@"target"] = weakTarget.reactTag; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [weakManager.bridge.eventDispatcher sendInputEventWithName:RCTNormalizeInputEventName(name) body:body]; +#pragma clang diagnostic pop } : nil); };