PageRenderTime 44ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/Pods/libPusher/Library/PTPusher.m

https://gitlab.com/trungminhnt/sampleShinobi
Objective C | 458 lines | 333 code | 82 blank | 43 comment | 51 complexity | f72147b1597f160ea0e7da7953b98ae4 MD5 | raw file
  1. //
  2. // PTPusher.m
  3. // PusherEvents
  4. //
  5. // Created by Luke Redpath on 22/03/2010.
  6. // Copyright 2010 LJR Software Limited. All rights reserved.
  7. //
  8. #import "PTPusher.h"
  9. #import "PTEventListener.h"
  10. #import "PTPusherEvent.h"
  11. #import "PTPusherChannel.h"
  12. #import "PTPusherEventDispatcher.h"
  13. #import "PTTargetActionEventListener.h"
  14. #import "PTBlockEventListener.h"
  15. #import "PTPusherErrors.h"
  16. #import "PTPusherChannelAuthorizationOperation.h"
  17. #import "PTPusherChannel_Private.h"
  18. #define kPUSHER_HOST @"ws.pusherapp.com"
  19. typedef NS_ENUM(NSUInteger, PTPusherAutoReconnectMode) {
  20. PTPusherAutoReconnectModeNoReconnect,
  21. PTPusherAutoReconnectModeReconnectImmediately,
  22. PTPusherAutoReconnectModeReconnectWithConfiguredDelay,
  23. PTPusherAutoReconnectModeReconnectWithBackoffDelay
  24. };
  25. NSURL *PTPusherConnectionURL(NSString *host, NSString *key, NSString *clientID, BOOL secure);
  26. NSString *const PTPusherEventReceivedNotification = @"PTPusherEventReceivedNotification";
  27. NSString *const PTPusherEventUserInfoKey = @"PTPusherEventUserInfoKey";
  28. NSString *const PTPusherErrorDomain = @"PTPusherErrorDomain";
  29. NSString *const PTPusherFatalErrorDomain = @"PTPusherFatalErrorDomain";
  30. NSString *const PTPusherErrorUnderlyingEventKey = @"PTPusherErrorUnderlyingEventKey";
  31. /** The Pusher protocol version, used to determined which features
  32. are supported.
  33. */
  34. #define kPTPusherClientProtocolVersion 6
  35. NSURL *PTPusherConnectionURL(NSString *host, NSString *key, NSString *clientID, BOOL encrypted)
  36. {
  37. NSString *scheme = ((encrypted == YES) ? @"wss" : @"ws");
  38. NSString *URLString = [NSString stringWithFormat:@"%@://%@/app/%@?client=%@&protocol=%d&version=%@",
  39. scheme, host, key, clientID, kPTPusherClientProtocolVersion, kPTPusherClientLibraryVersion];
  40. return [NSURL URLWithString:URLString];
  41. }
  42. #define kPTPusherDefaultReconnectDelay 5.0
  43. @interface PTPusher ()
  44. @property (nonatomic, strong, readwrite) PTPusherConnection *connection;
  45. @property (nonatomic, assign) PTPusherAutoReconnectMode autoReconnectMode;
  46. @end
  47. #pragma mark -
  48. @implementation PTPusher {
  49. NSOperationQueue *authorizationQueue;
  50. NSUInteger _numberOfReconnectAttempts;
  51. NSUInteger _maximumNumberOfReconnectAttempts;
  52. PTPusherEventDispatcher *dispatcher;
  53. NSMutableDictionary *channels;
  54. }
  55. - (id)initWithConnection:(PTPusherConnection *)connection
  56. {
  57. if (self = [super init]) {
  58. dispatcher = [[PTPusherEventDispatcher alloc] init];
  59. channels = [[NSMutableDictionary alloc] init];
  60. authorizationQueue = [[NSOperationQueue alloc] init];
  61. authorizationQueue.maxConcurrentOperationCount = 5;
  62. authorizationQueue.name = @"com.pusher.libPusher.authorizationQueue";
  63. self.connection = connection;
  64. self.connection.delegate = self;
  65. self.reconnectDelay = kPTPusherDefaultReconnectDelay;
  66. /* Three reconnection attempts should be more than enough attempts
  67. * to reconnect where the user has simply locked their device or
  68. * backgrounded the app.
  69. *
  70. * If there is no internet connection, we will only end up retrying
  71. * once as after the first failure we will no longer auto-retry.
  72. *
  73. * We may consider making this user-customisable in future but not
  74. * for now.
  75. */
  76. _maximumNumberOfReconnectAttempts = 3;
  77. }
  78. return self;
  79. }
  80. + (instancetype)pusherWithKey:(NSString *)key delegate:(id<PTPusherDelegate>)delegate
  81. {
  82. return [self pusherWithKey:key delegate:delegate encrypted:YES];
  83. }
  84. + (instancetype)pusherWithKey:(NSString *)key delegate:(id<PTPusherDelegate>)delegate encrypted:(BOOL)isEncrypted
  85. {
  86. return [self pusherWithKey:(NSString *)key delegate:(id<PTPusherDelegate>)delegate encrypted:(BOOL)isEncrypted cluster:(NSString *) nil];
  87. }
  88. + (instancetype)pusherWithKey:(NSString *)key delegate:(id<PTPusherDelegate>)delegate encrypted:(BOOL)isEncrypted cluster:(NSString *) cluster
  89. {
  90. NSString * hostURL;
  91. if ([cluster length] == 0) {
  92. hostURL = kPUSHER_HOST;
  93. } else {
  94. hostURL = [NSString stringWithFormat:@"ws-%@.pusher.com", cluster];
  95. }
  96. NSURL *serviceURL = PTPusherConnectionURL(hostURL, key, @"libPusher", isEncrypted);
  97. PTPusherConnection *connection = [[PTPusherConnection alloc] initWithURL:serviceURL];
  98. PTPusher *pusher = [[self alloc] initWithConnection:connection];
  99. pusher.delegate = delegate;
  100. return pusher;
  101. }
  102. - (void)dealloc;
  103. {
  104. [_connection setDelegate:nil];
  105. [_connection disconnect];
  106. }
  107. #pragma mark - Connection management
  108. - (void)setReconnectDelay:(NSTimeInterval)reconnectDelay
  109. {
  110. _reconnectDelay = MAX(reconnectDelay, 1);
  111. }
  112. - (void)connect
  113. {
  114. _numberOfReconnectAttempts = 0;
  115. self.autoReconnectMode = PTPusherAutoReconnectModeReconnectWithConfiguredDelay;
  116. [self.connection connect];
  117. }
  118. - (void)disconnect
  119. {
  120. // we do not want to reconnect if a user explicitly disconnects
  121. self.autoReconnectMode = PTPusherAutoReconnectModeNoReconnect;
  122. [self.connection disconnect];
  123. }
  124. #pragma mark - Binding to events
  125. - (PTPusherEventBinding *)bindToEventNamed:(NSString *)eventName target:(id)target action:(SEL)selector
  126. {
  127. return [dispatcher addEventListenerForEventNamed:eventName target:target action:selector];
  128. }
  129. - (PTPusherEventBinding *)bindToEventNamed:(NSString *)eventName handleWithBlock:(PTPusherEventBlockHandler)block
  130. {
  131. return [self bindToEventNamed:eventName handleWithBlock:block queue:dispatch_get_main_queue()];
  132. }
  133. - (PTPusherEventBinding *)bindToEventNamed:(NSString *)eventName handleWithBlock:(PTPusherEventBlockHandler)block queue:(dispatch_queue_t)queue
  134. {
  135. return [dispatcher addEventListenerForEventNamed:eventName block:block queue:queue];
  136. }
  137. - (void)removeBinding:(PTPusherEventBinding *)binding
  138. {
  139. [dispatcher removeBinding:binding];
  140. }
  141. - (void)removeAllBindings
  142. {
  143. [dispatcher removeAllBindings];
  144. }
  145. #pragma mark - Subscribing to channels
  146. - (PTPusherChannel *)subscribeToChannelNamed:(NSString *)name
  147. {
  148. PTPusherChannel *channel = channels[name];
  149. if (channel == nil) {
  150. channel = [PTPusherChannel channelWithName:name pusher:self];
  151. channels[name] = channel;
  152. }
  153. // private/presence channels require a socketID to authenticate
  154. if (self.connection.isConnected && self.connection.socketID) {
  155. [self subscribeToChannel:channel];
  156. }
  157. return channel;
  158. }
  159. - (PTPusherPrivateChannel *)subscribeToPrivateChannelNamed:(NSString *)name
  160. {
  161. return (PTPusherPrivateChannel *)[self subscribeToChannelNamed:[NSString stringWithFormat:@"private-%@", name]];
  162. }
  163. - (PTPusherPresenceChannel *)subscribeToPresenceChannelNamed:(NSString *)name
  164. {
  165. return (PTPusherPresenceChannel *)[self subscribeToChannelNamed:[NSString stringWithFormat:@"presence-%@", name]];
  166. }
  167. - (PTPusherPresenceChannel *)subscribeToPresenceChannelNamed:(NSString *)name delegate:(id<PTPusherPresenceChannelDelegate>)presenceDelegate
  168. {
  169. PTPusherPresenceChannel *channel = [self subscribeToPresenceChannelNamed:name];
  170. channel.presenceDelegate = presenceDelegate;
  171. return channel;
  172. }
  173. - (PTPusherChannel *)channelNamed:(NSString *)name
  174. {
  175. return channels[name];
  176. }
  177. /* This is only called when a client explicitly unsubscribes from a channel
  178. * by calling either [channel unsubscribe] or using the deprecated API
  179. * [client unsubscribeFromChannel:].
  180. *
  181. * This effectively ends the lifetime of a channel: the client will remove it
  182. * from it's channels collection and all bindings will be removed. If no other
  183. * code outside of libPusher has a strong reference to the channel, it will
  184. * be deallocated.
  185. *
  186. * This is different to implicit unsubscribes (where the connection has been lost)
  187. * where the channel will object will remain and be re-subscribed when connection
  188. * is re-established.
  189. *
  190. * A pusher:unsubscribe event will only be sent if there is a connection, otherwise
  191. * it's not necessary as the channel is already implicitly unsubscribed due to the
  192. * disconnection.
  193. */
  194. - (void)__unsubscribeFromChannel:(PTPusherChannel *)channel
  195. {
  196. NSParameterAssert(channel != nil);
  197. [channel removeAllBindings];
  198. if (self.connection.isConnected) {
  199. [self sendEventNamed:@"pusher:unsubscribe"
  200. data:@{@"channel": channel.name}];
  201. }
  202. [channels removeObjectForKey:channel.name];
  203. if ([self.delegate respondsToSelector:@selector(pusher:didUnsubscribeFromChannel:)]) {
  204. [self.delegate pusher:self didUnsubscribeFromChannel:channel];
  205. }
  206. }
  207. - (void)subscribeToChannel:(PTPusherChannel *)channel
  208. {
  209. [channel authorizeWithCompletionHandler:^(BOOL isAuthorized, NSDictionary *authData, NSError *error) {
  210. if (isAuthorized && self.connection.isConnected) {
  211. if ([self.delegate respondsToSelector:@selector(pusher:authorizationPayloadFromResponseData:)]) {
  212. authData = [self.delegate pusher:self authorizationPayloadFromResponseData:authData];
  213. }
  214. [channel subscribeWithAuthorization:authData];
  215. }
  216. else {
  217. if (error == nil) {
  218. error = [NSError errorWithDomain:PTPusherErrorDomain code:PTPusherSubscriptionUnknownAuthorisationError userInfo:nil];
  219. }
  220. if ([self.delegate respondsToSelector:@selector(pusher:didFailToSubscribeToChannel:withError:)]) {
  221. [self.delegate pusher:self didFailToSubscribeToChannel:channel withError:error];
  222. }
  223. }
  224. }];
  225. }
  226. - (void)subscribeAll
  227. {
  228. for (PTPusherChannel *channel in [channels allValues]) {
  229. [self subscribeToChannel:channel];
  230. }
  231. }
  232. #pragma mark - Sending events
  233. - (void)sendEventNamed:(NSString *)name data:(id)data
  234. {
  235. [self sendEventNamed:name data:data channel:nil];
  236. }
  237. - (void)sendEventNamed:(NSString *)name data:(id)data channel:(NSString *)channelName
  238. {
  239. NSParameterAssert(name);
  240. if (self.connection.isConnected == NO) {
  241. NSLog(@"Warning: attempting to send event while disconnected. Event will not be sent.");
  242. return;
  243. }
  244. NSMutableDictionary *payload = [NSMutableDictionary dictionary];
  245. payload[PTPusherEventKey] = name;
  246. if (data) {
  247. payload[PTPusherDataKey] = data;
  248. }
  249. if (channelName) {
  250. payload[PTPusherChannelKey] = channelName;
  251. }
  252. [self.connection send:payload];
  253. }
  254. #pragma mark - PTPusherConnection delegate methods
  255. - (BOOL)pusherConnectionWillConnect:(PTPusherConnection *)connection
  256. {
  257. if ([self.delegate respondsToSelector:@selector(pusher:connectionWillConnect:)]) {
  258. return [self.delegate pusher:self connectionWillConnect:connection];
  259. }
  260. return YES;
  261. }
  262. - (void)pusherConnectionDidConnect:(PTPusherConnection *)connection
  263. {
  264. _numberOfReconnectAttempts = 0;
  265. if ([self.delegate respondsToSelector:@selector(pusher:connectionDidConnect:)]) {
  266. [self.delegate pusher:self connectionDidConnect:connection];
  267. }
  268. [self subscribeAll];
  269. }
  270. - (void)pusherConnection:(PTPusherConnection *)connection didDisconnectWithCode:(NSInteger)errorCode reason:(NSString *)reason wasClean:(BOOL)wasClean
  271. {
  272. NSError *error = nil;
  273. if (errorCode > 0) {
  274. if (reason == nil) {
  275. reason = @"Unknown error"; // not sure what could cause this to be nil, but just playing it safe
  276. }
  277. NSString *errorDomain = PTPusherErrorDomain;
  278. if (errorCode >= 400 && errorCode <= 4099) {
  279. errorDomain = PTPusherFatalErrorDomain;
  280. }
  281. // check for error codes based on the Pusher Websocket protocol see http://pusher.com/docs/pusher_protocol
  282. error = [NSError errorWithDomain:errorDomain code:errorCode userInfo:@{@"reason": reason}];
  283. // 4000-4099 -> The connection SHOULD NOT be re-established unchanged.
  284. if (errorCode >= 4000 && errorCode <= 4099) {
  285. [self handleDisconnection:connection error:error reconnectMode:PTPusherAutoReconnectModeNoReconnect];
  286. } else
  287. // 4200-4299 -> The connection SHOULD be re-established immediately.
  288. if(errorCode >= 4200 && errorCode <= 4299) {
  289. [self handleDisconnection:connection error:error reconnectMode:PTPusherAutoReconnectModeReconnectImmediately];
  290. }
  291. else {
  292. // i.e. 4100-4199 -> The connection SHOULD be re-established after backing off.
  293. [self handleDisconnection:connection error:error reconnectMode:PTPusherAutoReconnectModeReconnectWithBackoffDelay];
  294. }
  295. }
  296. else {
  297. [self handleDisconnection:connection error:error reconnectMode:self.autoReconnectMode];
  298. }
  299. }
  300. - (void)pusherConnection:(PTPusherConnection *)connection didFailWithError:(NSError *)error wasConnected:(BOOL)wasConnected
  301. {
  302. if (wasConnected) {
  303. [self handleDisconnection:connection error:error reconnectMode:PTPusherAutoReconnectModeReconnectImmediately];
  304. }
  305. else {
  306. if ([self.delegate respondsToSelector:@selector(pusher:connection:failedWithError:)]) {
  307. [self.delegate pusher:self connection:connection failedWithError:error];
  308. }
  309. }
  310. }
  311. - (void)pusherConnection:(PTPusherConnection *)connection didReceiveEvent:(PTPusherEvent *)event
  312. {
  313. if ([event isKindOfClass:[PTPusherErrorEvent class]]) {
  314. if ([self.delegate respondsToSelector:@selector(pusher:didReceiveErrorEvent:)]) {
  315. [self.delegate pusher:self didReceiveErrorEvent:(PTPusherErrorEvent *)event];
  316. }
  317. }
  318. if (event.channel) {
  319. [channels[event.channel] dispatchEvent:event];
  320. }
  321. [dispatcher dispatchEvent:event];
  322. [[NSNotificationCenter defaultCenter]
  323. postNotificationName:PTPusherEventReceivedNotification
  324. object:self
  325. userInfo:@{PTPusherEventUserInfoKey: event}];
  326. }
  327. - (void)handleDisconnection:(PTPusherConnection *)connection error:(NSError *)error reconnectMode:(PTPusherAutoReconnectMode)reconnectMode
  328. {
  329. [authorizationQueue cancelAllOperations];
  330. for (PTPusherChannel *channel in [channels allValues]) {
  331. [channel handleDisconnect];
  332. }
  333. BOOL willReconnect = NO;
  334. if (reconnectMode > PTPusherAutoReconnectModeNoReconnect && _numberOfReconnectAttempts < _maximumNumberOfReconnectAttempts) {
  335. willReconnect = YES;
  336. }
  337. if ([self.delegate respondsToSelector:@selector(pusher:connection:didDisconnectWithError:willAttemptReconnect:)]) {
  338. [self.delegate pusher:self connection:connection didDisconnectWithError:error willAttemptReconnect:willReconnect];
  339. }
  340. if (willReconnect) {
  341. [self reconnectUsingMode:reconnectMode];
  342. }
  343. }
  344. #pragma mark - Private
  345. - (void)beginAuthorizationOperation:(PTPusherChannelAuthorizationOperation *)operation
  346. {
  347. [authorizationQueue addOperation:operation];
  348. }
  349. - (void)reconnectUsingMode:(PTPusherAutoReconnectMode)reconnectMode
  350. {
  351. _numberOfReconnectAttempts++;
  352. NSTimeInterval delay;
  353. switch (reconnectMode) {
  354. case PTPusherAutoReconnectModeReconnectImmediately:
  355. delay = 0;
  356. break;
  357. case PTPusherAutoReconnectModeReconnectWithConfiguredDelay:
  358. delay = self.reconnectDelay;
  359. break;
  360. case PTPusherAutoReconnectModeReconnectWithBackoffDelay:
  361. delay = self.reconnectDelay * _numberOfReconnectAttempts;
  362. break;
  363. default:
  364. delay = 0;
  365. break;
  366. }
  367. if ([self.delegate respondsToSelector:@selector(pusher:connectionWillAutomaticallyReconnect:afterDelay:)]) {
  368. BOOL shouldProceed = [self.delegate pusher:self connectionWillAutomaticallyReconnect:_connection afterDelay:delay];
  369. if (!shouldProceed) return;
  370. }
  371. dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC);
  372. dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
  373. [_connection connect];
  374. });
  375. }
  376. @end