Git Product home page Git Product logo

libpusher's Introduction

Pusher Channels Objective-C Client

Build Status

libPusher is an Objective-C client library for the Pusher Channels realtime service.

Pusher Channels is a hosted service that sits between your web application and the browser that lets you deliver events in realtime using WebSockets.

The libPusher API mirrors the Pusher Channels JavaScript client as closely as possible, with some allowances for Objective-C conventions. In particular, whilst the JavaScript client uses event binding for all event handling, where events are pre-defined, libPusher uses the standard Cocoa delegation pattern.

API Documentation

Example

Subscribe to the chat channel and bind to the new-message event.

// self.client is a strong instance variable of class PTPusher
self.client = [PTPusher pusherWithKey:@"YOUR_API_KEY" delegate:self encrypted:YES];

// subscribe to channel and bind to event
PTPusherChannel *channel = [self.client subscribeToChannelNamed:@"chat"];
[channel bindToEventNamed:@"new-message" handleWithBlock:^(PTPusherEvent *channelEvent) {
	// channelEvent.data is a NSDictianary of the JSON object received
    NSString *message = [channelEvent.data objectForKey:@"text"];
	NSLog(@"message received: %@", message);
}];

[self.client connect];

Supported platforms

Deployment targets

  • iOS 6.0 and above
  • macOS (OS X) 10.9 and above

Installation

Install using CocoaPods is recommended.

pod 'libPusher', '~> 1.6.4'

Import Pusher into the class that wants to make use of the library.

#import <Pusher/Pusher.h>

If you want to use the ReactiveExtensions version of libPusher, add the following line to your Podfile instead.

pod 'libPusher/ReactiveExtensions', '~> 1.6.4'

This will also load the core libPusher library and ReactiveCocoa as a dependency.

If you are not using CocoaPods, you can simply drop the extensions into your project.

Usage

Note: in the following examples, client is a strong property. The instance returned by the pusherWithKey:*: methods will be auto-released, according to standard Objective-C return conventions. You must retain the client otherwise it will be auto-released before anything useful happens causing silent failures and unexpected behaviour.

Create a client and connecting

self.client = [PTPusher pusherWithKey:@"YOUR_API_KEY" delegate:self encrypted:YES];

[self.client connect];

Note that clients do not connect automatically (as of version 1.5). You are responsible for calling connect as needed.

It is recommended to implement the PTPusherDelegate protocol in order to be notified when significant connection events happen such as connection errors, disconnects, and retries.

When the Pusher app is created in a different cluster to the default cluster, you must specify the cluster parameter.

self.client = [PTPusher pusehrWithKey:@"YOUR_API_KEY" delegate:self encrypt:YES cluster:@"YOUR_APP_CLUSTER"]

[self.client connect];

Subscribe to channels

Channels are a way of filtering the events you want your application to receive. In addition, private and presence channels can be used to control access to events and in the case of presence channels, see who else is subscribing to events. For more information on channels, see the documentation.

There is no need to wait for the client to establish a connection before subscribing. You can subscribe to a channel immediately and any subscriptions will be created once the connection has connected.

Subscribing to public channels

PTPusherChannel *channel = [self.client subscribeToChannelNamed:@"chat"];

Subscribing to private channels

This method will add the appropriate private- prefix to the channel name for you and return a channel cast to the correct PTPusherChannel subclass PTPusherPrivateChannel.

Subscribing to private channels needs server-side authorization. See section Channel authorization for details.

// subscribe to private-chat channel
PTPusherPrivateChannel *private = [self.client subscribeToPrivateChannelNamed:@"chat"];

Subscribing to presence channels

This method will add the appropriate presence- prefix to the channel name for you and return a channel cast to the correct PTPusherChannel subclass PTPusherPresenceChannel.

Subscribing to presence channels needs server-side authorization. See section Channel Authorization for details.

// subscribe to presence-chat channel
PTPusherPresenceChannel *presence = [client subscribeToPresenceChannelNamed:@"chat" delegate:self];

It is recommended to implement PTPusherPresenceChannelDelegate protocol, to receive notifications for members subscribing or unsubscribing from the presence channel.

Accessing subscribed channels

You can use the channelNamed: method to retrieve an existing subscribed channel. If you have not subscribed to the requested channel, it will return nil.

// get the 'chat' channel that you've already subscribed to
PTPusherChannel *channel = [self.client channelNamed:@"chat"];

Unsubscribe from channels

If you no longer want to receive events on a given channel, you can unsubscribe from it.

PTPusherChannel *channel = [self.client channelNamed:@"chat"];
[channel unsubscribe];

Channel object lifetime

When the client disconnects, all subscribed channels are implicitly unsubscribed (isSubscribed will return NO), however the channel objects will persist and so will any event bindings.

When the client reconnects, all previously subscribed channels will be resubscribed (which might involve another authentication request for any private/presence channels) and your existing event bindings will continue working as they did prior to the disconnection.

If you explicitly unsubscribe from a channel, all event bindings will be removed and the client will remove the channel object from its list of subscribed channels. If no other code has a strong reference to the channel object, it will be deallocated. If you resubscribe to the channel, a new channel object will be created. You should bear this in mind if you maintain any strong references to a channel object in your application code.

Channel authorization

Private and presence channels require server-side authorization before they can connect.

Note: Make sure your server responds correctly to the authentication request. See the authentication signature and user authentication docs for details and examples on how to implement authorization on the server side.

In order to connect to a private or presence channel, you first need to configure your server authorization URL.

self.client.authorizationURL = [NSURL URLWithString:@"https://www.yourserver.com/authorize"];

When you attempt to connect to a private or presence channel, libPusher will make a form-encoded POST request to the above URL, passing along the socket_id and channel_name as parameters. Prior to sending the request, the Pusher delegate will be notified, passing in the channel and the NSMutableURLRequest instance that will be sent.

Custom authorization

In order to perform custom authorization, you need to assign the channelAuthorizationDelegate and implement the PTPusherChannelAuthorizationDelegate protocol, which currently contains a single method. The following example uses the AFNetworking library to perform server-based authorization:

- (BOOL)applicationDidFinishLaunching:(UIApplication *)application
{
  ...
  self.pusherClient.channelAuthorizationDelegate = self;
}

- (void)pusherChannel:(PTPusherChannel *)channel requiresAuthorizationForSocketID:(NSString *)socketID completionHandler:(void(^)(BOOL isAuthorized, NSDictionary *authData, NSError *error))completionHandler
{
  NSDictionary *authParameters = :@{@"socket_id": socketID, @"channel_name": channel.name};

  [[MyHTTPClient sharedClient] postPath:@"/pusher/auth" parameters:authParameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
      completionHandler(YES, responseObject, nil);
  } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    completionHandler(NO, nil, error);
  }];
}

Binding to events

There are generally two ways to bind to events: binding to an event on the PTPusher client itself or binding to a specific channel.

Two types of direct binding are supported: target/action and block-based bindings. The examples below using block-based bindings.

Binding to a channel

Once you have created an instance of PTPusherChannel, you can set up event bindings. There is no need to wait for the PTPusher client connection to be established or the channel to be subscribed.

When you bind to events on a single channel, you will only receive events with that name if they are sent over this channel.

PTPusherChannel *channel = [self.client subscribeToChannelNamed:@"chat"];
[channel bindToEventNamed:@"new-message" handleWithBlock:^(PTPusherEvent *channelEvent) {
  // channelEvent.data is a NSDictionary of the JSON object received
}];

Binding directly to the client

When you bind to events on the client, you will receive all events with that name, regardless of the channel from which they originated.

[self.client bindToEventNamed:@"new-message" handleWithBlock:^(PTPusherEvent *event) {
  // event.data is a NSDictionary of the JSON object received
}];

Remove bindings

If you no longer want to receive events with a specific name, you can remove the binding. Removing a binding is as simple as storing a reference to the binding object, then passing that as an argument to removeBinding: at a later point.

Note: Binding objects are owned by the client or channel that they relate to and will exist for the lifetime of the binding. For this reason, you generally only need to store a weak reference to the binding object in order to remove the binding. In the event that something else removes the binding (perhaps as a result of calling removeAllBindings or explicitly unsubscribing from the channel), the weak reference will ensure that the binding object will become nil, so you should check this before calling removeBinding:.

// _binding is a weak reference
_binding = [self.client bindToEventNamed:@"new-message" target:self action:@selector(handleEvent:)];

// check that nothing else has removed the binding already
if (_binding) {
  [self.client removeBinding:_binding];
}

Memory management considerations for block-based bindings

Similar caveats apply to block-based bindings as they do to using block based NSNotificationCenter observers, i.e. when referencing self in your event handler block, it is possible in some situations to create retain cycles or prevent self from being deallocated.

When you reference self in your event handler block, the block will retain a strong reference to self. This means that self will never be deallocated until the binding (and in turn the event handler block) is destroyed by removing the binding. For this reason, you should be wary about removing event bindings in dealloc methods as dealloc will never be called if the binding references self.

For example, you might push a UIViewController on to a UINavigationController stack, then create an event binding in that view controller's viewDidAppear: method:

- (void)viewDidAppear:(BOOL)animated
{
  // _binding is a weak instance variable
  _binding = [self.client bindToEventNamed:@"new-message" handleWithBlock:^(PTPusherEvent *event) {
    [self doSomethingWithEvent:event];
  }];
}

If you were to then pop that view controller off the navigation stack without removing the event binding, because the binding block has a strong reference to self, the view controller will never be deallocated and you will have a memory leak.

You can handle this in one of two ways. The first is to ensure you remove the binding when in the corresponding viewDidDisappear:

- (void)viewDidDisappear:(BOOL)animated
{
  [self.client removeBinding:_binding];
}

The second, is to prevent a strong reference to self being captured in the first place:

- (void)viewDidAppear:(BOOL)animated
{
  __weak typeof(self) weakSelf = self;

  // _binding is a weak instance variable
  _binding = [self.client bindToEventNamed:@"new-message" handleWithBlock:^(PTPusherEvent *event) {
    __strong typeof(weakSelf) strongSelf = weakSelf;
    [strongSelf doSomethingWithEvent:event];
  }];
}

Finally, if you reference self in a block and store a strong reference to the binding object, you will create a retain cycle (self -> binding -> block -> self). You should avoid keeping strong references to binding objects but if you really need to, you should ensure you only capture a weak reference to self in the block as in the above example.

Binding to all events

In some cases it might be useful to bind to all events of a client or channel. libPusher will publish a NSNotification for every event received. You can subscribe to all events for a client or channel by adding a notification observer.

Binding to all events using NSNotificationCenter:

[[NSNotificationCenter defaultCenter]
          addObserver:self
             selector:@selector(didReceiveEventNotification:)
                 name:PTPusherEventReceivedNotification
               object:self.client];

Bind to all events on a single channel:

// get chat channel
PTPusherChannel *channel = [self.client channelNamed:@"chat"];

[[NSNotificationCenter defaultCenter]
          addObserver:self
             selector:@selector(didReceiveChannelEventNotification:)
                 name:PTPusherEventReceivedNotification
               object:channel];

The event can be retrieved in your callback from the notification's userInfo dictionary. The notification's object will be either the client or channel from which the event originated.

- (void)didReceiveEventNotification:(NSNotification *)notification
{
	PTPusherEvent *event = [notification.userInfo objectForKey:PTPusherEventUserInfoKey];
}

Handling network connectivity errors and disconnects

The nature of a mobile device is that connections will come and go. There are a number of things you can do do ensure that your Pusher connection remains active for as long as you have a network connection and reconnects after network connectivity has been re-established.

Automatic reconnection behaviour

libPusher will generally try and do its best to keep you connected in most cases:

  • If the connection fails having been previously connected, the client will try and reconnect immediately.
  • If the connection disconnects with a Pusher error code in the range 4200-4299, the client will try and reconnect immediately.
  • If the connection disconnects with a Pusher error code in the range 4100-4199, the client will try and reconnect with a linear back-off delay.
  • If the connection disconnects for an unknown reason, the client will try and reconnect after a configured delay (defaults to 5 seconds and can be changed using the reconnectDelay property).

All automatic reconnection attempts will be repeated up to a maximum limit before giving up.

Automatic reconnection will not happen in the following situations:

  • The connection fails on the initial attempt (i.e. not previously connected)
  • The connection disconnects with a Pusher error code in the range 4000-4099 (indicating a client error, normally a misconfiguration)
  • The maximum number of automatic reconnection attempts have been reached

An error code in the range 4000-4099 generally indicates a client misconfiguration (e.g. invalid API key) or rate limiting. See the Pusher protocol documentation for more information.

The other scenarios generally indicate that it is not currently possible to connect to the Pusher service - this might be because of an issue with the service but more likely is that there simply isn't an internet connection.

Up to version 1.6, automatic reconnection would happen after the configured reconnectDelay even after explicitly calling disconnect. This behaviour was undesirable and in all subsequent versions this no longer happens and an explicit call to connect is required to reconnect in this case.

Handling disconnections

If the client fails to connect at all, the delegate method pusher:connection:failedWithError: will be called and no automatic reconnection will be attempted.

If the client disconnects, the delegate method pusher:connection:didDisconnectWithError:willAttemptReconnect: will be called. If willAttemptReconnect is YES, you don't have any further work to do.

If willAttemptReconnect is NO, you should first check the error to see if there is a client misconfiguration. If the client is refusing to automatically reconnect due to a Pusher error code, the NSError will have a domain of PTPusherFatalErrorDomain.

How you handle disconnections is up to you, but the general idea is to check if there is network connectivity and if there is not, wait until there is before reconnecting.

It is recommended that you let the OS handle connection management. In practical terms this means that if you want to ensure that a client won't have any further messages sent to it when they have backgrounded the app then you should call unsubscribe on the relevant channel(s) as opposed to calling disconnect. Eventually the connection will be cleaned up (and therefore closed) by the OS. Trying to explicitly call disconnect yourself upon backgrounding can lead to unexpected behaviour with multiple connections then being opened upon the app being foregrounded again.

Example: handling disconnections using the Reachability library

In this example, we first check for any fatal Pusher errors, before using Reachability to wait for an internet connection to become available before manually reconnecting.

- (void)pusher:(PTPusher *)pusher connection:(PTPusherConnection *)connection failedWithError:(NSError *)error
{
  [self handleDisconnectionWithError:error];
}

- (void)pusher:(PTPusher *)pusher connection:(PTPusherConnection *)connection didDisconnectWithError:(NSError *)error willAttemptReconnect:(BOOL)willAttemptReconnect
{
  if (!willAttemptReconnect) {
    [self handleDisconnectionWithError:error];
  }
}

The implementation of handleDisconnectionWithError performs the error check and waits for Reachability to change:

- (void)handleDisconnectionWithError:(NSError *)error
{
  Reachability *reachability = [Reachability reachabilityWithHostname:self.client.connection.URL.host];

  if (error && [error.domain isEqualToString:PTPusherFatalErrorDomain]) {
    NSLog(@"FATAL PUSHER ERROR, COULD NOT CONNECT! %@", error);
  }
  else {
    if ([reachability isReachable]) {
      // we do have reachability so let's wait for a set delay before trying again
      [self.client performSelector:@selector(connect) withObject:nil afterDelay:5];
    }
    else {
      // we need to wait for reachability to change
      [[NSNotificationCenter defaultCenter] addObserver:self
                                               selector:@selector(_reachabilityChanged:)
                                                   name:kReachabilityChangedNotification
                                                 object:reachability];

      [reachability startNotifier];
    }
  }
}

- (void)_reachabilityChanged:(NSNotification *note)
{
  Reachability *reachability = [note object];

  if ([reachability isReachable]) {
    // we're reachable, we can try and reconnect, otherwise keep waiting
    [self.client connect];

    // stop watching for reachability changes
    [reachability stopNotifier];

    [[NSNotificationCenter defaultCenter]
        removeObserver:self
                  name:kReachabilityChangedNotification
                object:reachability];
  }
}

For a more sophisticated implementation of handling client disconnections and to see how this integrates with a real application, you could take a look at the ClientDisconnectionHandler class in the official Pusher iOS Diagnostics app.

License

All code is licensed under the MIT license. See the LICENSE file for more details.

libpusher's People

Contributors

alexbosworth avatar alloy avatar aschuch avatar avocade avatar ay8s avatar bdolman avatar codecaffeine avatar dependabot[bot] avatar fillito avatar hamchapman avatar idyll avatar jameshfisher avatar jannash avatar keith avatar leocassarani avatar lukeredpath avatar mackuba avatar marcelosalloum avatar mloughran avatar nero-tang avatar rantav avatar rex-remind101 avatar rickfillion avatar teameh avatar tomkemp avatar tonyxiao avatar topliceanu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

libpusher's Issues

Channel authorisation crashes if server returns auth data as an array

When the authorisation response is read from the server, it is assumed that the parsed JSON object is an NSDictionary.

If the server implementation is faulty and an NSArray is returned instead, this will alter cause a crash when we try to manipulate the auth data by calling, for instance, setObject:forKey, when we set the channel name in PTPusherPrivateChannel.

If it is valid for the server to return an array, the code should handle this. If it is not, then we should consider adding an assertion that we have an dictionary and if not, crash with a more useful error message.

Support removal of all bindings with a simple API

v1.2 introduced the ability to remove bindings by storing a reference to the PTPusherEventBinding object returned from the binding methods.

It's essential the user store a reference to these binding objects if they want to remove specific bindings later.

However, the library should be able to support removing all bindings with one method call by keeping track of bindings itself. Then a user could do:

// this could be channel too
[pusher removeAllBindings];

Outdated download README

In the readme included in the zip file of compiled builds, it gives instructions about JSONKit but the readme on the repo says it uses Apple's. It would be nice if they were in sync.

Also, in either readme, it would be great if they included instructions regarding which libraries you are required to link.

Crash in -[PTPusherChannelAuthorizationOperation finish]

I'm seeing the following in my crash logs:

Invalid State: Cannot call send: until connection is open

Here's the stack:

...Assertion handler...
-[SRWebSocket send:] + 605
-[PTPusherConnection send:] PTPusherConnection.m line 80
-[PTPusher sendEventNamed:data:channel:] PTPusher.m line 233
-[PTPusherChannel unsubscribe] PTPusherChannel.m line 153
-[PTPusher unsubscribeFromChannel:] PTPusher.m line 186

Cannot reconnect pusher after connection failedWithError after screenlock

On iOS 5.0, when running the app locking the screen (vs. multitasking) will cause a connection failedWithError upon unlocking the device:

[pusher-6094.30832] Failed to connect to pusher, error: Error Domain=NSPOSIXErrorDomain Code=57 "The operation couldn’t be completed. Socket is not connected" UserInfo=0xbde580 {}

I'm guessing iOS is closing the socket.

However, at this point [[pusher connection] isConnected] remains as 1. And [pusher connect] has no effect.

To work around this I tried:

  1. [pusher disconnect] to try to close the socket (such that isConnected would return NO)

isConnected is still YES at this point. Looking at the libPusher source, isConnected is only set to NO if the websockets didClose delegate is called. Thus this could be a bug in the websocket?

  1. [pusher connect]

At this point I do not receive connectionDidConnect or connection FailedWithError, so I can only guess that [pusher connect] was ignored.

Segfault on OS X

Hey,

Seems like libPusher segfaults on Snow Leopard. The full crash report can be found here: travis-ci/travis-watcher-macosx#3.

Seems like this could be caused by an instance of SRWebSocket not being retained. Looking at the code, it seems like the SRWebSocket instance is just being assigned to an instance variable, which to the best of my knowledge does not retain the instance. I'm not too familiar with Objective-C, though. Is there a reason this isn't assigned with a @property setter? I'd love to submit a pull request if it should be changed, but if it's actually supposed to be this way, then I won't.

Can't Connect Due to NULL theReadStream

Hi,

First of all, thanks for this library! Unfortunately, I'm having trouble connecting.

When I execute the following code...

// Init pusher
NSLog(@"%@", @"Connecting to pusher...");
pusher = [PTPusher pusherWithKey:PUSHER_API_KEY delegate:self encrypted:YES];
pusher.reconnectAutomatically = YES;
[pusher connect];

I get the following error...

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: theReadStream != NULL'
*** Call stack at first throw:
(
0 CoreFoundation 0x0196b5a9 __exceptionPreprocess + 185
1 libobjc.A.dylib 0x01abf313 objc_exception_throw + 44
2 CoreFoundation 0x01923ef8 +[NSException raise:format:arguments:] + 136
3 Foundation 0x0165a3bb -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 116
4 [my app] 0x0005e502 -[AsyncSocket doCFReadStreamCallback:forStream:] + 242
5 [my app] 0x00057cf9 MyCFReadStreamCallback + 169

I've double checked everything and tried a few different parameters. Any ideas?

Thanks!
Devin

Feature request: Add `unsubscribe` to channel

It would be great to have unsubscribe as a method on channel so you could do:

[self.someChannel unsubscribe];

This way you can safely message nil and everything is fine instead of checking for nil and calling the Pusher client.

Any chance to have a patch for presence/private channel?

As title, we have a project that another developer is doing the iOS frontend but we need private/presence channel. The iOS guy is not particularly familiar with pusher, so if anyone can contrib. a patch would be wonderful.

v1.3 Fails to Call Delegate Methods or Connect to Channels

I downloaded libPusher-iOS-v1.3.zip — Built from d4d51f8 at 30/03/20121 and it is very broken.

The delegate methods are not called when connecting. I can see the client connect on the Pusher debug console though. Subscribing to a channel fails silently and never actually works. I reverted back to an older version and things started to work. (I did noticed if you just [PTPusher pusherWithKey:... and don't anything else with it, you get an over release, but that's another issue.)

It would be great if you fixed this. I tried to add the project to Xcode so I could debug, but it appears that I need cocoapods and getting that installed is an enormous amount of setup work just do debug this. I'd really appreciate a fix.

Thanks Luke.

After connection fails not resubscribing to channels

In the current build, if a connection fails, the channels PTPusher was subscribed to are not marked as unsubscribed, meaning they will not be resubscribed to once the connection is re-established.

I consider this a bug, because even calling - subscribeToChannelNamed: will not re-subscribe to the given channel, only unsubscribing from it (which serves no purpose as the connection is new) and then subscribing works.

I haven't noticed this behaviour if disconnecting properly, as in those cases I explicitly unsubscribe before disconnecting.

Auth errors not getting bubbled up

If the authentication operation fails at the

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error

level, the error is not getting bubbled back up the chain so the caller has no idea if something went wrong. I accidentally mistyped the hostname for my authentication URL and couldn't figure out why nothing was happening.

libPusherMac

Hello,

libPusher looks great. But is it still supported on OSX? It's mentioned in the project description paragraph but the the library as-is doesn't seem to work on OSX?

Looks like the pre-compiled library is only compiled for iOS.

ld: warning: ignoring file foo/bar/libPusher-combined.a, missing required architecture x86_64 in file

Tried compiling from source, which has libPusherMac as a target in the drop down but seems to be missing the actual target - not listed with the others and the build button is greyed out.

I'm no expert when delving into these kind of compiling situations, so am not sure if I'm missing something obvious or attempting something misguided.

Be great to know one way or the other and some pointers if it should.

Thanks again for your time on the project and reading this.

Toby

possible ios versions > 3.1.1 bugs

I am having crashes on an app that i am trying to develop.
These crashes never happen on the simulator which is set to 3.1.3 since i am running xcode over leopard 10.6.4, they only happen when i try to install the app on my iphone or ipad which run ios version 4.1.

please help.

pusher:didSubscribeToChannel: not called on public channels?

Neither the 1.2 or latest 1.3 version of libPusher appear to 1) call the Pusher delegate's "pusher:didSubscribeToChannel" method or, 2) set the "subscribed" property of the channel in the specific case where the channel is a PUBLIC channel. Both these actions are done in "PTPusherChannel:handleSubscribeEvent" but this doesn't appear to be called since "pusher_internal:subscription_succeeded" is not received.

Additionally, in 1.2 the Pusher delegate method "pusherConnectionDidConnect" was called in webSocket:didReceiveMessage upon receiving a "PTPusherConnectionEstablishedEvent" event and AFTER the socketId property was set from the event's data. In the latest 1.3 its now called as soon as the web socket is opened and BEFORE the socketId is actually set. webSocket:didReceiveMessage now calls didReceiveHandshakeEvent" once the connection is established and socketId is set but unfortunately there is no Pusher delegate method for this stage (my app needs to know when the socketid for a connection is set).

I would have thought that 1) "pusherConnectionDidConnect" would not be called until socketId was set (as was the case in v1.2) and, 2) that "pusher:didSubscribeToChannel" would be called for PUBLIC channels (not just private/presence).

Am I missing something or are these bugs?

Thanks,

Dave

Unsubscribed channels are not released

Luke:

I am using libPusher build 7f7bf1b, which is the one named version 1.1, just before you ARC-enabled the library. I am using this library since my current project is not ARC-enabled.

I am attempting to subscribe and unsubscribe from authenticated private channels. Subscribing to channels and binding to events works fine. When I unsubscribe from channels, everything seems to work fine. It appears, however, that all of the channels remain in memory.

Consequently, when the async connection drops and attempts to reconnect, it will attempt to reauthenticate and resubscribe to all the channels stored in the client - including all of the ones that were previously unsubscribed.

Additionally, every time resubscribe to a new channel, the 'subscription-succeeded' event triggers handlers for every channel - including ones that were previously unsubscribed.

Have you noticed this behaviour? Is there a way to fix?

Ideally, when I unsubscribe from a channel, I would like to permanently remove all objects and events associated with that channel.

Your help would be greatly appreciated.

Many thanks,
Tim

Strange behavior on connect

Hi! I have such code in my App Delegate:

PTPusher *client = [PTPusher pusherWithKey:@"xxx" connectAutomatically:NO encrypted:NO];
  client.delegate = self;
  [client connect];

  PTPusherChannel *channel = [client subscribeToChannelNamed:@"test_channel"];

  [channel bindToEventNamed:@"channel_event" handleWithBlock:^(PTPusherEvent *channelEvent) {
    NSLog(@"%@", channelEvent);
  }];

Also, I implemented PTPusherDelegate protocol on my App Delegate.

When I start my app up, I see such a message at Pusher.com console — http://cl.ly/FU8L

So, it connects ok, but not joins public channel. Moreover, none of:

- (void)pusher:(PTPusher *)pusher connectionDidConnect:(PTPusherConnection *)connection
- (void)pusher:(PTPusher *)pusher connection:(PTPusherConnection *)connection failedWithError:(NSError *)error
- (void)pusher:(PTPusher *)pusher didSubscribeToChannel:(PTPusherChannel *)channel
- (void)pusher:(PTPusher *)pusher didReceiveErrorEvent:(PTPusherErrorEvent *)errorEvent
- (void)pusher:(PTPusher *)pusher didFailToSubscribeToChannel:(PTPusherChannel *)channel withError:(NSError *)error

delegate's method being executed :-(

I am new to Obj-c and maybe it is common error? Can you help me please?

Thanks in advance.

Missing Files

Hi

The repository appears to be missing some key files.

For example: Constants.h and CJSONDeserializer.h

cheers

Brett

Undefined Reachability Symbols

When adding version 1.2 to my project and compiling, I get 21 errors because of missing Reachability symbols in SRWebSocket.

Steps to recreate:

  • Download v1.2 from the Downloads page.
  • Create an empty iOS Xcode project
  • Add framework according to README.
  • Compile

No Connection?

This is highly likely my own fault, but I did not know where else to post this question.

I was experiencing this issue with a build of libPusher from early January, and to just verify I am still having this same problem with a build from yesterday.

Essentially, it does not appear to be connecting or receiving any events.

I initialize PTPusher as follows:

client = [PTPusher pusherWithKey:@"API_KEY" delegate:self];

My class implements PTPusherDelegate, additionally I've provided implementations for:

- (void)pusher:(PTPusher *)client connectionDidConnect:(PTPusherConnection *)connection;
- (void)pusher:(PTPusher *)pusher connection:(PTPusherConnection *)connection failedWithError:(NSError *)error;

My implementation of each does nothing more than NSLog so I can see if it was even connecting.

Back in my code, right after I initialized PTPusher, I went on to wire up some event handlers:

channel = [client subscribeToChannelNamed:@"test"];
[channel bindToEventNamed:@"test" target:self action:@selector(honk:)];

[channel bindToEventNamed:@"test2" handleWithBlock:^(PTPusherEvent *event) {
    NSLog(@"test2 received");
}];

The strange thing is that none of my NSLogs, event in the connectionDidConnect and connection: failedWithError: fire.

Am I correct in assuming that either connectionDidConnect or connection: failedWithError: should be firing (at the least)? It would seem that either I connected, or failed :-)

Crash if the server is unreachable

If you try to join a private channel when the authentication server is unreachable, the client will crash with the following stack:

-[PTNSJSONParser objectFromJSONData:]
-[PTPusherChannelAuthorizationOperation finish]
-[PTURLRequestOperation connection:didFailWithError:]
... NSURLConnectionInternal ...

Am I responsible to not join channels if there is no reachability?

PTUTLRequestOperation does not work on other Queues

If the connect method on PTPusher is called from anywhere but the main queue and you have an authorization url set, then the start method in PTURLRequestOperation may cause the application to halt (not crash) because of this call "[[NSRunLoop currentRunLoop] run];"

Removing [[NSRunLoop currentRunLoop] run]; fixes the problem but I don't know what the side effect are.

I don't know if this is a bug or if it just needs to be documented or neither.

Missing Header in Build

I noticed that you created builds for libPusher. Thank you!

I was experiencing a small problem with my current version, so I decided I'd give the latest version of the code a try and see if what I'm experienced resolved itself.

In the process of setting up libPusher using your new pre-built distribution I noticed what appears to be a missing header.

If you simply download "libPusher-iOS-v1.1.zip", and install per the instruction, you'll (at least I did) get an error. The compiler cannot resolved an import at the top of PTPusher.h, line 12 to be specific.

 #import "PTPusherEventPublisher.h"

If I download the raw source code, and then add this additional header my project compiles without any difficulties.

Posting data from client

Trying to post a simple string to the server from my OS X client, but it's just not doing anything at all. Not sure where I'm going wrong here.

- (void)sendEventWithMessage:(NSString *)message;
{
    // construct a simple payload for the event
    NSDictionary *payload = [NSDictionary dictionaryWithObjectsAndKeys:message, @"song-info", nil];

    // send the event
    [self performSelector:@selector(sendEvent:) withObject:payload];

}

- (void)sendEvent:(id)payload;
{
    if (self.pusherAPI == nil) {
        PTPusherAPI *api = [[PTPusherAPI alloc] initWithKey:PUSHER_API_KEY appID:PUSHER_APP_ID secretKey:PUSHER_API_SECRET];
        self.pusherAPI = api;
    }
    NSLog(@"%@", payload);
    // we set the socket ID to nil here as we want to receive the events that we are sending
   [self.pusherAPI triggerEvent:@"my_event" onChannel:@"test_channel" data:payload socketID:nil];


}

-(IBAction)pushTrackToServer:(id)sender
{

    NSString *trackInfo = [NSString stringWithFormat:@"%@ - %@", [self.sonora track], [self.sonora artist]];
    [self sendEventWithMessage:trackInfo];
}

The NSLog is displaying the right data, so I'm not sure what's wrong. There's little to no documentation on sending data from the client, so I'm a little lost.

Crash on member_added

hi,

First, thanks a lot for the library!

My iOS app is crashing whenever my presence channel receives "pusher:member_added" event.
In debug console, it says ...

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFDictionary setObject:forKey:]: attempt to insert nil value (key: 2254)'
*** First throw call stack:
(0xb3e022 0x226bcd6 0xae6a48 0xae69b9 0xb3d2da 0x68b4 0xb3fe42 0x79ce 0x7817 0x7695 0x57b0 0x41b3 0x7169 0x18f38 0x2160330 0x2162509 0xa75803 0xa74d84 0xa74c9b 0x28f87d8 0x28f888a 0x1107626 0x1caf5 0x1ca99)
terminate called throwing an exception

And it's pointing at [PTPusherPresenceChannel handleMemberAddedEvent:]
Do you have any idea I can fix this or find some detour?

Thanks!

Using an iPod2 and iOS 4.2.1 results in connection failure

When trying to createClientWithAutomatic connection on iPod Touch 2 with iOS 4.2.1, the following NSError event is received.

[pusher-(null)] Failed to connect to pusher, error: Error Domain=NSOSStatusErrorDomain Code=-9807 "The operation couldn’t be completed. (OSStatus error -9807.)" UserInfo=0xb65ae0 {}

UserInfo is an empty dictionary.

Might this possibly be because sockets aren't supported? Or is this a bug with SocketRocket?

UIScrollView gets stuck if touched when PTURLRequestOperation executes

I noticed that there is a quirk WRT UIScrollView and the libPusher. I'm not sure whether that's actually a bug or my misusage of the lib.

What happens is that if a user scrolls a UIScrollView (or a UITableView) while [PTURLRequestOperation start] is executing, this UIScrollView gets 'stuck' and becomes unusable.

At PTURLRequestOperation.m we have:

- (void)start 
{
  NSAssert(URLRequest, @"Cannot start URLRequestOperation without a NSURLRequest.");

  [self setExecuting:YES];

  URLConnection = [[NSURLConnection alloc] initWithRequest:URLRequest delegate:self startImmediately:NO];

  if (URLConnection == nil) {
    [self setFinished:YES]; 
  }

  // Common modes instead of default so it won't stall uiscrollview scrolling
  [URLConnection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
  [URLConnection start];

  do {
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
  } while (!_isFinished); 
}

If, by complete coincidence, the user scrolls a UIScrollView while this method is executing and keeps his fingers on it for a while, then the UIScrollView is left in a miserable state... Unusable as a matter of fact. Stuck...

I see per the comment about uiscrollview that the code is aware of this and tries to prevent it, alas, from my testing, this prevention doesn't work and it's actually easy enough if you have the right timing in your fingers to render the scrollviews unusable.

I admit I'm a complete ignorant WRT run loops et al. Of course, I may be doing something wrong elsewhere... but what I can say is that if I comment out the while loop, then the scrollviews are fine, but if the method stays as is and scrolling takes place, then the scrollview is left unusable, doesn't respond to events as it should and in short, simply unusable.

any tips?

Support on 3G network?

Hi,

Great library.

I have been playing with the sample app. It works great on Wifi network but failed when I switch to 3G network. The app shows the following error. I tested it with two different 3G network and it shows the same error. Any idea on how to overcome this? Thanks a lot.

2010-12-25 17:00:40.246 SampleApp[20622:307] Pusher failed with error Error Domain=ZTWebSocketErrorDomain Code=2 "The operation couldn’t be completed.

Dreamvx

3G support

Hi,
I have strange issue with 3G.
In wifi, the libpusher works without problem but in 3G I receive the following error :

2011-10-31 16:20:15.561 transcovo[308:707] [pusher-(null)] Failed to connect to pusher, error: Error Domain=ZTWebSocketErrorDomain Code=2 "The operation couldn’t be completed. (ZTWebSocketErrorDomain error 2.)"
2011-10-31 16:20:15.563 transcovo[308:707] [pusher-(null)] Reconnecting after 5 seconds...
2011-10-31 16:20:20.647 transcovo[308:707] *** Terminating app due to uncaught exception 'AsyncSocketException', reason: 'Attempting to connect while connected or accepting connections. Disconnect first.'
*** Call stack at first throw:
(
0 CoreFoundation 0x3144e64f __exceptionPreprocess + 114
1 libobjc.A.dylib 0x34f69c5d objc_exception_throw + 24
2 CoreFoundation 0x3144e491 +[NSException raise:format:arguments:] + 68
3 CoreFoundation 0x3144e4cb +[NSException raise:format:] + 34
4 transcovo 0x0001b45d -[AsyncSocket connectToHost:onPort:withTimeout:error:] + 172
5 transcovo 0x00039a4d -[ZTWebSocket open] + 220
6 transcovo 0x000164ad -[PTPusherConnection connect] + 72
7 transcovo 0x00013c0d __-[PTPusher reconnectAfterDelay]_block_invoke_1 + 52
8 libdispatch.dylib 0x35d348e7 _dispatch_call_block_and_release + 10
9 libdispatch.dylib 0x35d30771 _dispatch_after_timer_callback + 12
10 libdispatch.dylib 0x35d32341 _dispatch_source_invoke + 436
11 libdispatch.dylib 0x35d30041 _dispatch_queue_invoke + 92
12 libdispatch.dylib 0x35d301c3 _dispatch_main_queue_callback_4CF$VARIANT$up + 262
13 CoreFoundation 0x3142893b __CFRunLoopRun + 1334
14 CoreFoundation 0x313b8ec3 CFRunLoopRunSpecific + 230
15 CoreFoundation 0x313b8dcb CFRunLoopRunInMode + 58
16 GraphicsServices 0x30d3741f GSEventRunModal + 114
17 GraphicsServices 0x30d374cb GSEventRun + 62
18 UIKit 0x314ded69 -[UIApplication _run] + 404
19 UIKit 0x314dc807 UIApplicationMain + 670
20 transcovo 0x00003eb3 main + 82
21 transcovo 0x00003e5c start + 40
)
terminate called after throwing an instance of 'NSException'

The xcode debugger show me the following method in class "AsyncSocket.m" :

  • (BOOL)connectToHost:(NSString )hostname
    onPort:(UInt16)port
    withTimeout:(NSTimeInterval)timeout
    error:(NSError *
    )errPtr
    {
    if(theDelegate == NULL)
    {
    [NSException raise:AsyncSocketException
    format:@"Attempting to connect without a delegate. Set a delegate first."];
    }

    if(theSocket4 != NULL || theSocket6 != NULL)
    {
    //CRASH HERE

    [NSException raise:AsyncSocketException
                format:@"Attempting to connect while connected or accepting connections. Disconnect first."];
    

    }

    if(![self createStreamsToHost:hostname onPort:port error:errPtr]) goto Failed;
    if(![self attachStreamsToRunLoop:nil error:errPtr]) goto Failed;
    if(![self configureStreamsAndReturnError:errPtr]) goto Failed;
    if(![self openStreamsAndReturnError:errPtr]) goto Failed;

    [self startConnectTimeout:timeout];
    theFlags |= kDidPassConnectMethod;

    return YES;

Failed:
[self close];
return NO;
}

Do you have an idea why I have this issue ?

I also show, there were an issue like this and it was fix with the SSL. How can I set to use SSL instead ?

Crashes when sending event within example iphone project

Due to non-nil:ed out NSError in PTPusherClientOperation#main, sends garbage value to delegate if doesn't get proper error object back from [NSURLConnection sendSynchronousRequest]. Easy fix:

NSError *error = nil;

Good idea to always nil out objects before use.

Great lib, thanks!

Calling unsubscribeFromChannel results in an NSAssert from SRWebsocket

When the socket is no longer (or was never) connected, and PTPusher unsubscrbeFromChannel is called, an exception is thrown from SRWebSocket's send method.

You might consider this a deficiency in the SRWebSocket api...
https://github.com/square/SocketRocket/blob/master/SocketRocket/SRWebSocket.m line 615 send:

As a workaround I'm going to use PTPusher's connection.isConnected property, but I'm going to have to assume that its state is consistent with SRWebSocket's readyState.

Static analyzer warning

I'm using XCode 4.2 and the LLVM 3.0 compiler, and when the static analyzer runs it complains about AsyncSocket.m line 935.

The line is this:

934: struct sockaddr_in6 *pSockAddr6 = (struct sockaddr_in6 *)[address6 bytes];
935: pSockAddr6->sin6_port = htons(chosenPort);

The warning is "Access to field 'sin6_port' results in a dereference of a null pointer".

The only way I could see to tell the silly analyzer this isn't the case is to add a test:

934: struct sockaddr_in6 *pSockAddr6 = (struct sockaddr_in6 *)[address6 bytes];
935: if (pSockAddr6)
936: pSockAddr6->sin6_port = htons(chosenPort);

I don't know how to use GitHub well enough to submit this code :-p sorry.

PTPusherEvent fails to return data if it is not in JSON format

When an event arrives, PTPusherEvent attempts to decode it as JSON. If the data is not JSON, it should just return the data as a string: https://github.com/lukeredpath/libPusher/blob/master/Library/PTPusherEvent.m#L34

When objectFromJSONString fails to parse the data, it returns nil. However, the check on line 30 isn't looking at _data at all, and is expecting an NSError (error), which is never created.

I was able to fix the issue by changing line 30 to if (_data == nil). Is there something I am missing here, or should I submit a pull request with a fix?

Adam

test project with static library fails to build :(

I have problems with static library too. I don't know why.
I made all from readme.
This is sample project: http://cl.ly/2V3C1V161g3Z25003K0P

Also, on some other project (real project) I tried to implement static library and it builds with simple import.
But, when I try to init client, it trows "unknown object for key" exception.

I appreciate any kind of help.
Thx

Verizon iPhone Compatibility

Hey Luke,
I'm noticing an issue with connecting to a pusher channel on the Verizon iPhone 4/4S with my iOS app using your previous version of libPusher. Connecting to a pusher channel works if the Verizon iPhone is on WIFI which leads me to believe that it has something to do with Verizon's network. Do you have any suggestions on what might be the problem?

Thanks.
Adam

Static Library for OS X Build

I've built a command line tool, and I need to implement libPusher with it. Since it has no Framework directory of any sort, I've read that I need to link a static library to it instead. Is there anyway we could have a static library for OS X? Thanks!

EDIT: I'm totally aware anyone with the source code can technically build a static lib, I just have 0 experience in the matter.

Support [channel unsubscribe]

Currently, to unsubscribe from a channel requires passing a channel object into [PTPusher unsubscribeFromChannel:].

This API requires that a channel should never be nil (it will currently crash). I think this is correct behaviour and should be enforced with an assertion, but it does make things slightly more awkward for the library user as they must do an explicit nil check before calling unsubscribe.

An alternative or complementary API would be to support calling [channel unsubscribe] directly. This would avoid the need for a nil check as [nil unsubscribe] would be a no-op, as per Objective-C calling conventions.

I would greatly prefer to have this API rather than having [PTPusher unsubscribeFromChannel:] perform a nil check and ignore it silently.

The main hurdle in supporting this API is that unsubscribing from a channel currently has responsibilities in both PTPusher (removing the channel from the subscription list) and PTPusherChannel (sending the unsubscribe message to the server). Some refactoring is needed here to consolidate this logic such that an unsubscribe could be triggered from either side of the API.

pod install fails

$ pod --version                                                                                                                                                                                                                                              
0.5.1
$ pod install --verbose                                                                                                                                                                                                                                       master
Updating spec repo `master'
/usr/local/bin/git pull
Already up-to-date.
Installing dependencies of: /Users/samsoffes/Desktop/libPusher/Podfile
  * Pre-downloading: `SocketRocket'
/usr/local/bin/git clone 'git://github.com/square/SocketRocket.git' '/Users/samsoffes/Desktop/libPusher/Pods/SocketRocket'
fatal: destination path '/Users/samsoffes/Desktop/libPusher/Pods/SocketRocket' already exists and is not an empty directory.
  * Pre-downloading: `Kiwi'
/usr/local/bin/git clone 'git://github.com/allending/Kiwi.git' '/Users/samsoffes/Desktop/libPusher/Pods/Kiwi'
fatal: destination path '/Users/samsoffes/Desktop/libPusher/Pods/Kiwi' already exists and is not an empty directory.
Using Kiwi (1.0.0)
Using Reachability (3.0.0)
Using SocketRocket (0.1)
Generating support files
* Generating xcconfig file at `/Users/samsoffes/Desktop/libPusher/Pods/Pods.xcconfig'
* Generating prefix header at `/Users/samsoffes/Desktop/libPusher/Pods/Pods-prefix.pch'
* Generating copy resources script at `/Users/samsoffes/Desktop/libPusher/Pods/Pods-resources.sh'
* Generating xcconfig file at `/Users/samsoffes/Desktop/libPusher/Pods/Pods-specs.xcconfig'
* Generating prefix header at `/Users/samsoffes/Desktop/libPusher/Pods/Pods-specs-prefix.pch'
* Generating copy resources script at `/Users/samsoffes/Desktop/libPusher/Pods/Pods-specs-resources.sh'
* Running post install hooks
Oh no, an error occurred. Please run with `--verbose' and report on https://github.com/CocoaPods/CocoaPods/issues.

undefined method `target_definition' for #<Pod::Installer::TargetInstaller:0x007fc43a224088>
/Users/samsoffes/Desktop/libPusher/Podfile:8:in `block (3 levels) in from_file'
/Users/samsoffes/Desktop/libPusher/Podfile:8:in `each'
/Users/samsoffes/Desktop/libPusher/Podfile:8:in `find'
/Users/samsoffes/Desktop/libPusher/Podfile:8:in `block (2 levels) in from_file'
/Users/samsoffes/.rbenv/versions/1.9.3-p125/lib/ruby/gems/1.9.1/gems/cocoapods-0.5.1/lib/cocoapods/podfile.rb:241:in `call'
/Users/samsoffes/.rbenv/versions/1.9.3-p125/lib/ruby/gems/1.9.1/gems/cocoapods-0.5.1/lib/cocoapods/podfile.rb:241:in `post_install!'
/Users/samsoffes/.rbenv/versions/1.9.3-p125/lib/ruby/gems/1.9.1/gems/cocoapods-0.5.1/lib/cocoapods/installer.rb:94:in `install!'
/Users/samsoffes/.rbenv/versions/1.9.3-p125/lib/ruby/gems/1.9.1/gems/cocoapods-0.5.1/lib/cocoapods/command/install.rb:44:in `run'
/Users/samsoffes/.rbenv/versions/1.9.3-p125/lib/ruby/gems/1.9.1/gems/cocoapods-0.5.1/lib/cocoapods/command.rb:53:in `run'
/Users/samsoffes/.rbenv/versions/1.9.3-p125/lib/ruby/gems/1.9.1/gems/cocoapods-0.5.1/bin/pod:11:in `<top (required)>'
/Users/samsoffes/.rbenv/versions/1.9.3-p125/bin/pod:19:in `load'
/Users/samsoffes/.rbenv/versions/1.9.3-p125/bin/pod:19:in `<main>

Exc_Bad_Access on pusherWithKey

Hi Luke,

Sorry if I'm missing something simple.

I'm using the following code in the viewDidLoad method within the ViewController.m of a brand new project

PTPusher *client = [PTPusher pusherWithKey:@"MyKey" delegate:self encrypted:YES];

In Other Linker Flags I have -all_load

I'm linking to the following libraries:

libPusher-combined.a
libicucore.dylib
Security.framework
CFNetwork.framework
SystemConfiguration.framework
UIKit.framework
Foundation.framework
CoreGraphics.framework

libPusher-combined.a is being copied in a build phase.

The app builds and runs successfully though immediately crashes with the following crash log:

https://gist.github.com/1942930

No Messages After Reconnecting

So... everything is working great, except that after losing network connection, my app no longer receives incoming messages. I have auto-reconnect enabled, and I can see that the delegate does get connectionDidConnect called upon the return of the network. Messages just stop coming through as if I've been unsubscribed.

Right now I'm doing this to fix it... https://gist.github.com/1377988

Feels kinda bad.

libPusher and Garbage Collection

I have a GC-enabled app and am not using cocoapods.

I've tried rebuilding both socketrockets and then libpusher.framework (with the new socketrocket) with gc turned on, but to no avail.

If I use the precompiled version from the downloads section here and make GC optional, I get a seg fault, if I turn off GC entirely, I get random seg faults from other things.

Could someone (smarter than me) build a version of this framework that I could drop into a GC-enabled app?

PTPusherConnection needs to nil out socket's delegate in dealloc

PTPusherConnection's dealloc() method should nil out the socket's delegate (see below). Without this, code crashes in SRWebSocket's stream::handleEvent method in the switch case NSStreamEventHasBytesAvailable, when the code tries to call a method on self.delegate (self being the socket and self.delegate being the already disposed PTPusherConnection).

  • (void)dealloc
    {
    socket.delegate = nil; <=== add this
    [socket close];
    }

Crash was 100% reproducible for me on iOS simulator and device. Fixed with the above 1 liner.

Thanks

subscribing to multiple private channels simultaniously fails

We're trying to use libPusher in the Gaug.es iOS app and we're having problems subscribing to private channels. Here's essentially what we're doing:


PTPusher *client = [PTPusher pusherWithKey:@"xxx" delegate:self encrypted:YES];
client.authorizationURL = [NSURL URLWithString:@"https://secure.gaug.es/pusher/auth"];

for (Site *site in sites) {
  NSString *name = [NSString stringWithFormat:@"private-%@", site.id];
  if ([client channelNamed:name] == nil) {
    PTPusherChannel *channel = [client subscribeToChannelNamed:name];

    [channel bindToEventNamed:@"hit" handleWithBlock:^(PTPusherEvent *event) {
      NSLog(@"Received event data: %@", event.data); 
    }];
  }
}

When there is not latency, the authorization requests work fine (i.e. in development), but whenever there is any latency at all (hitting production, or a sleep of 0.1 seconds or more), only the first channel is authorized and subscribed. Are we doing something wrong, or is there something timing out in the operation somewhere?

Crash on unsubscribing from a nil channel

I can reproduce this every time with the nightly using the following code:

[self.pusherClient unsubscribeFromChannel:nil];

The workaround is to check for nil before unsubscribing.

Compatibility

According to your read me, the most recent version of libPusher is not backwards compatible with the previous version. Does this mean that clients using the previous version of libPusher can't send messages to clients using the most recent version of libPusher?

Thanks for the great work!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.