Pusher Channels Swift 客户端(也适用于 Objective-C)

Build Status codecov Latest Release CocoaPods API Docs Supported Platforms Swift Versions Twitter GitHub license

这是 Pusher Channels websocket 客户端,PusherSwift,它支持 iOS、macOS (OS X) 和 tvOS。它适用于 Swift 和 Objective-C。

有关 Pusher Channels 的教程和更深入的信息,请访问我们的官方文档

支持的平台

部署目标

遗留操作系统支持

如果您需要支持旧版本的 iOS、macOS 或 tvOS,请使用 SDK 的最新 v8.x 版本。

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

您还想要什么?前往我们的示例应用之一

目录

安装

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', '~> 10.1.0'

然后,运行以下命令

$ pod install

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

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

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

Carthage

Carthage 是一个去中心化的依赖管理器,它可以自动执行将框架添加到您的 Cocoa 应用程序的过程。

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

$ brew update
$ brew install carthage

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

github "pusher/pusher-websocket-swift"

Carthage 将生成多个框架。您需要从 Carthage/Build 目录中将以下框架二进制文件包含在您的项目中:PusherSwiftNWWebSocketTweetNacl

Xcode 12 注意事项

在 Xcode 12.0 及更高版本下构建通用框架时,包含的架构已发生更改。这是为了支持 Apple Silicon 系列处理器的引入。

强烈建议您使用 --use-xcframeworks 标志集成 PusherSwift,并运行 Carthage 0.37.0 或更高版本。对此有完整说明(以及如果您已经使用 Carthage 集成,则迁移到 XCFrameworks的说明)。

或者,如果您使用 Intel Mac 构建,并且不想迁移以使用 XCFrameworks 构建 Carthage 依赖项,则有一个解决方法可以成功构建。您可以在此处找到此解决方法的示例,该示例用于运行“Consumption-Tests”。

Swift Package Manager

要使用 Swift Package Manager 将 PusherSwift 集成到您的项目中,您可以在 Xcode 中将该库添加为依赖项 – 请参阅文档。包仓库 URL 是

https://github.com/pusher/pusher-websocket-swift.git

或者,您可以将 PusherSwift 作为依赖项添加到您的 Package.swift 文件中。例如

// swift-tools-version:5.1
import PackageDescription

let package = Package(
    name: "YourPackage",
    products: [
        .library(
            name: "YourPackage",
            targets: ["YourPackage"]),
    ],
    dependencies: [
        .package(url: "https://github.com/pusher/pusher-websocket-swift.git", from: "10.1.0"),
    ],
    targets: [
        .target(
            name: "YourPackage",
            dependencies: ["PusherSwift"]),
    ]
)

然后,您需要在任何要使用 SDK 的源文件中包含 import PusherSwift 语句。

配置

Pusher 客户端有许多配置参数可以设置。对于 Swift 用法,它们是

查看旧版配置选项

authMethod 参数的类型必须为 AuthMethod。这是一个枚举,定义为

public enum AuthMethod {
    case endpoint(authEndpoint: String)
    case authRequestBuilder(authRequestBuilder: AuthRequestBuilderProtocol)
    case inline(secret: String)
    case authorizer(authorizer: Authorizer)
    case noMethod
}

这是 AuthRequestBuilderProtocol 定义

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

这是 Authorizer 协议定义

public protocol Authorizer {
    func fetchAuthValue(socketID: String, channelName: String, completionHandler: (PusherAuth?) -> ())
}

其中 PusherAuth 定义为

public class PusherAuth: NSObject {
    public let auth: String
    public let channelData: String?
    public let sharedSecret: String?

    public init(auth: String, channelData: String? = nil, sharedSecret: String? = nil) {
        self.auth = auth
        self.channelData = channelData
        self.sharedSecret = sharedSecret
    }
}

如果授权过程成功,您需要使用 PusherAuth 对象调用提供的 completionHandler,以便订阅过程可以完成。

如果由于任何原因您的授权过程失败,那么您只需要调用 completionHandler,并将 nil 作为唯一参数。

请注意,如果您想指定要连接到的集群,请按如下方式使用 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
                                autoReconnect:YES
                                ocHost:host
                                port:nil
                                useTLS:YES
                                activityTimeout:nil];

所有这些配置选项都需要传递给 PusherClientOptions 对象,该对象又需要在实例化 Pusher 对象时传递给 Pusher 对象,例如

Swift

let options = PusherClientOptions(
    authMethod: .endpoint(authEndpoint: "https://: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
                                autoReconnect:YES
                                ocHost:host
                                port:nil
                                useTLS:YES
                                activityTimeout:nil];
pusher = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY" options:options];

您可能已经注意到,这对于 Objective-C 用法略有不同。主要更改是您需要使用 OCAuthMethodOCPusherHost 代替 AuthMethodPusherHostOCAuthMethod 类具有以下功能,您可以在 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: "https://: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:@"https://:9292/pusher/auth"]];
    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL: [[NSURL alloc] initWithString:@"https://: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];

其中 "Authorization""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() 触发连接过程开始。

重要提示:您必须保持对 Pusher 客户端的强引用。例如,您可以通过将 pusher 设置为应用程序委托的属性来实现这一点。

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

您可以像这样设置它

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)
@objc(receivedError:) optional func receivedError(error: PusherError)
@objc optional func failedToDecryptEvent(eventName: String, channelName: String, data: String?)

这些函数的名称在很大程度上说明了它们的用途,但为了完整起见

设置委托看起来像这样

Swift

class ViewController: UIViewController, PusherDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        let pusher = Pusher(key: "APP_KEY")
        pusher.connection.delegate = self
        // ...
    }
}

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?) {
        // ...
    }

    func receivedError(error: PusherError) {
        let message = error.message
        if let code = error.code {
            // ...
        }
    }

    func failedToDecryptEvent(eventName: String, channelName: String, data: String?) {
      // ...
    }

}

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
- (void)receivedError:(PusherError *)error
- (void)failedToDecryptEventWithEventName:(NSString *)eventName channelName:(NSString *)channelName data:(NSString *)data

@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 {
    // ...
}

- (void)receivedError:(PusherError *)error {
    NSNumber *code = error.codeOC;
    NSString *message = error.message;
    // ...
}

- (void)failedToDecryptEventWithEventName:(NSString *)eventName channelName:(NSString *)channelName data:(NSString *)data {
  // ...
}

@end

连接可能处于的不同状态是(Objective-C 整数枚举情况在括号中)

有一个 stringValue() 函数,您可以在 ConnectionState 对象上调用该函数,以便获取状态的 String 表示形式,例如 "connecting"

重新连接

断开连接主要有三种方式

在第一种类型的断开连接情况下,库将(正如您所期望的)不会尝试重新连接。

该库使用 NWWebSocket,它尝试检测导致断开连接的网络降级事件。如果检测到这种情况,则库将(默认情况下)尝试使用指数退避无限期地重新连接(默认情况下,重新连接尝试之间的最大时间上限为 120 秒)。reconnectAttemptsMax 的值是 PusherConnection 上的公共属性,因此如果您希望设置最大重新连接尝试次数,可以更改它。

如果 Pusher 服务器关闭 websocket,或者由于 NWWebSocket 未涵盖的网络事件而发生断开连接,则库仍将尝试如上所述重新连接。

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

注意:如果 Pusher 服务器使用 Channels 协议关闭代码关闭 websocket,则 autoReconnect 选项将被忽略,并且重新连接策略由收到的特定关闭代码确定。

连接 (PusherConnection) 上有两个属性可以设置,它们会影响重新连接行为的工作方式。这些是

请注意,一旦成功建立连接,重新连接尝试次数将重置为 0。

订阅

公共频道

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

Swift

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

Objective-C

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

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

对于非 Presence 频道,您还可以提供一个函数,当客户端订阅或取消订阅频道时,将调用该函数,并将订阅者数量作为参数。此外,此函数可用作 subscribe 函数的参数。

let onSubscriptionCountChanged = { (count: Int) in
    print("\(count) subscriptions")
}

let channel = pusher.subscribe(
    channelName: "my-channel",
    onSubscriptionCountChanged: onSubscriptionCountChanged
)

私有频道

私有频道的创建方式与公共频道完全相同,只是它们位于“private-”命名空间中。这意味着在频道名称前加上前缀

Swift

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

Objective-C

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

订阅私有频道涉及客户端的身份验证。有关更多信息,请参阅配置部分中的已验证频道示例。

私有加密频道

与私有频道类似,您也可以订阅私有加密频道。此库现在完全支持端到端加密。这意味着只有您和您连接的客户端才能读取您的消息。Pusher 无法解密它们。

与私有频道一样,您必须提供身份验证端点。该端点必须使用支持端到端加密的服务器客户端。有一个演示端点,用于查看如何使用 nodejs

用于解密事件的共享密钥是从用于授权您的订阅的同一身份验证端点请求中加载的。如果您的加密主密钥发生更改,还有一种重新加载共享密钥的机制。如果遇到无法解密的事件,则会向您的身份验证端点发出请求,以尝试加载新的共享密钥。如果该请求失败,或者返回的密钥仍然无法解密该事件,则将跳过该事件,将调用 failedToDecryptEvent 连接委托函数,并且将处理下一个收到的事件。

由于需要按需重新加载共享密钥,因此您只能使用以下身份验证方法endpointauthRequestBuilderauthorizer。如果您要订阅加密频道,则无法将 PusherAuth 的实例传递给 subscribe 函数。

局限性

Swift

let privateEncryptedChannel = pusher.subscribe(channelName: "private-encrypted-my-channel")

Objective-C

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

连接委托中还有一个可选的回调,当您可以监听任何解密失败事件时

optional func failedToDecryptEvent(eventName: String, channelName: String, data: String?)

Presence 频道

Presence 频道是名称以 presence- 为前缀的频道。

订阅 Presence 频道的推荐方法是使用 subscribeToPresenceChannel 函数,而不是标准的 subscribe 函数。使用 subscribeToPresenceChannel 函数意味着您将获得返回的 PusherPresenceChannel 对象,而不是标准的 PusherChannel。此 PusherPresenceChannel 对象具有一些额外的 Presence 频道特定功能,例如 membersmefindMember

Swift

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

Objective-C

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

如前所述,您仍然可以使用 subscribe 方法订阅 Presence 频道,但您获得的频道对象将无法访问 Presence 频道特定功能,除非您选择将频道对象强制转换为 PusherPresenceChannel

Swift

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

Objective-C

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

您还可以提供在成员添加到频道或从频道中删除时将调用的函数。这些函数可用作 subscribesubscribeToPresenceChannel 的参数。

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

注意PusherPresenceChannel 对象的 membersmyId 属性(以及获取这些属性值的函数)仅在成功订阅频道后才会设置。

找出频道何时成功订阅的最简单方法是绑定到您感兴趣的频道上名为 pusher:subscription_succeeded 的事件。它看起来像这样

Swift

let pusher = Pusher(key: "YOUR_APP_KEY")

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

chan.bind(eventName: "pusher:subscription_succeeded", eventCallback: { event 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" eventCallback: ^void (PusherEvent *event) {
    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"];

请注意,私有频道和 Presence 频道都需要用户进行身份验证才能订阅频道。如果您使用应用程序密钥配置了 Pusher 对象,则此身份验证可以在库内部发生,或者向您提供的身份验证端点发出身份验证请求,同样是在实例化 Pusher 对象时。

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

使用自行提供的身份验证值进行订阅

可以通过在调用 subscribesubscribeToPresenceChannel 时提供身份验证信息来订阅需要身份验证的频道。这样做如下所示

Swift

let pusherAuth = PusherAuth(auth: yourAuthString, channelData: yourOptionalChannelDataString)
let chan = self.pusher.subscribe(channelName, auth: pusherAuth)

如果订阅的是私有频道,则可以使用仅包含身份验证 (String) 值的 PusherAuth 对象进行初始化,如果订阅的是 Presence 频道,则可以使用包含 auth (String)channelData (String) 值对的 PusherAuth 对象进行初始化。

这些 authchannelData 值是您在调用我们的各种服务器库之一中的 pusher.authenticate(...) 创建的 json 对象时收到的值。

请记住,为了为订阅生成有效的身份验证值,在生成身份验证值时,必须存在 socketId(即与 Pusher 服务器的 websocket 连接的唯一标识符)。因此,使用此功能的可能流程类似于在尝试订阅任何需要身份验证的频道之前,检查连接状态何时变为 connected

绑定到事件

事件可以在 2 个级别绑定:全局和按频道。在绑定到事件时,您可以选择保存返回值,该返回值是为创建的事件处理程序提供的唯一标识符。保存此标识符的唯一原因是为了在稍后的时间点取消绑定事件。下面有一个示例。

按频道事件

这些事件绑定到特定频道,这意味着您可以在客户端应用程序的不同部分重用事件名称。

Swift

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

myChannel.bind(eventName: "new-price", eventCallback: { (event: PusherEvent) -> Void in
    if let data: String = event.data {
        // `data` is a string that you can parse if necessary.
    }
})

回调传递一个 PusherEvent(请参阅文档)。

查看旧版方法
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" eventCallback:^void (PusherEvent *event) {
    NSString *data = event.data;
    // `data` is a string that you can parse if necessary.
}];
查看旧版方法
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"];

    NSLog(@"%@ is now priced at %@", company, price);
}];

全局事件

您可以将行为附加到这些事件,而无需考虑事件广播到的频道。

Swift

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

pusher.bind(eventCallback: { (event: PusherEvent) -> Void in
    if let data: String = event.data {
        // `data` is a string that you can parse if necessary.
    }
})

回调传递一个 PusherEvent(请参阅文档)。

查看旧版方法
let pusher = Pusher(key: "YOUR_APP_KEY")
pusher.subscribe("my-channel")

pusher.bind(callback: { (event: Any?) -> Void in
    if let data = event["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 bindWithEventCallback: ^void (PusherEvent *event) {
    // `data` is a string that you can parse if necessary.
    NSString *data = event.data;
}];
查看旧版方法
Pusher *pusher = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY"];
PusherChannel *chan = [pusher subscribeWithChannelName:@"my-channel"];

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

    NSLog(@"%@ wrote %@", commenter, message);
}];

回调参数

PusherEvent

您绑定的回调接收一个 PusherEvent

属性 类型 描述
eventName String 事件的名称。
channelName String? 触发事件的频道的名称。
data String? 传递给 trigger 的数据,编码为字符串。如果您传递了一个对象,那么该对象将被序列化为 JSON 字符串,您可以根据需要解析该字符串。请参阅解析事件数据
userId String? 触发事件的用户的 ID。这仅适用于在 Presence 频道上触发的客户端事件。
函数 参数 返回类型 描述
property withKey: String - 属性的键 Any? 用于从 websocket 事件访问原始属性的辅助函数。此函数返回的数据不应被视为稳定,建议您改用上面的属性。

解析事件数据

PusherEventdata 属性包含您在触发事件时传递的数据的字符串表示形式。如果您传递了一个对象,那么该对象将被序列化为 JSON。您可以根据需要解析该 JSON。您可以使用 JSONSerialization,或者您可以使用 JSONDecoder 将 JSON 解码为 Codable Class 或 Struct。请参阅 Apple 文档:编码和解码自定义类型

例如,以下可能是一个股票跟踪应用程序发布公司价格更新的示例。您可以将“price-update”事件解码为 Swift 中的结构体

struct PriceUpdate: Codable {
    public let company: String,
    public let price: Int,
}

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

myChannel.bind(eventName: "price-update", eventCallback: { (event: PusherEvent) -> Void in
    guard let json: String = event.data,
        let jsonData: Data = json.data(using: .utf8)
    else{
        print("Could not convert JSON string to data")
        return
    }

    let decoded = try? decoder.decode(PriceUpdate.self, from: jsonData)
    guard let priceUpdate = decoded else {
        print("Could not decode price update")
        return
    }

    print("\(priceUpdate.company) is now priced at \(priceUpdate.price)")
})

或者,您可以使用 JSONSerialization 将 JSON 解码为 Swift 数据类型

Swift

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

myChannel.bind(eventName: "price-update", eventCallback: { (event: PusherEvent) -> Void in
    guard let json: String = event.data,
        let jsonData: Data = json.data(using: .utf8)
    else{
        print("Could not convert JSON string to data")
        return
    }

    let decoded = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any]
    guard let priceUpdate = decoded else {
        print("Could not decode price update")
        return
    }

    if let company = priceUpdate["company"] as? String, let price = priceUpdate["price"] 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:@"price-update" eventCallback:^void (PusherEvent *event) {
    NSString *dataString = event.data;
    NSData *data = [dataString dataUsingEncoding:NSUTF8StringEncoding];

    NSError *error;
    NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];

    NSString *price = jsonObject[@"price"];
    NSString *company = jsonObject[@"company"];

    NSLog(@"%@ is now priced at %@", company, price);
}];

接收错误

可以通过连接委托访问从 Pusher Channels 接收的错误。以前是通过绑定回调来完成的。

查看旧版方法

错误会发送到与之相关的客户端,事件名称为 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"];

    if ([eventName isEqualToString:@"pusher:error"]) {
        NSString *errorMessage = data[@"data"][@"message"];
        NSLog(@"Error message: %@", errorMessage);
    }
}];

您可能会收到以下类型的错误

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

"{\"event\":\"pusher:error\",\"data\":{\"code\":null,\"message\":\"Existing subscription to channel presence-channel\"}}"

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

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

您可以看到它们采用的一般形式是

{
  "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", eventCallback: { (event: PusherEvent) -> 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" eventCallback:^void (PusherEvent *event) {
    //...
}];

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

您可以从全局级别和按频道级别取消绑定事件。对于这两个对象,您还可以选择调用 unbindAll,顾名思义,这将取消绑定对象上的所有 eventHandler。

触发事件

一旦 私有Presence 订阅获得授权(请参阅验证用户)并且订阅成功,就可以在这些频道上触发事件。

chan.trigger(eventName: "client-myEvent", data: ["myName": "Bob"])

客户端触发的事件称为客户端事件。由于它们是从可能不受信任的客户端触发的,因此在使用它们时会强制执行许多规则。其中一些规则包括

有关完整详细信息,请参阅客户端事件文档

测试

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

测试也在 Github Actions 上运行,请参阅 CI Action

扩展

通信

鸣谢

PusherSwift 由 Pusher 拥有和维护。它最初由 Hamilton Chapman 创建。

它使用了以下存储库中的代码

这些库的各个许可证包含在相应的 Swift 文件中。

许可证

PusherSwift 在 MIT 许可证下发布。有关详细信息,请参阅 LICENSE