Updated Websocket to use new event system

Reviewed By: javache

Differential Revision: D3292473

fbshipit-source-id: f9a9e0a1b5a12f7fa8b36ebdba88405370f91c54
This commit is contained in:
Nick Lockwood 2016-05-12 08:29:39 -07:00 committed by Facebook Github Bot 6
parent cd691e2cc2
commit 2525feb37f
8 changed files with 56 additions and 54 deletions

View File

@ -12,5 +12,6 @@
'use strict'; 'use strict';
const NativeEventEmitter = require('NativeEventEmitter'); const NativeEventEmitter = require('NativeEventEmitter');
const KeyboardObserver = require('NativeModules').KeyboardObserver;
module.exports = new NativeEventEmitter('KeyboardObserver'); module.exports = new NativeEventEmitter(KeyboardObserver);

View File

@ -13,6 +13,7 @@
const NativeEventEmitter = require('NativeEventEmitter'); const NativeEventEmitter = require('NativeEventEmitter');
const StatusBar = require('StatusBar'); const StatusBar = require('StatusBar');
const StatusBarManager = require('NativeModules').StatusBarManager;
import type {StatusBarStyle, StatusBarAnimation} from 'StatusBar'; import type {StatusBarStyle, StatusBarAnimation} from 'StatusBar';
@ -38,6 +39,6 @@ class StatusBarIOS extends NativeEventEmitter {
); );
StatusBar.setNetworkActivityIndicatorVisible(visible); StatusBar.setNetworkActivityIndicatorVisible(visible);
} }
}; }
module.exports = new StatusBarIOS('StatusBarManager'); module.exports = new StatusBarIOS(StatusBarManager);

View File

@ -11,7 +11,6 @@
*/ */
'use strict'; 'use strict';
const NativeModules = require('NativeModules');
const Platform = require('Platform'); const Platform = require('Platform');
const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
const invariant = require('fbjs/lib/invariant'); const invariant = require('fbjs/lib/invariant');
@ -24,21 +23,17 @@ import type EmitterSubscription from 'EmitterSubscription';
*/ */
class NativeEventEmitter { class NativeEventEmitter {
_listenerCount: number;
_nativeModule: Object; _nativeModule: Object;
constructor(nativeModuleName: string) { constructor(nativeModule: Object) {
if (Platform.OS === 'ios') { if (Platform.OS === 'ios') {
this._listenerCount = 0; invariant(nativeModule, 'Native module cannot be null.');
this._nativeModule = NativeModules[nativeModuleName]; this._nativeModule = nativeModule;
invariant(this._nativeModule,
'Native module `' + nativeModuleName + '` not found.');
} }
} }
addListener(eventType: string, listener: any, context: ?Object): EmitterSubscription { addListener(eventType: string, listener: any, context: ?Object): EmitterSubscription {
if (Platform.OS === 'ios') { if (Platform.OS === 'ios') {
this._listenerCount++;
this._nativeModule.addListener(eventType); this._nativeModule.addListener(eventType);
} }
return RCTDeviceEventEmitter.nativeAddListener(eventType, listener, context); return RCTDeviceEventEmitter.nativeAddListener(eventType, listener, context);
@ -54,7 +49,7 @@ class NativeEventEmitter {
removeAllListeners(eventType: string) { removeAllListeners(eventType: string) {
invariant(eventType, 'eventType argument is required.'); invariant(eventType, 'eventType argument is required.');
if (Platform.OS === 'ios') { if (Platform.OS === 'ios') {
var count = RCTDeviceEventEmitter.listeners(eventType).length; const count = RCTDeviceEventEmitter.listeners(eventType).length;
this._nativeModule.removeListeners(count); this._nativeModule.removeListeners(count);
} }
RCTDeviceEventEmitter.removeAllListeners(eventType); RCTDeviceEventEmitter.removeAllListeners(eventType);
@ -62,7 +57,6 @@ class NativeEventEmitter {
removeCurrentListener() { removeCurrentListener() {
if (Platform.OS === 'ios') { if (Platform.OS === 'ios') {
this._listenerCount--;
this._nativeModule.removeListeners(1); this._nativeModule.removeListeners(1);
} }
RCTDeviceEventEmitter.removeCurrentListener(); RCTDeviceEventEmitter.removeCurrentListener();

View File

@ -13,23 +13,22 @@
const EventEmitter = require('EventEmitter'); const EventEmitter = require('EventEmitter');
const BatchedBridge = require('BatchedBridge'); const BatchedBridge = require('BatchedBridge');
const NativeModules = require('NativeModules');
import type EmitterSubscription from 'EmitterSubscription'; import type EmitterSubscription from 'EmitterSubscription';
/** /**
* Deprecated - subclass NativeEventEmitter to create granular event modules instead of * Deprecated - subclass NativeEventEmitter to create granular event modules instead of
* routing all event observation through RCTDeviceEventEmitter. * adding all event listeners directly to RCTDeviceEventEmitter.
*/ */
class RCTDeviceEventEmitter extends EventEmitter { class RCTDeviceEventEmitter extends EventEmitter {
addListener(eventType: string, listener: any, context: ?Object): EmitterSubscription { addListener(eventType: string, listener: any, context: ?Object): EmitterSubscription {
if (eventType.lastIndexOf('statusBar', 0) === 0) { if (eventType.lastIndexOf('statusBar', 0) === 0) {
console.warn('statusBar events should be registered via the StatusBarIOS module'); console.warn('`%s` event should be registered via the StatusBarIOS module', eventType);
return require('StatusBarIOS').addListener(eventType, listener, context); return require('StatusBarIOS').addListener(eventType, listener, context);
} }
if (eventType.lastIndexOf('keyboard', 0) === 0) { if (eventType.lastIndexOf('keyboard', 0) === 0) {
console.warn('keyboard events should be registered via the Keyboard module'); console.warn('`%s` event should be registered via the Keyboard module', eventType);
return require('Keyboard').addListener(eventType, listener, context); return require('Keyboard').addListener(eventType, listener, context);
} }
return super.addListener(eventType, listener, context); return super.addListener(eventType, listener, context);

View File

@ -7,9 +7,9 @@
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
*/ */
#import "RCTBridgeModule.h" #import "RCTEventEmitter.h"
#import "RCTSRWebSocket.h" #import "RCTSRWebSocket.h"
@interface RCTWebSocketModule : NSObject <RCTBridgeModule, RCTSRWebSocketDelegate> @interface RCTWebSocketModule : RCTEventEmitter <RCTSRWebSocketDelegate>
@end @end

View File

@ -9,8 +9,6 @@
#import "RCTWebSocketModule.h" #import "RCTWebSocketModule.h"
#import "RCTBridge.h"
#import "RCTEventDispatcher.h"
#import "RCTConvert.h" #import "RCTConvert.h"
#import "RCTUtils.h" #import "RCTUtils.h"
@ -35,7 +33,13 @@
RCT_EXPORT_MODULE() RCT_EXPORT_MODULE()
@synthesize bridge = _bridge; - (NSArray *)supportedEvents
{
return @[@"websocketMessage",
@"websocketOpen",
@"websocketFailed",
@"websocketClosed"];
}
- (void)dealloc - (void)dealloc
{ {
@ -84,7 +88,7 @@ RCT_EXPORT_METHOD(close:(nonnull NSNumber *)socketID)
- (void)webSocket:(RCTSRWebSocket *)webSocket didReceiveMessage:(id)message - (void)webSocket:(RCTSRWebSocket *)webSocket didReceiveMessage:(id)message
{ {
BOOL binary = [message isKindOfClass:[NSData class]]; BOOL binary = [message isKindOfClass:[NSData class]];
[_bridge.eventDispatcher sendDeviceEventWithName:@"websocketMessage" body:@{ [self sendEventWithName:@"websocketMessage" body:@{
@"data": binary ? [message base64EncodedStringWithOptions:0] : message, @"data": binary ? [message base64EncodedStringWithOptions:0] : message,
@"type": binary ? @"binary" : @"text", @"type": binary ? @"binary" : @"text",
@"id": webSocket.reactTag @"id": webSocket.reactTag
@ -93,14 +97,14 @@ RCT_EXPORT_METHOD(close:(nonnull NSNumber *)socketID)
- (void)webSocketDidOpen:(RCTSRWebSocket *)webSocket - (void)webSocketDidOpen:(RCTSRWebSocket *)webSocket
{ {
[_bridge.eventDispatcher sendDeviceEventWithName:@"websocketOpen" body:@{ [self sendEventWithName:@"websocketOpen" body:@{
@"id": webSocket.reactTag @"id": webSocket.reactTag
}]; }];
} }
- (void)webSocket:(RCTSRWebSocket *)webSocket didFailWithError:(NSError *)error - (void)webSocket:(RCTSRWebSocket *)webSocket didFailWithError:(NSError *)error
{ {
[_bridge.eventDispatcher sendDeviceEventWithName:@"websocketFailed" body:@{ [self sendEventWithName:@"websocketFailed" body:@{
@"message":error.localizedDescription, @"message":error.localizedDescription,
@"id": webSocket.reactTag @"id": webSocket.reactTag
}]; }];
@ -109,7 +113,7 @@ RCT_EXPORT_METHOD(close:(nonnull NSNumber *)socketID)
- (void)webSocket:(RCTSRWebSocket *)webSocket didCloseWithCode:(NSInteger)code - (void)webSocket:(RCTSRWebSocket *)webSocket didCloseWithCode:(NSInteger)code
reason:(NSString *)reason wasClean:(BOOL)wasClean reason:(NSString *)reason wasClean:(BOOL)wasClean
{ {
[_bridge.eventDispatcher sendDeviceEventWithName:@"websocketClosed" body:@{ [self sendEventWithName:@"websocketClosed" body:@{
@"code": @(code), @"code": @(code),
@"reason": RCTNullIfNil(reason), @"reason": RCTNullIfNil(reason),
@"clean": @(wasClean), @"clean": @(wasClean),

View File

@ -11,9 +11,9 @@
*/ */
'use strict'; 'use strict';
const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); const NativeEventEmitter = require('NativeEventEmitter');
const RCTWebSocketModule = require('NativeModules').WebSocketModule;
const Platform = require('Platform'); const Platform = require('Platform');
const RCTWebSocketModule = require('NativeModules').WebSocketModule;
const WebSocketEvent = require('WebSocketEvent'); const WebSocketEvent = require('WebSocketEvent');
const EventTarget = require('event-target-shim'); const EventTarget = require('event-target-shim');
@ -67,6 +67,7 @@ class WebSocket extends EventTarget(...WEBSOCKET_EVENTS) {
CLOSED: number = CLOSED; CLOSED: number = CLOSED;
_socketId: number; _socketId: number;
_eventEmitter: NativeEventEmitter;
_subscriptions: Array<EventSubscription>; _subscriptions: Array<EventSubscription>;
onclose: ?Function; onclose: ?Function;
@ -91,6 +92,7 @@ class WebSocket extends EventTarget(...WEBSOCKET_EVENTS) {
protocols = null; protocols = null;
} }
this._eventEmitter = new NativeEventEmitter(RCTWebSocketModule);
this._socketId = nextWebSocketId++; this._socketId = nextWebSocketId++;
RCTWebSocketModule.connect(url, protocols, options, this._socketId); RCTWebSocketModule.connect(url, protocols, options, this._socketId);
this._registerEvents(); this._registerEvents();
@ -136,8 +138,8 @@ class WebSocket extends EventTarget(...WEBSOCKET_EVENTS) {
_close(code?: number, reason?: string): void { _close(code?: number, reason?: string): void {
if (Platform.OS === 'android') { if (Platform.OS === 'android') {
// See https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent // See https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
var statusCode = typeof code === 'number' ? code : CLOSE_NORMAL; const statusCode = typeof code === 'number' ? code : CLOSE_NORMAL;
var closeReason = typeof reason === 'string' ? reason : ''; const closeReason = typeof reason === 'string' ? reason : '';
RCTWebSocketModule.close(statusCode, closeReason, this._socketId); RCTWebSocketModule.close(statusCode, closeReason, this._socketId);
} else { } else {
RCTWebSocketModule.close(this._socketId); RCTWebSocketModule.close(this._socketId);
@ -151,47 +153,43 @@ class WebSocket extends EventTarget(...WEBSOCKET_EVENTS) {
_registerEvents(): void { _registerEvents(): void {
this._subscriptions = [ this._subscriptions = [
RCTDeviceEventEmitter.addListener('websocketMessage', ev => { this._eventEmitter.addListener('websocketMessage', ev => {
if (ev.id !== this._socketId) { if (ev.id !== this._socketId) {
return; return;
} }
var event = new WebSocketEvent('message', { this.dispatchEvent(new WebSocketEvent('message', {
data: (ev.type === 'binary') ? base64.toByteArray(ev.data).buffer : ev.data data: (ev.type === 'binary') ? base64.toByteArray(ev.data).buffer : ev.data
}); }));
this.dispatchEvent(event);
}), }),
RCTDeviceEventEmitter.addListener('websocketOpen', ev => { this._eventEmitter.addListener('websocketOpen', ev => {
if (ev.id !== this._socketId) { if (ev.id !== this._socketId) {
return; return;
} }
this.readyState = this.OPEN; this.readyState = this.OPEN;
var event = new WebSocketEvent('open'); this.dispatchEvent(new WebSocketEvent('open'));
this.dispatchEvent(event);
}), }),
RCTDeviceEventEmitter.addListener('websocketClosed', ev => { this._eventEmitter.addListener('websocketClosed', ev => {
if (ev.id !== this._socketId) { if (ev.id !== this._socketId) {
return; return;
} }
this.readyState = this.CLOSED; this.readyState = this.CLOSED;
var event = new WebSocketEvent('close'); this.dispatchEvent(new WebSocketEvent('close', {
event.code = ev.code; code: ev.code,
event.reason = ev.reason; reason: ev.reason,
this.dispatchEvent(event); }));
this._unregisterEvents(); this._unregisterEvents();
this.close(); this.close();
}), }),
RCTDeviceEventEmitter.addListener('websocketFailed', ev => { this._eventEmitter.addListener('websocketFailed', ev => {
if (ev.id !== this._socketId) { if (ev.id !== this._socketId) {
return; return;
} }
var event = new WebSocketEvent('error'); this.dispatchEvent(new WebSocketEvent('error', {
event.message = ev.message; message: ev.message,
this.dispatchEvent(event); }));
this.dispatchEvent(new WebSocketEvent('close', {
event = new WebSocketEvent('close'); message: ev.message,
event.message = ev.message; }));
this.dispatchEvent(event);
this._unregisterEvents(); this._unregisterEvents();
this.close(); this.close();
}) })

View File

@ -23,18 +23,23 @@
- (NSArray<NSString *> *)supportedEvents - (NSArray<NSString *> *)supportedEvents
{ {
RCTAssert(NO, @"You must override the `supportedEvents` method of %@", [self class]);
return nil; return nil;
} }
- (void)sendEventWithName:(NSString *)eventName body:(id)body - (void)sendEventWithName:(NSString *)eventName body:(id)body
{ {
RCTAssert(self.bridge != nil, @"bridge is not set."); RCTAssert(_bridge != nil, @"bridge is not set.");
if (RCT_DEBUG && ![[self supportedEvents] containsObject: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 %@", eventName, [self class]);
} }
[self.bridge enqueueJSCall:@"RCTDeviceEventEmitter.emit" if (_listenerCount > 0) {
args:body ? @[eventName, body] : @[eventName]]; [_bridge enqueueJSCall:@"RCTDeviceEventEmitter.emit"
args:body ? @[eventName, body] : @[eventName]];
} else {
RCTLogWarn(@"Sending `%@` with no listeners registered.", eventName);
}
} }
- (void)startObserving - (void)startObserving