PusherSwift (pusher-websocket-swift) (also works with Objective-C!)

Build Status Languages Platform Cocoapods Compatible Carthage Compatible Twitter GitHub license

Supports iOS, macOS (OS X) and tvOS! (Hopefully watchOS soon!)

I just want to copy and paste some code to get me started

What else would you want? Head over to one of our example apps:

Table of Contents

Installation

CocoaPods

CocoaPods is a dependency manager for Cocoa projects and is our recommended method of installing PusherSwift and its dependencies.

If you don't already have the Cocoapods gem installed, run the following command:

$ gem install cocoapods

To integrate PusherSwift into your Xcode project using CocoaPods, specify it in your Podfile:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '10.0'
use_frameworks!

pod 'PusherSwift', '~> 4.0'

Then, run the following command:

$ pod install

If you find that you're not having the most recent version installed when you run pod install then try running:

$ pod cache clean
$ pod repo update PusherSwift
$ pod install

Also you'll need to make sure that you've not got the version of PusherSwift locked to an old version in your Podfile.lock file.

Carthage

Carthage is a decentralized dependency manager that automates the process of adding frameworks to your Cocoa application.

You can install Carthage with Homebrew using the following command:

$ brew update
$ brew install carthage

To integrate PusherSwift into your Xcode project using Carthage, specify it in your Cartfile:

github "pusher/pusher-websocket-swift"

Configuration

There are a number of configuration parameters which can be set for the Pusher client. For Swift usage they are:

  • authMethod (AuthMethod) - the method you would like the client to use to authenticate subscription requests to channels requiring authentication (see below for more details)
  • attemptToReturnJSONObject (Bool) - whether or not you'd like the library to try and parse your data as JSON (or not, and just return a string)
  • encrypted (Bool) - whether or not you'd like to use encypted transport or not, default is true
  • autoReconnect (Bool) - set whether or not you'd like the library to try and autoReconnect upon disconnection
  • host (PusherHost) - set a custom value for the host you'd like to connect to, e.g. PusherHost.host("ws-test.pusher.com")
  • port (Int) - set a custom value for the port that you'd like to connect to

The authMethod parameter must be of the type AuthMethod. This is an enum defined as:

public enum AuthMethod {
    case endpoint(authEndpoint: String)
    case authRequestBuilder(authRequestBuilder: AuthRequestBuilderProtocol)
    case inline(secret: String)
    case noMethod
}
  • endpoint(authEndpoint: String) - the client will make a POST request to the endpoint you specify with the socket ID of the client and the channel name attempting to be subscribed to
  • authRequestBuilder(authRequestBuilder: AuthRequestBuilderProtocol) - you specify an object that conforms to the AuthRequestBuilderProtocol (defined below), which must generate an NSURLRequest object that will be used to make the auth request
  • inline(secret: String) - your app's secret so that authentication requests do not need to be made to your authentication endpoint and instead subscriptions can be authenticated directly inside the library (this is mainly desgined to be used for development)
  • noMethod - if you are only using public channels then you do not need to set an authMethod (this is the default value)

This is the AuthRequestBuilderProtocol definition:

public protocol AuthRequestBuilderProtocol {
    func requestFor(socketID: String, channelName: String) -> URLRequest?

<span class="pl-c"><span class="pl-c">//</span> DEPRECATED</span>

func requestFor(socketID: String, channel: PusherChannel) -> NSMutableURLRequest? }

Note that if you want to specify the cluster to which you want to connect then you use the host property as follows:

Swift

let options = PusherClientOptions(
    host: .cluster("eu")
)

Objective-C

OCAuthMethod *authMethod = [[OCAuthMethod alloc] initWithAuthEndpoint:@"https://your.authendpoint/pusher/auth"];
OCPusherHost *host = [[OCPusherHost alloc] initWithCluster:@"eu"];
PusherClientOptions *options = [[PusherClientOptions alloc]
                                initWithOcAuthMethod:authMethod
                                attemptToReturnJSONObject:YES
                                autoReconnect:YES
                                ocHost:host
                                port:nil
                                encrypted:YES];

All of these configuration options need to be passed to a PusherClientOptions object, which in turn needs to be passed to the Pusher object, when instantiating it, for example:

Swift

let options = PusherClientOptions(
    authMethod: .endpoint(authEndpoint: "http://localhost:9292/pusher/auth")
)

let pusher = Pusher(key: "APP_KEY", options: options)

Objective-C

OCAuthMethod *authMethod = [[OCAuthMethod alloc] initWithAuthEndpoint:@"https://your.authendpoint/pusher/auth"];
OCPusherHost *host = [[OCPusherHost alloc] initWithCluster:@"eu"];
PusherClientOptions *options = [[PusherClientOptions alloc]
                                initWithOcAuthMethod:authMethod
                                attemptToReturnJSONObject:YES
                                autoReconnect:YES
                                ocHost:host
                                port:nil
                                encrypted:YES];
pusher = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY" options:options];

As you may have noticed, this differs slightly for Objective-C usage. The main changes are that you need to use OCAuthMethod and OCPusherHost in place of AuthMethod and PusherHost. The OCAuthMethod class has the following functions that you can call in your Objective-C code.

public init(authEndpoint: String)

public init(authRequestBuilder: AuthRequestBuilderProtocol)

public init(secret: String)

public init()

OCAuthMethod *authMethod = [[OCAuthMethod alloc] initWithSecret:@"YOUR_APP_SECRET"];
PusherClientOptions *options = [[PusherClientOptions alloc] initWithAuthMethod:authMethod];

The case is similar for OCPusherHost. You have the following functions available:

public init(host: String)

public init(cluster: String)

[[OCPusherHost alloc] initWithCluster:@"YOUR_CLUSTER_SHORTCODE"];

Authenticated channel example:

Swift

class AuthRequestBuilder: AuthRequestBuilderProtocol {
    func requestFor(socketID: String, channelName: String) -> URLRequest? {
        var request = URLRequest(url: URL(string: "http://localhost:9292/builder")!)
        request.httpMethod = "POST"
        request.httpBody = "socket_id=(socketID)&channel_name=(channel.name)".data(using: String.Encoding.utf8)
        request.addValue("myToken", forHTTPHeaderField: "Authorization")
        return request
    }
}

let options = PusherClientOptions( authMethod: AuthMethod.authRequestBuilder(authRequestBuilder: AuthRequestBuilder()) ) let pusher = Pusher( key: "APP_KEY", options: options )

Objective-C

@interface AuthRequestBuilder : NSObject <AuthRequestBuilderProtocol>

  • (NSURLRequest *)requestForSocketID:(NSString *)socketID channelName:(NSString *)channelName;

@end

@implementation AuthRequestBuilder

  • (NSURLRequest *)requestForSocketID:(NSString *)socketID channelName:(NSString *)channelName { NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[[NSURL alloc] initWithString:@"http://localhost:9292/pusher/auth"]]; NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL: [[NSURL alloc] initWithString:@"http://localhost:9292/pusher/auth"]];

    NSString *dataStr = [NSString stringWithFormat: @"socket_id=%@&channel_name=%@", socketID, channelName]; NSData *data = [dataStr dataUsingEncoding:NSUTF8StringEncoding]; mutableRequest.HTTPBody = data; mutableRequest.HTTPMethod = @"POST"; [mutableRequest addValue:@"myToken" forHTTPHeaderField:@"Authorization"];

    request = [mutableRequest copy];

    return request; }

@end

OCAuthMethod *authMethod = [[OCAuthMethod alloc] initWithAuthRequestBuilder:[[AuthRequestBuilder alloc] init]]; PusherClientOptions *options = [[PusherClientOptions alloc] initWithAuthMethod:authMethod];

Where "Authorization" and "myToken" are the field and value your server is expecting in the headers of the request.

Connection

A Websocket connection is established by providing your API key to the constructor function:

Swift

let pusher = Pusher(key: "APP_KEY")
pusher.connect()

Objective-C

Pusher *pusher = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY"];
[pusher connect];

This returns a client object which can then be used to subscribe to channels and then calling connect() triggers the connection process to start.

You can also set a userDataFetcher on the connection object.

  • userDataFetcher (() -> PusherPresenceChannelMember) - if you are subscribing to an authenticated channel and wish to provide a function to return user data

You set it like this:

Swift

let pusher = Pusher(key: "APP_KEY")

pusher.connection.userDataFetcher = { () -> PusherPresenceChannelMember in return PusherPresenceChannelMember(userId: "123", userInfo: ["twitter": "hamchapman"]) }

Objective-C

Pusher *pusher = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY"];

pusher.connection.userDataFetcher = ^PusherPresenceChannelMember* () { NSString *uuid = [[NSUUID UUID] UUIDString]; return [[PusherPresenceChannelMember alloc] initWithUserId:uuid userInfo:nil]; };

Connection delegate

There is a PusherDelegate that you can use to get notified of connection-related information. These are the functions that you can optionally implement when conforming to the PusherDelegate protocol:

@objc optional func changedConnectionState(from old: ConnectionState, to new: ConnectionState)
@objc optional func subscribedToChannel(name: String)
@objc optional func failedToSubscribeToChannel(name: String, response: URLResponse?, data: String?, error: NSError?)
@objc optional func debugLog(message: String)

The names of the functions largely give away what their purpose is but just for completeness:

  • changedConnectionState - use this if you want to use connection state changes to perform different actions / UI updates
  • subscribedToChannel - use this if you want to be informed of when a channel has successfully been subscribed to, which is useful if you want to perform actions that are only relevant after a subscription has succeeded, e.g. logging out the members of a presence channel
  • failedToSubscribeToChannel - use this if you want to be informed of a failed subscription attempt, which you could use, for exampple, to then attempt another subscription or make a call to a service you use to track errors
  • debugLog - use this if you want to log Pusher-related events, e.g. the underlying websocket receiving a message

Setting up a delegate looks like this:

Swift

class ViewController: UIViewController, PusherDelegate {

<span class="pl-k">override</span> <span class="pl-k">func</span> <span class="pl-en">viewDidLoad</span>() {
    <span class="pl-c1">super</span>.<span class="pl-c1">viewDidLoad</span>()
    <span class="pl-k">let</span> pusher <span class="pl-k">=</span> <span class="pl-c1">Pusher</span>(<span class="pl-c1">key</span>: <span class="pl-s"><span class="pl-pds">&#34;</span>APP_KEY<span class="pl-pds">&#34;</span></span>)
    pusher.<span class="pl-smi">connection</span>.<span class="pl-smi">delegate</span> <span class="pl-k">=</span> <span class="pl-c1">self</span>
    <span class="pl-c"><span class="pl-c">//</span> ...</span>

} }

Objective-C

@implementation ViewController

  • (void)viewDidLoad { [super viewDidLoad];

    self.client = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY"];

    self.client.connection.delegate = self; // }

Here are examples of setting up a class with functions for each of the optional protocol functions:

Swift

class DummyDelegate: PusherDelegate {
func changedConnectionState(from old: ConnectionState, to new: ConnectionState) {
    //
    }

func debugLog(message: String) { // }

func subscribedToChannel(name: String) { // }

func failedToSubscribeToChannel(name: String, response: URLResponse?, data: String?, error: NSError?) { // } }

Objective-C

@interface DummyDelegate : NSObject <PusherDelegate>

  • (void)changedConnectionState:(enum ConnectionState)old to:(enum ConnectionState)new_

  • (void)debugLogWithMessage:(NSString *)message

  • (void)subscribedToChannelWithName:(NSString *)name

  • (void)failedToSubscribeToChannelWithName:(NSString *)name response:(NSURLResponse *)response data:(NSString *)data error:(NSError *)error

  • @end

    @implementation DummyDelegate

    • (void)changedConnectionState:(enum ConnectionState)old to:(enum ConnectionState)new_ { // }

    • (void)debugLogWithMessage:(NSString *)message { // }

    • (void)subscribedToChannelWithName:(NSString *)name { // }

    • (void)failedToSubscribeToChannelWithName:(NSString *)name response:(NSURLResponse *)response data:(NSString *)data error:(NSError *)error { // }

    @end

    The different states that the connection can be in are (Objective-C integer enum cases in brackets):

    • connecting (0) - the connection is about to attempt to be made
    • connected (1) - the connection has been successfully made
    • disconnecting (2) - the connection has been instructed to disconnect and it is just about to do so
    • disconnected (3) - the connection has disconnected and no attempt will be made to reconnect automatically
    • reconnecting (4) - an attempt is going to be made to try and re-establish the connection
    • reconnectingWhenNetworkBecomesReachable (5) - when the network becomes reachable an attempt will be made to reconnect

    There is a stringValue function that you can call on ConnectionState objects in order to get a String representation of the state, for example "connecting".

    Reconnection

    There are three main ways in which a disconnection can occur:

    • The client explicitly calls disconnect and a close frame is sent over the websocket connection
    • The client experiences some form of network degradation which leads to a heartbeat (ping/pong) message being missed and thus the client disconnects
    • The Pusher server closes the websocket connection; typically this will only occur during a restart of the Pusher socket servers and an almost immediate reconnection should occur

    In the case of the first type of disconnection the library will (as you'd hope) not attempt a reconnection.

    If there is network degradation that leads to a disconnection then the library has the Reachability library embedded and will be able to automatically determine when to attempt a reconnect based on the changing network conditions.

    If the Pusher servers close the websocket then the library will attempt to reconnect (by default) a maximum of 6 times, with an exponential backoff. The value of reconnectAttemptsMax is a public property on the PusherConnection and so can be changed if you wish.

    All of this is the case if you have the client option of autoReconnect set as true, which it is by default. If the reconnection strategies are not suitable for your use case then you can set autoReconnect to false and implement your own reconnection strategy based on the connection state changes.

    There are a couple of properties on the connection (PusherConnection) that you can set that affect how the reconnection behaviour works. These are:

    • public var reconnectAttemptsMax: Int? = 6 - if you set this to nil then there is no maximum number of reconnect attempts and so attempts will continue to be made with an exponential backoff (based on number of attempts), otherwise only as many attempts as this property's value will be made before the connection's state moves to .disconnected
    • public var maxReconnectGapInSeconds: Double? = nil - if you want to set a maximum length of time (in seconds) between reconnect attempts then set this property appropriately

    Note that the number of reconnect attempts gets reset to 0 as soon as a successful connection is made.

    Subscribing

    Public channels

    The default method for subscribing to a channel involves invoking the subscribe method of your client object:

    Swift

    let myChannel = pusher.subscribe("my-channel")

    Objective-C

    PusherChannel *myChannel = [pusher subscribeWithChannelName:@"my-channel"];

    This returns PusherChannel object, which events can be bound to.

    Private channels

    Private channels are created in exactly the same way as public channels, except that they reside in the 'private-' namespace. This means prefixing the channel name:

    Swift

    let myPrivateChannel = pusher.subscribe("private-my-channel")

    Objective-C

    PusherChannel *myPrivateChannel = [pusher subscribeWithChannelName:@"private-my-channel"];

    Subscribing to private channels involves the client being authenticated. See the Configuration section for the authenticated channel example for more information.

    Presence channels

    Presence channels are channels whose names are prefixed by presence-.

    The recommended way of subscribing to a presence channel is to use the subscribeToPresenceChannel function, as opposed to the standard subscribe function. Using the subscribeToPresenceChannel function means that you get a PusherPresenceChannel object returned, as opposed to a standard PusherChannel. This PusherPresenceChannel object has some extra, presence-channel-specific functions availalbe to it, such as members, me, and findMember.

    Swift

    let myPresenceChannel = pusher.subscribeToPresenceChannel(channelName: "presence-my-channel")

    Objective-C

    PusherPresenceChannel *myPresenceChannel = [pusher subscribeToPresenceChannelWithChannelName:@"presence-my-channel"];

    As alluded to, you can still subscribe to presence channels using the subscribe method, but the channel object you get back won't have access to the presence-channel-specific functions, unless you choose to cast the channel object to a PusherPresenceChannel.

    Swift

    let myPresenceChannel = pusher.subscribe("presence-my-channel")

    Objective-C

    PusherChannel *myPresenceChannel = [pusher subscribeWithChannelName:@"presence-my-channel"];

    You can also provide functions that will be called when members are either added to or removed from the channel. These are available as parameters to both subscribe and subscribeToPresenceChannel.

    Swift

    let onMemberChange = { (member: PusherPresenceChannelMember) in
        print(member)
    }

    let chan = pusher.subscribeToPresenceChannel("presence-channel", onMemberAdded: onMemberChange, onMemberRemoved: onMemberChange)

    Objective-C

    void (^onMemberChange)(PusherPresenceChannelMember*) = ^void (PusherPresenceChannelMember *member) {
        NSLog(@"%@", member);
    };

    PusherChannel *myPresenceChannel = [pusher subscribeWithChannelName:@"presence-my-channel" onMemberAdded:onMemberChange onMemberRemoved:onMemberChange];

    Note: The members and myId properties of PusherPresenceChannel objects (and functions that get the value of these properties) will only be set once subscription to the channel has succeeded.

    The easiest way to find out when a channel has been successfully susbcribed to is to bind to the event named pusher:subscription_succeeded on the channel you're interested in. It would look something like this:

    Swift

    let pusher = Pusher(key: "YOUR_APP_KEY")

    let chan = pusher.subscribeToPresenceChannel("presence-channel")

    chan.bind(eventName: "pusher:subscription_succeeded", callback: { data in print("Subscribed!") print("I can now access myId: (chan.myId)") print("And here are the channel members: (chan.members)") })

    Objective-C

    Pusher *pusher = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY"];
    PusherPresenceChannel *chan = [pusher subscribeToPresenceChannelWithChannelName:@"presence-channel"];

    [chan bindWithEventName:@"pusher:subscription_succeeded" callback: ^void (NSDictionary *data) { NSLog(@"Subscribed!"); NSLog(@"I can now access myId: %@", chan.myId); NSLog(@"And here are my channel members: %@", chan.members); }];

    You can also be notified of a successfull subscription by using the subscriptionDidSucceed delegate method that is part of the PusherDelegate protocol.

    Here is an example of using the delegate:

    Swift

    class DummyDelegate: PusherDelegate {
        func subscribedToChannel(name: String) {
            if channelName == "presence-channel" {
                if let presChan = pusher.connection.channels.findPresence(channelName) {
                    // in here you can now have access to the channel's members and myId properties
                    print(presChan.members)
                    print(presChan.myId)
                }
            }
        }
    }

    let pusher = Pusher(key: "YOUR_APP_KEY") pusher.connection.delegate = DummyDelegate() let chan = pusher.subscribeToPresenceChannel("presence-channel")

    Objective-C

    @implementation DummyDelegate

    • (void)subscribedToChannelWithName:(NSString *)name { if ([channelName isEqual: @"presence-channel"]) { PusherPresenceChannel *presChan = [self.client.connection.channels findPresenceWithName:@"presence-channel"]; NSLog(@"%@", [presChan members]); NSLog(@"%@", [presChan myId]); } }

    @implementation ViewController

    • (void)viewDidLoad { //

      Pusher *pusher = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY"]; pusher.connection.delegate = [[DummyDelegate alloc] init]; PusherChannel *chan = [pusher subscribeToPresenceChannelWithChannelName:@"presence-channel"];

    Note that both private and presence channels require the user to be authenticated in order to subscribe to the channel. This authentication can either happen inside the library, if you configured your Pusher object with your app's secret, or an authentication request is made to an authentication endpoint that you provide, again when instantiaing your Pusher object.

    We recommend that you use an authentication endpoint over including your app's secret in your app in the vast majority of use cases. If you are completely certain that there's no risk to you including your app's secret in your app, for example if your app is just for internal use at your company, then it can make things easier than setting up an authentication endpoint.

    Binding to events

    Events can be bound to at 2 levels; globally and per channel. When binding to an event you can choose to save the return value, which is a unique identifier for the event handler that gets created. The only reason to save this is if you're going to want to unbind from the event at a later point in time. There is an example of this below.

    Global events

    You can attach behaviour to these events regardless of the channel the event is broadcast to. The following is an example of an app that binds to new comments from any channel:

    Swift

    let pusher = Pusher(key: "YOUR_APP_KEY")
    pusher.subscribe("my-channel")

    pusher.bind(callback: { (data: Any?) -> Void in if let data = data as? [String : AnyObject] { if let commenter = data["commenter"] as? String, message = data["message"] as? String { print("(commenter) wrote (message)") } } })

    Objective-C

    Pusher *pusher = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY"];
    PusherChannel *chan = [pusher subscribeWithChannelName:@"my-channel"];

    [pusher bind: ^void (NSDictionary *data) { NSString *commenter = data[@"commenter"]; NSString *message = data[@"message"];

    <span class="pl-c1">NSLog</span>(<span class="pl-s"><span class="pl-pds">@&#34;</span><span class="pl-c1">%@</span> wrote <span class="pl-c1">%@</span><span class="pl-pds">&#34;</span></span>, commenter, message);
    

    }];

    Per-channel events

    These are bound to a specific channel, and mean that you can reuse event names in different parts of your client application. The following might be an example of a stock tracking app where several channels are opened for different companies:

    Swift

    let pusher = Pusher(key: "YOUR_APP_KEY")
    let myChannel = pusher.subscribe("my-channel")

    myChannel.bind(eventName: "new-price", callback: { (data: Any?) -> Void in if let data = data as? [String : AnyObject] { if let price = data["price"] as? String, company = data["company"] as? String { print("(company) is now priced at (price)") } } })

    Objective-C

    Pusher *pusher = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY"];
    PusherChannel *chan = [pusher subscribeWithChannelName:@"my-channel"];

    [chan bindWithEventName:@"new-price" callback:^void (NSDictionary *data) { NSString *price = data[@"price"]; NSString *company = data[@"company"];

    <span class="pl-c1">NSLog</span>(<span class="pl-s"><span class="pl-pds">@&#34;</span><span class="pl-c1">%@</span> is now priced at <span class="pl-c1">%@</span><span class="pl-pds">&#34;</span></span>, company, price);
    

    }];

    Receiving errors

    Errors are sent to the client for which they are relevant with an event name of pusher:error. These can be received and handled using code as follows. Obviously the specifics of how to handle them are left up to the developer but this displays the general pattern.

    Swift

    pusher.bind({ (message: Any?) in
        if let message = message as? [String: AnyObject], eventName = message["event"] as? String where eventName == "pusher:error" {
            if let data = message["data"] as? [String: AnyObject], errorMessage = data["message"] as? String {
                print("Error message: (errorMessage)")
            }
        }
    })

    Objective-C

    [pusher bind:^void (NSDictionary *data) {
        NSString *eventName = data[@"event"];

    <span class="pl-k">if</span> ([eventName <span class="pl-c1">isEqualToString:</span><span class="pl-s"><span class="pl-pds">@&#34;</span>pusher:error<span class="pl-pds">&#34;</span></span>]) {
        <span class="pl-c1">NSString</span> *errorMessage = data[<span class="pl-s"><span class="pl-pds">@&#34;</span>data<span class="pl-pds">&#34;</span></span>][<span class="pl-s"><span class="pl-pds">@&#34;</span>message<span class="pl-pds">&#34;</span></span>];
        <span class="pl-c1">NSLog</span>(<span class="pl-s"><span class="pl-pds">@&#34;</span>Error message: <span class="pl-c1">%@</span><span class="pl-pds">&#34;</span></span>, errorMessage);
    }
    

    }];

    The sort of errors you might get are:

    # if attempting to subscribe to an already subscribed-to channel

    "{&#34;event&#34;:&#34;pusher:error&#34;,&#34;data&#34;:{&#34;code&#34;:null,&#34;message&#34;:&#34;Existing subscription to channel presence-channel&#34;}}"

    # if the auth signature generated by your auth mechanism is invalid

    "{&#34;event&#34;:&#34;pusher:error&#34;,&#34;data&#34;:{&#34;code&#34;:null,&#34;message&#34;:&#34;Invalid signature: Expected HMAC SHA256 hex digest of 200557.5043858:presence-channel:{\&#34;user_id\&#34;:\&#34;200557.5043858\&#34;}, but got 8372e1649cf5a45a2de3cd97fe11d85de80b214243e3a9e9f5cee502fa03f880&#34;}}"

    You can see that the general form they take is:

    {
      "event": "pusher:error",
      "data": {
        "code": null,
        "message": "Error message here"
      }
    }

    Unbind event handlers

    You can remove previously-bound handlers from an object by using the unbind function. For example,

    Swift

    let pusher = Pusher(key: "YOUR_APP_KEY")
    let myChannel = pusher.subscribe("my-channel")

    let eventHandlerId = myChannel.bind(eventName: "new-price", callback: { (data: Any?) -> Void in })

    myChannel.unbind(eventName: "new-price", callbackId: eventHandlerId)

    Objective-C

    Pusher *pusher = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY"];
    PusherChannel *chan = [pusher subscribeWithChannelName:@"my-channel"];

    NSString *callbackId = [chan bindWithEventName:@"new-price" callback:^void (NSDictionary *data) { … }];

    [chan unbindWithEventName:@"new-price" callbackId:callbackId];

    You can unbind from events at both the global and per channel level. For both objects you also have the option of calling unbindAll, which, as you can guess, will unbind all eventHandlers on the object.

    Push notifications

    Pusher also supports push notifications. Instances of your application can register for push notifications and subscribe to "interests". Your server can then publish to those interests, which will be delivered to your application as push notifications. See our guide to setting up APNs push notifications for a friendly introduction.

    Initializing the Pusher object

    You should set up your app for push notifications in your AppDelegate. The setup varies slightly depending on whether you're using Swift or Objective-C, and whether you're using iOS or macOS (OS X):

    Swift on iOS

    import PusherSwift
    import UserNotifications

    @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { let pusher = Pusher(key: "YOUR_APP_KEY")

    Objective-C on iOS

    #import "AppDelegate.h"
    @import UserNotifications;

    @interface AppDelegate ()

    @end

    @implementation AppDelegate

    Swift on macOS

    import Cocoa
    import PusherSwift

    @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate, PusherDelegate { let pusher = Pusher(key: "YOUR_APPKEY") //

    Registering with APNs

    For your app to receive push notifications, it must first register with APNs. You should do this when the application finishes launching. Your app should register for all types of notification, like so:

    Swift on iOS

    func application( application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
        let center = UNUserNotificationCenter.current()
        center.requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in
            // Enable or disable features based on authorization.
        }
        application.registerForRemoteNotifications()

    <span class="pl-k">return</span> <span class="pl-c1">true</span>
    

    }

    Objective-C on iOS

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        self.pusher = [[Pusher alloc] initWithKey:@"YOUR_APP_KEY"];

    UNUserNotificationCenter *center = [UNUserNotificationCenter <span class="pl-c1">currentNotificationCenter</span>];
    [center <span class="pl-c1">requestAuthorizationWithOptions:</span>(UNAuthorizationOptionBadge | UNAuthorizationOptionAlert | UNAuthorizationOptionSound) <span class="pl-c1">completionHandler:</span>^(<span class="pl-c1">BOOL</span> granted, <span class="pl-c1">NSError</span> * _Nullable error) {
        <span class="pl-c"><span class="pl-c">//</span> Enable or disable features based on authorization.</span>
    }];
    
    [application <span class="pl-c1">registerForRemoteNotifications</span>];
    <span class="pl-k">return</span> <span class="pl-c1">YES</span>;
    

    }

    Swift on macOS

    func applicationDidFinishLaunching( aNotification: Notification) {
        NSApp.registerForRemoteNotifications(matching: [NSRemoteNotificationType.alert, NSRemoteNotificationType.sound, NSRemoteNotificationType.badge])
    }

    Receiving your APNs device token and registering with Pusher

    Next, APNs will respond with a device token identifying your app instance. Your app should then register with Pusher, passing along its device token.

    Your app can now subscribe to interests. The following registers and subscribes the app to the interest "donuts":

    Swift on iOS

    func application( application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        pusher.nativePusher.register(deviceToken: deviceToken)
        pusher.nativePusher.subscribe(interestName: "donuts")
    }

    Objective-C on iOS

    - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
        NSLog(@"Registered for remote notifications; received device token");
        [[[self pusher] nativePusher] registerWithDeviceToken:deviceToken];
        [[[self pusher] nativePusher] subscribeWithInterestName:@"donuts"];
    }

    Swift on macOS

    func application( application: NSApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        self.pusher.nativePusher.register(deviceToken: deviceToken)
        self.pusher.nativePusher.subscribe(interestName: "donuts")
    }

    Receiving push notifications

    When your server publishes a notification to the interest "donuts", it will get passed to your app. This happens as a call in your AppDelegate which you should listen to:

    Swift on iOS

    func application( application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        print(userInfo)
    }

    Objective-C on iOS

    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
        NSLog(@"Received remote notification: %@", userInfo);
    }

    Swift on macOS

    func application(_ application: NSApplication, didReceiveRemoteNotification userInfo: [String: Any]) {
        print("Received remote notification: (userInfo.debugDescription)" )
    }

    Unsubscribing from interests

    If at a later point you wish to unsubscribe from an interest, this works in the same way:

    Swift

    pusher.nativePusher.unsubscribe(interestName: "donuts")

    Objective-C

    [[[self pusher] nativePusher] unsubscribeWithInterestName:@"donuts"];

    For a complete example of a working app, see the Example/ directory in this repository. Specifically for push notifications code, see the Example/AppDelegate.swift file.

    Pusher delegate

    You can also implement some of the PusherDelegate functions to get access to events that occur in relation to push notifications interactions. These are the functions that you can optionally implement when conforming to the PusherDelegate protocol:

    @objc optional func registeredForPushNotifications(clientId: String)
    @objc optional func failedToRegisterForPushNotifications(response: URLResponse, responseBody: String?)
    @objc optional func subscribedToInterest(name: String)
    @objc optional func unsubscribedFromInterest(name: String)

    Again, the names of the functions largely give away what their purpose is but just for completeness:

    • registeredForPushNotifications - use this if you want to know when a client has successfully registered with the Pusher Push Notifications service, or if you want access to the clientId that is returned upon successful registration
    • failedToRegisterForPushNotifications - use this if you want to know when a client has failed to register with the Pusher Push Notifications service
    • subscribedToInterest - use this if you want keep track of interests that are successfully subscribed to
    • unsubscribedFromInterest - use this if you want keep track of interests that are successfully unsubscribed from

    Setting up a delegate looks like this:

    Swift

    class ViewController: UIViewController, PusherDelegate {

    <span class="pl-k">override</span> <span class="pl-k">func</span> <span class="pl-en">viewDidLoad</span>() {
        <span class="pl-c1">super</span>.<span class="pl-c1">viewDidLoad</span>()
        <span class="pl-k">let</span> pusher <span class="pl-k">=</span> <span class="pl-c1">Pusher</span>(<span class="pl-c1">key</span>: <span class="pl-s"><span class="pl-pds">&#34;</span>APP_KEY<span class="pl-pds">&#34;</span></span>)
        pusher.<span class="pl-smi">delegate</span> <span class="pl-k">=</span> <span class="pl-c1">self</span>
        <span class="pl-c"><span class="pl-c">//</span> ...</span>
    

    } }

    Objective-C

    @implementation ViewController

    • (void)viewDidLoad { [super viewDidLoad];

      self.client = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY"];

      self.client.delegate = self; // }

    The process is identical to that of setting up the PusherDelegate to receive notifications of connection-based events.

    Testing

    There are a set of tests for the library that can be run using the standard method (Command-U in Xcode).

    The tests also get run on Travis-CI. See .travis.yml for details on how the Travis tests are run.

    Extensions

    Communication

    • If you have found a bug, please open an issue.
    • If you have a feature request, please open an issue.
    • If you want to contribute, please submit a pull request (preferrably with some tests



    PusherSwift (pusher-websocket-swift) (also works with Objective-C!)

    构建状态data-canonical-src 语言 平台 的CocoaPods兼容 迦太基兼容 推 GitHub < / p>

    支持iOS,MacOS(OS X)和tvOS! (希望看到哦!)

    我只想复制并粘贴一些代码让我开始

    你还想要什么?转到我们的示例应用程序之一:

    目录

    安装

    CocoaPods

    CocoaPods 是Cocoa项目的依赖管理器,是我们推荐的安装PusherSwift及其依赖项的方法。

    如果您尚未安装Cocoapods gem,请运行以下命令:

    $ gem install cocoapods

    要使用CocoaPods将PusherSwift集成到Xcode项目中,请在您的 Podfile 中指定:

    source 'https://github.com/CocoaPods/Specs.git'
    platform :ios, '10.0'
    use_frameworks!

    pod 'PusherSwift', '~> 4.0'

    然后,运行以下命令:

    $ pod install

    如果您发现运行 pod install 时没有安装最新版本,请尝试运行:

    $ pod cache clean
    $ pod repo update PusherSwift
    $ pod install

    此外,您还需要确保您没有将PusherSwift的版本锁定到您的 Podfile.lock 文件中的旧版本。

    Carthage

    Carthage 是一个分散的依赖管理器,可以自动执行向Cocoa应用程序添加框架的过程。

    您可以使用以下命令,使用 Homebrew 安装Carthage:

    $ brew update
    $ brew install carthage

    要使用Carthage将PusherSwift集成到Xcode项目中,请在您的 Cartfile 中指定:

    github "pusher/pusher-websocket-swift"
    

    配置

    可以为Pusher客户端设置多个配置参数。对于Swift使用,它们是:

    • authMethod(AuthMethod) - 您希望客户端用于向需要身份验证的频道验证订阅请求的方法(请参阅下文了解更多详细信息)
    • 尝试尝试JSONObject(Bool) - 无论您是否希望库尝试使用JSON解析数据(否则,只返回一个字符串)
    • 加密(Bool) - 无论您是否要使用传统的传输,默认为 true
    • autoReconnect(Bool) - 设置是否要让库尝试并断开连接时自动重新连接
    • host(PusherHost) - 为要连接的主机设置自定义值,例如 PusherHost.host(ws-test.pusher.com)
    • port(Int) - 为要连接的端口设置自定义值

    authMethod 参数必须是 AuthMethod 。这是一个定义为:

    的枚举
    public enum AuthMethod {
        case endpoint(authEndpoint: String)
        case authRequestBuilder(authRequestBuilder: AuthRequestBuilderProtocol)
        case inline(secret: String)
        case noMethod
    }
    • endpoint(authEndpoint:String) - 客户端将使用客户端的套接字ID为您指定的端点发出 POST 请求,订阅
    • authRequestBuilder(authRequestBuilder:AuthRequestBuilderProtocol) - 指定一个符合 AuthRequestBuilderProtocol (定义如下)的对象,它必须生成一个 NSURLRequest 将用于进行认证请求的对象
    • inline(secret:String) - 您的应用程序的秘密,以便不需要对身份验证端点进行身份验证请求,而是可以直接在库中验证订阅(这主要是由用于开发)
    • noMethod - 如果您只使用公共频道,那么您不需要设置 authMethod (这是默认值)

    这是 AuthRequestBuilderProtocol 定义:

    public protocol AuthRequestBuilderProtocol {
        func requestFor(socketID: String, channelName: String) -> URLRequest?

    <span class="pl-c"><span class="pl-c">//</span> DEPRECATED</span>
    

    func requestFor(socketID: String, channel: PusherChannel) -> NSMutableURLRequest? }

    请注意,如果要指定要连接的群集,则可以使用 host 属性,如下所示:

    Swift

    let options = PusherClientOptions(
        host: .cluster("eu")
    )

    Objective-C

    OCAuthMethod *authMethod = [[OCAuthMethod alloc] initWithAuthEndpoint:@"https://your.authendpoint/pusher/auth"];
    OCPusherHost *host = [[OCPusherHost alloc] initWithCluster:@"eu"];
    PusherClientOptions *options = [[PusherClientOptions alloc]
                                    initWithOcAuthMethod:authMethod
                                    attemptToReturnJSONObject:YES
                                    autoReconnect:YES
                                    ocHost:host
                                    port:nil
                                    encrypted:YES];

    所有这些配置选项都需要传递给一个 PusherClientOptions 对象,它们需要在实例化时将其传递给Pusher对象,例如:

    Swift

    let options = PusherClientOptions(
        authMethod: .endpoint(authEndpoint: "http://localhost:9292/pusher/auth")
    )

    let pusher = Pusher(key: "APP_KEY", options: options)

    Objective-C

    OCAuthMethod *authMethod = [[OCAuthMethod alloc] initWithAuthEndpoint:@"https://your.authendpoint/pusher/auth"];
    OCPusherHost *host = [[OCPusherHost alloc] initWithCluster:@"eu"];
    PusherClientOptions *options = [[PusherClientOptions alloc]
                                    initWithOcAuthMethod:authMethod
                                    attemptToReturnJSONObject:YES
                                    autoReconnect:YES
                                    ocHost:host
                                    port:nil
                                    encrypted:YES];
    pusher = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY" options:options];
    您可能已经注意到,Objective-C使用情况略有不同。主要的变化是您需要使用 OCAuthMethod OCPusherHost 代替 AuthMethod PusherHost OCAuthMethod 类具有以下可以在Objective-C代码中调用的函数。

    public init(authEndpoint: String)

    public init(authRequestBuilder: AuthRequestBuilderProtocol)

    public init(secret: String)

    public init()

    OCAuthMethod *authMethod = [[OCAuthMethod alloc] initWithSecret:@"YOUR_APP_SECRET"];
    PusherClientOptions *options = [[PusherClientOptions alloc] initWithAuthMethod:authMethod];

    OCPusherHost 的情况类似。您可以使用以下功能:

    public init(host: String)

    public init(cluster: String)

    [[OCPusherHost alloc] initWithCluster:@"YOUR_CLUSTER_SHORTCODE"];

    认证通道示例:

    Swift

    class AuthRequestBuilder: AuthRequestBuilderProtocol {
        func requestFor(socketID: String, channelName: String) -> URLRequest? {
            var request = URLRequest(url: URL(string: "http://localhost:9292/builder")!)
            request.httpMethod = "POST"
            request.httpBody = "socket_id=(socketID)&channel_name=(channel.name)".data(using: String.Encoding.utf8)
            request.addValue("myToken", forHTTPHeaderField: "Authorization")
            return request
        }
    }

    let options = PusherClientOptions( authMethod: AuthMethod.authRequestBuilder(authRequestBuilder: AuthRequestBuilder()) ) let pusher = Pusher( key: "APP_KEY", options: options )

    Objective-C

    @interface AuthRequestBuilder : NSObject <AuthRequestBuilderProtocol>

    • (NSURLRequest *)requestForSocketID:(NSString *)socketID channelName:(NSString *)channelName;

    @end

    @implementation AuthRequestBuilder

    • (NSURLRequest *)requestForSocketID:(NSString *)socketID channelName:(NSString *)channelName { NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[[NSURL alloc] initWithString:@"http://localhost:9292/pusher/auth"]]; NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL: [[NSURL alloc] initWithString:@"http://localhost:9292/pusher/auth"]];

      NSString *dataStr = [NSString stringWithFormat: @"socket_id=%@&channel_name=%@", socketID, channelName]; NSData *data = [dataStr dataUsingEncoding:NSUTF8StringEncoding]; mutableRequest.HTTPBody = data; mutableRequest.HTTPMethod = @"POST"; [mutableRequest addValue:@"myToken" forHTTPHeaderField:@"Authorization"];

      request = [mutableRequest copy];

      return request; }

    @end

    OCAuthMethod *authMethod = [[OCAuthMethod alloc] initWithAuthRequestBuilder:[[AuthRequestBuilder alloc] init]]; PusherClientOptions *options = [[PusherClientOptions alloc] initWithAuthMethod:authMethod];

    其中授权myToken是您的服务器在请求标头中期望的字段和值。

    连接

    通过将API密钥提供给构造函数来建立Websocket连接:

    Swift

    let pusher = Pusher(key: "APP_KEY")
    pusher.connect()

    Objective-C

    Pusher *pusher = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY"];
    [pusher connect];

    这将返回一个客户端对象,然后可以使用它来订阅通道,然后调用 connect()触发连接进程启动。

    您还可以在连接对象上设置 userDataFetcher

    • userDataFetcher(() - &gt; PusherPresenceChannelMember) - 如果您订阅经过身份验证的频道,并希望提供返回用户数据的功能

    您设置如下:

    Swift

    let pusher = Pusher(key: "APP_KEY")

    pusher.connection.userDataFetcher = { () -> PusherPresenceChannelMember in return PusherPresenceChannelMember(userId: "123", userInfo: ["twitter": "hamchapman"]) }

    Objective-C

    Pusher *pusher = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY"];

    pusher.connection.userDataFetcher = ^PusherPresenceChannelMember* () { NSString *uuid = [[NSUUID UUID] UUIDString]; return [[PusherPresenceChannelMember alloc] initWithUserId:uuid userInfo:nil]; };

    连接委托

    有一个 PusherDelegate 可以用来获取有关连接相关信息的通知。这些是在符合 PusherDelegate 协议时可以选择实现的功能:

    @objc optional func changedConnectionState(from old: ConnectionState, to new: ConnectionState)
    @objc optional func subscribedToChannel(name: String)
    @objc optional func failedToSubscribeToChannel(name: String, response: URLResponse?, data: String?, error: NSError?)
    @objc optional func debugLog(message: String)

    功能的名称大部分放弃了它们的目的,仅仅是为了完整性:

    • changedConnectionState - 如果要使用连接状态更改来执行不同的操作/ UI更新,请使用此功能
    • subscribedToChannel - 如果您想知道某个频道何时成功订阅,则使用此功能,如果您希望在订阅成功后执行相关的操作(例如,注销在线频道的成员
    • failedToSubscribeToChannel - 如果要通知失败的订阅尝试,您可以使用它,例如,然后尝试另一个订阅或拨打您用于跟踪的服务
    • debugLog - 如果要记录与推动器相关的事件,请参见底层websocket接收消息

    设置代理如下所示:

    Swift

    class ViewController: UIViewController, PusherDelegate {

    <span class="pl-k">override</span> <span class="pl-k">func</span> <span class="pl-en">viewDidLoad</span>() {
        <span class="pl-c1">super</span>.<span class="pl-c1">viewDidLoad</span>()
        <span class="pl-k">let</span> pusher <span class="pl-k">=</span> <span class="pl-c1">Pusher</span>(<span class="pl-c1">key</span>: <span class="pl-s"><span class="pl-pds">&#34;</span>APP_KEY<span class="pl-pds">&#34;</span></span>)
        pusher.<span class="pl-smi">connection</span>.<span class="pl-smi">delegate</span> <span class="pl-k">=</span> <span class="pl-c1">self</span>
        <span class="pl-c"><span class="pl-c">//</span> ...</span>
    

    } }

    Objective-C

    @implementation ViewController

    • (void)viewDidLoad { [super viewDidLoad];

      self.client = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY"];

      self.client.connection.delegate = self; // }

    以下是为每个可选协议功能设置具有功能的类的示例:

    Swift

    class DummyDelegate: PusherDelegate {
    func changedConnectionState(from old: ConnectionState, to new: ConnectionState) {
        //
        }

    func debugLog(message: String) { // }

    func subscribedToChannel(name: String) { // }

    func failedToSubscribeToChannel(name: String, response: URLResponse?, data: String?, error: NSError?) { // } }

    Objective-C

    @interface DummyDelegate : NSObject <PusherDelegate>

  • (void)changedConnectionState:(enum ConnectionState)old to:(enum ConnectionState)new_

  • (void)debugLogWithMessage:(NSString *)message

  • (void)subscribedToChannelWithName:(NSString *)name

  • (void)failedToSubscribeToChannelWithName:(NSString *)name response:(NSURLResponse *)response data:(NSString *)data error:(NSError *)error

  • @end

    @implementation DummyDelegate

    • (void)changedConnectionState:(enum ConnectionState)old to:(enum ConnectionState)new_ { // }

    • (void)debugLogWithMessage:(NSString *)message { // }

    • (void)subscribedToChannelWithName:(NSString *)name { // }

    • (void)failedToSubscribeToChannelWithName:(NSString *)name response:(NSURLResponse *)response data:(NSString *)data error:(NSError *)error { // }

    @end

    连接可能处于不同的状态(括号中的Objective-C整数枚举大小写):

    • 连接(0) - 连接即将尝试进行
    • connected(1) - 连接已成功完成
    • disconnecting(2) - 连接已被指示断开连接,即将开始
    • disconnect(3) - 连接断开连接,不会自动重新连接
    • 重新连接(4) - 将尝试尝试重新建立连接
    • reconnectingWhenNetworkBecomesReachable(5) - 当网络可达时,将尝试重新连接

    有一个 stringValue 函数可以调用 ConnectionState 对象,以获取状态的 String 表示形式,例如<代码>连接

    重新连接

    有三种主要的断开连接方式:

    • 客户端明确地呼叫断开连接,并通过websocket连接发送关闭帧
    • 客户端经历某种形式的网络退化,导致心跳(乒乓)消息被丢失,因此客户端断开连接
    • 推送服务器关闭websocket连接;通常这只会在重新启动Pusher套接字服务器时发生,并且几乎立即重新连接应该发生

    在第一种断开连接的情况下,库将(您希望)不尝试重新连接。

    如果存在导致断开连接的网络恶化,则库会嵌入可访问性库,并可以根据不断变化的网络条件自动确定何时尝试重新连接。

    如果Pusher服务器关闭了websocket,则库将尝试重新连接(默认情况下)最多6次,具有指数退避。 reconnectAttemptsMax 的值是 PusherConnection 的公共属性,所以可以根据需要进行更改。

    如果您将 autoReconnect 的客户端选项设置为默认情况下为 true ,则所有这些都是如此。如果重新连接策略不适合您的用例,则可以将 autoReconnect 设置为 false ,并根据连接状态更改实现自己的重新连接策略。

    连接上有几个属性( PusherConnection ),您可以设置它们影响重新连接行为的工作原理。这些是:

    • public var reconnectAttemptsMax:Int? = 6 - 如果将其设置为 nil ,则没有重新连接尝试的最大次数,因此尝试将继续使用指数退避(基于尝试次数),否则在连接状态移动到 .disconnected
    • 之前,只会尝试这个属性值。
    • public var maxReconnectGapInSeconds:Double? = nil - 如果要设置重新连接尝试之间的最大时间长度(以秒为单位),请适当地设置此属性

    请注意,一旦成功连接,重新连接尝试的次数就会重置为0。

    订阅

    公共频道

    订阅频道的默认方法包括调用客户端对象的 subscribe 方法:

    Swift

    let myChannel = pusher.subscribe("my-channel")

    Objective-C

    PusherChannel *myChannel = [pusher subscribeWithChannelName:@"my-channel"];

    这将返回PusherChannel对象,哪些事件可以绑定到。

    私人频道

    私人频道的创建方式与公共频道完全相同,但它们位于私人命名空间中。这意味着在频道名称前缀:

    Swift

    let myPrivateChannel = pusher.subscribe("private-my-channel")

    Objective-C

    PusherChannel *myPrivateChannel = [pusher subscribeWithChannelName:@"private-my-channel"];

    订阅私有渠道涉及客户端的身份验证。有关详细信息,请参阅配置部分了解已验证通道示例。

    存在频道

    存在频道是其名称以存在为前缀的频道

    订阅存在频道的推荐方法是使用 subscribeToPresenceChannel 功能,而不是标准的 subscribe 功能。使用 subscribeToPresenceChannel 函数意味着您得到返回的 PusherPresenceChannel 对象,而不是标准的 PusherChannel 。这个 PusherPresenceChannel 对象具有一些额外的,存在通道特定的功能,例如 member me findMember < / code>。

    Swift

    let myPresenceChannel = pusher.subscribeToPresenceChannel(channelName: "presence-my-channel")

    Objective-C

    PusherPresenceChannel *myPresenceChannel = [pusher subscribeToPresenceChannelWithChannelName:@"presence-my-channel"];
    如上所述,您仍然可以使用 subscribe 方式订阅在线频道,但您返回的频道对象将无法访问直播专属功能,除非您选择将通道对象转换为一个 PusherPresenceChannel

    Swift

    let myPresenceChannel = pusher.subscribe("presence-my-channel")

    Objective-C

    PusherChannel *myPresenceChannel = [pusher subscribeWithChannelName:@"presence-my-channel"];

    您还可以提供在向频道添加或删除成员时将调用的功能。这些可用作 subscribe subscribeToPresenceChannel 的参数。

    Swift

    let onMemberChange = { (member: PusherPresenceChannelMember) in
        print(member)
    }

    let chan = pusher.subscribeToPresenceChannel("presence-channel", onMemberAdded: onMemberChange, onMemberRemoved: onMemberChange)

    Objective-C

    void (^onMemberChange)(PusherPresenceChannelMember*) = ^void (PusherPresenceChannelMember *member) {
        NSLog(@"%@", member);
    };

    PusherChannel *myPresenceChannel = [pusher subscribeWithChannelName:@"presence-my-channel" onMemberAdded:onMemberChange onMemberRemoved:onMemberChange];

    注意成员 myId 属性的 PusherPresenceChannel 对象(以及获取这些值的函数属性)只有在订阅频道成功后才能设置。

    查找频道成功被捕获的最简单的方法是绑定到您感兴趣的频道上的名为 pusher:subscription_succeeded 的事件,看起来像这样:

    Swift

    let pusher = Pusher(key: "YOUR_APP_KEY")

    let chan = pusher.subscribeToPresenceChannel("presence-channel")

    chan.bind(eventName: "pusher:subscription_succeeded", callback: { data in print("Subscribed!") print("I can now access myId: (chan.myId)") print("And here are the channel members: (chan.members)") })

    Objective-C

    Pusher *pusher = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY"];
    PusherPresenceChannel *chan = [pusher subscribeToPresenceChannelWithChannelName:@"presence-channel"];

    [chan bindWithEventName:@"pusher:subscription_succeeded" callback: ^void (NSDictionary *data) { NSLog(@"Subscribed!"); NSLog(@"I can now access myId: %@", chan.myId); NSLog(@"And here are my channel members: %@", chan.members); }];

    您也可以使用作为 PusherDelegate 协议一部分的 subscriptionDidSucceed 代理方法,通知您成功订阅。

    以下是使用委托的一个示例:

    Swift

    class DummyDelegate: PusherDelegate {
        func subscribedToChannel(name: String) {
            if channelName == "presence-channel" {
                if let presChan = pusher.connection.channels.findPresence(channelName) {
                    // in here you can now have access to the channel's members and myId properties
                    print(presChan.members)
                    print(presChan.myId)
                }
            }
        }
    }

    let pusher = Pusher(key: "YOUR_APP_KEY") pusher.connection.delegate = DummyDelegate() let chan = pusher.subscribeToPresenceChannel("presence-channel")

    Objective-C

    @implementation DummyDelegate

    • (void)subscribedToChannelWithName:(NSString *)name { if ([channelName isEqual: @"presence-channel"]) { PusherPresenceChannel *presChan = [self.client.connection.channels findPresenceWithName:@"presence-channel"]; NSLog(@"%@", [presChan members]); NSLog(@"%@", [presChan myId]); } }

    @implementation ViewController

    • (void)viewDidLoad { //

      Pusher *pusher = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY"]; pusher.connection.delegate = [[DummyDelegate alloc] init]; PusherChannel *chan = [pusher subscribeToPresenceChannelWithChannelName:@"presence-channel"];

    请注意,私人和在线频道都要求用户进行身份验证,以便订阅该频道。如果您使用应用程序的秘密配置了Pusher对象,或者在您的Pusher对象实例化时再次对您提供的身份验证端点进行身份验证请求,则此身份验证可能发生在库内。

    我们建议您在绝大多数使用案例中使用身份验证端点,将应用的秘密包含在您的应用中。如果您完全确定您的应用程序的秘密并不存在风险,例如,如果您的应用程序仅供公司内部使用,那么可以使设置更容易,而不是设置身份验证端点。

    绑定到事件

    事件可以绑定到2级;全球和每个频道。绑定到事件时,您可以选择保存返回值,该值是创建的事件处理程序的唯一标识符。唯一的理由是如果你要在稍后的时间点解除事件的束缚。下面是一个例子。

    全球事件

    您可以将事件附加到这些事件,而不考虑广播事件的频道。以下是绑定到任何渠道的新评论的应用程序示例:

    Swift

    let pusher = Pusher(key: "YOUR_APP_KEY")
    pusher.subscribe("my-channel")

    pusher.bind(callback: { (data: Any?) -> Void in if let data = data as? [String : AnyObject] { if let commenter = data["commenter"] as? String, message = data["message"] as? String { print("(commenter) wrote (message)") } } })

    Objective-C

    Pusher *pusher = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY"];
    PusherChannel *chan = [pusher subscribeWithChannelName:@"my-channel"];

    [pusher bind: ^void (NSDictionary *data) { NSString *commenter = data[@"commenter"]; NSString *message = data[@"message"];

    <span class="pl-c1">NSLog</span>(<span class="pl-s"><span class="pl-pds">@&#34;</span><span class="pl-c1">%@</span> wrote <span class="pl-c1">%@</span><span class="pl-pds">&#34;</span></span>, commenter, message);
    

    }];

    每通道事件

    这些绑定到特定通道,意味着您可以在客户端应用程序的不同部分重复使用事件名称。以下可能是股票跟踪应用程序的示例,其中为不同公司打开了几个通道:

    Swift

    let pusher = Pusher(key: "YOUR_APP_KEY")
    let myChannel = pusher.subscribe("my-channel")

    myChannel.bind(eventName: "new-price", callback: { (data: Any?) -> Void in if let data = data as? [String : AnyObject] { if let price = data["price"] as? String, company = data["company"] as? String { print("(company) is now priced at (price)") } } })

    Objective-C

    Pusher *pusher = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY"];
    PusherChannel *chan = [pusher subscribeWithChannelName:@"my-channel"];

    [chan bindWithEventName:@"new-price" callback:^void (NSDictionary *data) { NSString *price = data[@"price"]; NSString *company = data[@"company"];

    <span class="pl-c1">NSLog</span>(<span class="pl-s"><span class="pl-pds">@&#34;</span><span class="pl-c1">%@</span> is now priced at <span class="pl-c1">%@</span><span class="pl-pds">&#34;</span></span>, company, price);
    

    }];

    收到错误

    错误发送到与 pusher:error 的事件名称相关的客户端。这些可以使用以下代码接收和处理。显然,如何处理它们的具体情况由开发人员决定,但这将显示一般模式。

    Swift

    pusher.bind({ (message: Any?) in
        if let message = message as? [String: AnyObject], eventName = message["event"] as? String where eventName == "pusher:error" {
            if let data = message["data"] as? [String: AnyObject], errorMessage = data["message"] as? String {
                print("Error message: (errorMessage)")
            }
        }
    })

    Objective-C

    [pusher bind:^void (NSDictionary *data) {
        NSString *eventName = data[@"event"];

    <span class="pl-k">if</span> ([eventName <span class="pl-c1">isEqualToString:</span><span class="pl-s"><span class="pl-pds">@&#34;</span>pusher:error<span class="pl-pds">&#34;</span></span>]) {
        <span class="pl-c1">NSString</span> *errorMessage = data[<span class="pl-s"><span class="pl-pds">@&#34;</span>data<span class="pl-pds">&#34;</span></span>][<span class="pl-s"><span class="pl-pds">@&#34;</span>message<span class="pl-pds">&#34;</span></span>];
        <span class="pl-c1">NSLog</span>(<span class="pl-s"><span class="pl-pds">@&#34;</span>Error message: <span class="pl-c1">%@</span><span class="pl-pds">&#34;</span></span>, errorMessage);
    }
    

    }];

    您可能会遇到的错误有:

    # if attempting to subscribe to an already subscribed-to channel

    "{&#34;event&#34;:&#34;pusher:error&#34;,&#34;data&#34;:{&#34;code&#34;:null,&#34;message&#34;:&#34;Existing subscription to channel presence-channel&#34;}}"

    # if the auth signature generated by your auth mechanism is invalid

    "{&#34;event&#34;:&#34;pusher:error&#34;,&#34;data&#34;:{&#34;code&#34;:null,&#34;message&#34;:&#34;Invalid signature: Expected HMAC SHA256 hex digest of 200557.5043858:presence-channel:{\&#34;user_id\&#34;:\&#34;200557.5043858\&#34;}, but got 8372e1649cf5a45a2de3cd97fe11d85de80b214243e3a9e9f5cee502fa03f880&#34;}}"

    你可以看到他们采取的一般形式是:

    {
      "event": "pusher:error",
      "data": {
        "code": null,
        "message": "Error message here"
      }
    }

    解除绑定事件处理程序

    您可以使用 unbind 功能从对象中删除先前绑定的处理程序。例如,

    Swift

    let pusher = Pusher(key: "YOUR_APP_KEY")
    let myChannel = pusher.subscribe("my-channel")

    let eventHandlerId = myChannel.bind(eventName: "new-price", callback: { (data: Any?) -> Void in })

    myChannel.unbind(eventName: "new-price", callbackId: eventHandlerId)

    Objective-C

    Pusher *pusher = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY"];
    PusherChannel *chan = [pusher subscribeWithChannelName:@"my-channel"];

    NSString *callbackId = [chan bindWithEventName:@"new-price" callback:^void (NSDictionary *data) { … }];

    [chan unbindWithEventName:@"new-price" callbackId:callbackId];

    您可以在全局和每个通道级别解除事件的绑定。对于这两个对象,您还可以选择调用 unbindAll ,您可以猜到,它将取消绑定对象上的所有eventHandler。

    推送通知

    Pusher还支持推送通知。您的应用实例可以注册推送通知并订阅兴趣。然后,您的服务器可以发布到那些将作为推送通知发送到您的应用程序的兴趣。有关友好的介绍,请参见我们的APN推送通知指南

    初始化Pusher对象

    您应该在您的 AppDelegate 中设置推送通知的应用。根据您是否使用Swift或Objective-C,以及是否使用iOS或MacOS(OS X),设置会稍有不同:

    Swift on iOS

    import PusherSwift
    import UserNotifications

    @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { let pusher = Pusher(key: "YOUR_APP_KEY")

    Objective-C on iOS

    #import "AppDelegate.h"
    @import UserNotifications;

    @interface AppDelegate ()

    @end

    @implementation AppDelegate

    Swift on macOS

    import Cocoa
    import PusherSwift

    @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate, PusherDelegate { let pusher = Pusher(key: "YOUR_APPKEY") //

    注册APN

    为了让您的应用接收推送通知,必须首先向APN注册。您应该在应用程序完成启动时执行此操作。您的应用应注册所有类型的通知,如:

    Swift on iOS

    func application( application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
        let center = UNUserNotificationCenter.current()
        center.requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in
            // Enable or disable features based on authorization.
        }
        application.registerForRemoteNotifications()

    <span class="pl-k">return</span> <span class="pl-c1">true</span>
    

    }

    Objective-C on iOS

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        self.pusher = [[Pusher alloc] initWithKey:@"YOUR_APP_KEY"];

    UNUserNotificationCenter *center = [UNUserNotificationCenter <span class="pl-c1">currentNotificationCenter</span>];
    [center <span class="pl-c1">requestAuthorizationWithOptions:</span>(UNAuthorizationOptionBadge | UNAuthorizationOptionAlert | UNAuthorizationOptionSound) <span class="pl-c1">completionHandler:</span>^(<span class="pl-c1">BOOL</span> granted, <span class="pl-c1">NSError</span> * _Nullable error) {
        <span class="pl-c"><span class="pl-c">//</span> Enable or disable features based on authorization.</span>
    }];
    
    [application <span class="pl-c1">registerForRemoteNotifications</span>];
    <span class="pl-k">return</span> <span class="pl-c1">YES</span>;
    

    }

    Swift on macOS

    func applicationDidFinishLaunching( aNotification: Notification) {
        NSApp.registerForRemoteNotifications(matching: [NSRemoteNotificationType.alert, NSRemoteNotificationType.sound, NSRemoteNotificationType.badge])
    }

    接收您的APN设备令牌并使用Pusher进行注册

    接下来,APN将使用标识您的应用实例的设备令牌进行响应。您的应用程序应该会随Pusher注册,传递其设备令牌。

    您的应用程式现在可以订阅兴趣。以下注册和订阅应用程序的兴趣甜甜圈:

    Swift on iOS

    func application( application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        pusher.nativePusher.register(deviceToken: deviceToken)
        pusher.nativePusher.subscribe(interestName: "donuts")
    }

    Objective-C on iOS

    - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
        NSLog(@"Registered for remote notifications; received device token");
        [[[self pusher] nativePusher] registerWithDeviceToken:deviceToken];
        [[[self pusher] nativePusher] subscribeWithInterestName:@"donuts"];
    }

    Swift on macOS

    func application( application: NSApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        self.pusher.nativePusher.register(deviceToken: deviceToken)
        self.pusher.nativePusher.subscribe(interestName: "donuts")
    }

    接收推送通知

    当您的服务器发布关于甜甜圈的通知时,它将被传递到您的应用程序。这种情况发生在您应该听的 AppDelegate 中的调用:

    Swift on iOS

    func application( application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        print(userInfo)
    }

    Objective-C on iOS

    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
        NSLog(@"Received remote notification: %@", userInfo);
    }

    Swift on macOS

    func application(_ application: NSApplication, didReceiveRemoteNotification userInfo: [String: Any]) {
        print("Received remote notification: (userInfo.debugDescription)" )
    }

    取消订阅兴趣

    如果您稍后想取消订阅兴趣,则可以以相同的方式工作:

    Swift

    pusher.nativePusher.unsubscribe(interestName: "donuts")

    Objective-C

    [[[self pusher] nativePusher] unsubscribeWithInterestName:@"donuts"];

    有关工作应用程序的完整示例,请参阅示例/ 目录。特别针对推送通知代码,请参阅示例/ AppDelegate.swift 文件。

    推送代表

    您还可以实现一些 PusherDelegate 函数来访问与推送通知交互相关的事件。这些是在符合 PusherDelegate 协议时可以选择实现的功能:

    @objc optional func registeredForPushNotifications(clientId: String)
    @objc optional func failedToRegisterForPushNotifications(response: URLResponse, responseBody: String?)
    @objc optional func subscribedToInterest(name: String)
    @objc optional func unsubscribedFromInterest(name: String)

    同样,函数的名称大部分放弃了它们的目的,只是为了完整性:

    • registeredForPushNotifications - 如果您想知道客户端何时成功注册了推送推送通知服务,或者您希望访问返回的 clientId 成功注册
    • failedToRegisterForPushNotifications - 如果您想知道客户端何时未能使用推送推送通知服务注册,请使用此功能
    • subscribedToInterest - 如果您想要跟踪成功订阅的兴趣
    • unsubscribedFromInterest - 如果您想要追踪已成功取消订阅的兴趣

    设置代理如下所示:

    Swift

    class ViewController: UIViewController, PusherDelegate {

    <span class="pl-k">override</span> <span class="pl-k">func</span> <span class="pl-en">viewDidLoad</span>() {
        <span class="pl-c1">super</span>.<span class="pl-c1">viewDidLoad</span>()
        <span class="pl-k">let</span> pusher <span class="pl-k">=</span> <span class="pl-c1">Pusher</span>(<span class="pl-c1">key</span>: <span class="pl-s"><span class="pl-pds">&#34;</span>APP_KEY<span class="pl-pds">&#34;</span></span>)
        pusher.<span class="pl-smi">delegate</span> <span class="pl-k">=</span> <span class="pl-c1">self</span>
        <span class="pl-c"><span class="pl-c">//</span> ...</span>
    

    } }

    Objective-C

    @implementation ViewController

    • (void)viewDidLoad { [super viewDidLoad];

      self.client = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY"];

      self.client.delegate = self; // }

    该过程与设置 PusherDelegate 的过程相同,以接收基于连接的事件的通知。

    测试

    可以使用标准方法(Xcode中的Command-U)运行库的一组测试。

    测试也可以在 Travis-CI 上运行。有关Travis测试如何运行的详细信息,请参阅 .travis.yml

    扩展名

    通讯

    • If you have found a bug, please open an issue.
    • If you have a feature request, please open an issue.
    • If you want to contribute, please submit a pull request (preferrably with some tests