Updated AppState module to use new emitter system

Summary: AppState now subclasses NativeEventEmitter instead of using global RCTDeviceEventEmitter.

Reviewed By: javache

Differential Revision: D3310488

fbshipit-source-id: f0116599223f4411307385c0dab683659d8d63b6
This commit is contained in:
Nick Lockwood 2016-05-23 09:08:51 -07:00 committed by Facebook Github Bot 3
parent c87b737ca1
commit d9737571c4
20 changed files with 187 additions and 264 deletions

View File

@ -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

View File

@ -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];
}

View File

@ -41,11 +41,16 @@
RCT_EXPORT_MODULE()
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
- (NSArray<NSString *> *)customDirectEventTypes
{
return @[@"foo"];
}
#pragma clang diagnostic pop
@end

View File

@ -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;

View File

@ -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;

View File

@ -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 (
* <Text>Current state is: {this.state.currentAppState}</Text>
* );
* },
* ```
*
* 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;

View File

@ -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) {

View File

@ -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

View File

@ -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
}
}

View File

@ -346,8 +346,11 @@ RCT_EXPORT_MODULE()
}
NSArray<id> *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<id> *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];
});

View File

@ -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)

View File

@ -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
}
/**

View File

@ -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;

View File

@ -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

View File

@ -35,7 +35,6 @@ RCT_EXTERN const NSInteger RCTTextUpdateLagWarningThreshold;
RCT_EXTERN NSString *RCTNormalizeInputEventName(NSString *eventName);
@protocol RCTEvent <NSObject>
@required
@property (nonatomic, strong, readonly) NSNumber *viewTag;
@ -60,25 +59,25 @@ RCT_EXTERN NSString *RCTNormalizeInputEventName(NSString *eventName);
@interface RCTEventDispatcher : NSObject <RCTBridgeModule>
/**
* 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

View File

@ -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<RCTEvent>)event

View File

@ -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<RCTBridgeModule>
@interface RCTAppState : RCTEventEmitter
@end

View File

@ -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<NSString *> *)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

View File

@ -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<NSString *> *)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];

View File

@ -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);
};