这是 Pusher Channels websocket 客户端,PusherSwift,它支持 iOS、macOS (OS X) 和 tvOS。它适用于 Swift 和 Objective-C。
有关 Pusher Channels 的教程和更深入的信息,请访问我们的官方文档。
如果您需要支持旧版本的 iOS、macOS 或 tvOS,请使用 SDK 的最新 v8.x 版本。
您还想要什么?前往我们的示例应用之一
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 是一个去中心化的依赖管理器,它可以自动执行将框架添加到您的 Cocoa 应用程序的过程。
您可以使用 Homebrew 安装 Carthage,使用以下命令
$ brew update
$ brew install carthage
要使用 Carthage 将 PusherSwift 集成到您的 Xcode 项目中,请在您的 Cartfile
中指定它
github "pusher/pusher-websocket-swift"
Carthage 将生成多个框架。您需要从 Carthage/Build
目录中将以下框架二进制文件包含在您的项目中:PusherSwift
、NWWebSocket
和 TweetNacl
在 Xcode 12.0 及更高版本下构建通用框架时,包含的架构已发生更改。这是为了支持 Apple Silicon 系列处理器的引入。
强烈建议您使用 --use-xcframeworks
标志集成 PusherSwift,并运行 Carthage 0.37.0
或更高版本。对此有完整说明(以及如果您已经使用 Carthage 集成,则迁移到 XCFrameworks的说明)。
或者,如果您使用 Intel Mac 构建,并且不想迁移以使用 XCFrameworks 构建 Carthage 依赖项,则有一个解决方法可以成功构建。您可以在此处找到此解决方法的示例,该示例用于运行“Consumption-Tests”。
要使用 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)
- 您希望客户端使用的方法,用于验证对需要身份验证的频道的订阅请求(更多详细信息请参见下文)useTLS (Bool)
- 是否要使用 TLS 加密传输,默认为 true
autoReconnect (Bool)
- 设置是否希望库在断开连接后尝试自动重新连接(如果可能)。有关更多信息,请参阅重新连接host (PusherHost)
- 为您要连接的主机设置自定义值,例如 PusherHost.host("ws-test.pusher.com")
port (Int)
- 为您要连接的端口设置自定义值activityTimeout (TimeInterval)
- 在此时间(以秒为单位)后,如果没有从服务器收到任何消息,将发送 ping 消息以检查连接是否仍然有效;默认值由服务器提供,较低的值将导致不必要的流量。attemptToReturnJSONObject (Bool)
- 是否希望库尝试将您的数据解析为 JSON(或者不解析,只返回一个字符串)authMethod
参数的类型必须为 AuthMethod
。这是一个枚举,定义为
public enum AuthMethod {
case endpoint(authEndpoint: String)
case authRequestBuilder(authRequestBuilder: AuthRequestBuilderProtocol)
case inline(secret: String)
case authorizer(authorizer: Authorizer)
case noMethod
}
endpoint(authEndpoint: String)
- 客户端将向您指定的端点发出 POST
请求,其中包含客户端的 socket ID 和尝试订阅的频道名称authRequestBuilder(authRequestBuilder: AuthRequestBuilderProtocol)
- 您指定一个符合 AuthRequestBuilderProtocol
(定义如下)的对象,该对象必须生成一个 URLRequest
对象,该对象将用于发出身份验证请求inline(secret: String)
- 您的应用程序密钥,这样身份验证请求就不需要发送到您的身份验证端点,而是可以直接在库中验证订阅(这主要用于开发)authorizer(authorizer: Authorizer)
- 您指定一个符合 Authorizer
协议的对象,该对象必须能够提供适当的身份验证信息noMethod
- 如果您仅使用公共频道,则不需要设置 authMethod
(这是默认值)这是 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
属性
let options = PusherClientOptions(
host: .cluster("eu")
)
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 对象,例如
let options = PusherClientOptions(
authMethod: .endpoint(authEndpoint: "https://:9292/pusher/auth")
)
let pusher = Pusher(key: "APP_KEY", options: options)
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 用法略有不同。主要更改是您需要使用 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"];
已验证频道示例
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
)
@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 连接
let pusher = Pusher(key: "APP_KEY")
pusher.connect()
Pusher *pusher = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY"];
[pusher connect];
这将返回一个客户端对象,然后可以使用该对象订阅频道,然后调用 connect()
触发连接过程开始。
重要提示:您必须保持对 Pusher
客户端的强引用。例如,您可以通过将 pusher
设置为应用程序委托的属性来实现这一点。
您还可以在连接对象上设置 userDataFetcher
。
userDataFetcher (() -> PusherPresenceChannelMember)
- 如果您正在订阅已验证的频道,并且希望提供一个函数来返回用户数据您可以像这样设置它
let pusher = Pusher(key: "APP_KEY")
pusher.connection.userDataFetcher = { () -> PusherPresenceChannelMember in
return PusherPresenceChannelMember(userId: "123", userInfo: ["twitter": "hamchapman"])
}
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?)
这些函数的名称在很大程度上说明了它们的用途,但为了完整起见
changedConnectionState
- 如果您想使用连接状态更改来执行不同的操作/UI 更新,请使用此功能subscribedToChannel
- 如果您想在频道成功订阅后收到通知,请使用此功能,如果您想在订阅成功后执行仅相关的操作(例如,注销 Presence 频道的成员)时,此功能很有用failedToSubscribeToChannel
- 如果您想收到订阅尝试失败的通知,请使用此功能,例如,您可以使用它来尝试另一次订阅或调用您用来跟踪错误的服务debugLog
- 如果您想记录 Pusher 相关事件,例如,底层 websocket 接收消息,请使用此功能receivedError
- 如果您想收到来自 Pusher Channels 的错误通知,例如 Application is over connection quota
。您可以在此处找到一些可能的错误列表。failedToDecryptEvent
- 仅与私有加密频道一起使用 - 如果您想在任何消息解密失败时收到通知,请使用此功能。设置委托看起来像这样
class ViewController: UIViewController, PusherDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let pusher = Pusher(key: "APP_KEY")
pusher.connection.delegate = self
// ...
}
}
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.client = [[Pusher alloc] initWithAppKey:@"YOUR_APP_KEY"];
self.client.connection.delegate = self;
// ...
}
以下是设置一个类,其中包含每个可选协议功能的函数的示例
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?) {
// ...
}
}
@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 整数枚举情况在括号中)
connecting (0)
- 连接即将尝试建立connected (1)
- 连接已成功建立disconnecting (2)
- 连接已指示断开连接,并且即将断开连接disconnected (3)
- 连接已断开,并且不会尝试自动重新连接reconnecting (4)
- 即将尝试重新建立连接有一个 stringValue()
函数,您可以在 ConnectionState
对象上调用该函数,以便获取状态的 String
表示形式,例如 "connecting"
。
断开连接主要有三种方式
在第一种类型的断开连接情况下,库将(正如您所期望的)不会尝试重新连接。
该库使用 NWWebSocket,它尝试检测导致断开连接的网络降级事件。如果检测到这种情况,则库将(默认情况下)尝试使用指数退避无限期地重新连接(默认情况下,重新连接尝试之间的最大时间上限为 120 秒)。reconnectAttemptsMax
的值是 PusherConnection
上的公共属性,因此如果您希望设置最大重新连接尝试次数,可以更改它。
如果 Pusher 服务器关闭 websocket,或者由于 NWWebSocket 未涵盖的网络事件而发生断开连接,则库仍将尝试如上所述重新连接。
如果您的客户端选项 autoReconnect
设置为 true
(默认情况下是这样),则所有这些情况都适用。如果重新连接策略不适合您的用例,则可以将 autoReconnect
设置为 false
,并根据连接状态更改实现您自己的重新连接策略。
注意:如果 Pusher 服务器使用 Channels 协议关闭代码关闭 websocket,则 autoReconnect
选项将被忽略,并且重新连接策略由收到的特定关闭代码确定。
连接 (PusherConnection
) 上有两个属性可以设置,它们会影响重新连接行为的工作方式。这些是
public var reconnectAttemptsMax: Int? = 6
- 如果您将其设置为 nil
,则重新连接尝试次数没有上限,因此将继续使用指数退避(基于尝试次数)进行尝试,否则只会进行此属性值所指定的尝试次数,然后连接的状态将变为 .disconnected
public var maxReconnectGapInSeconds: Double? = nil
- 如果您想设置重新连接尝试之间的最大时间长度(以秒为单位),请适当地设置此属性请注意,一旦成功建立连接,重新连接尝试次数将重置为 0。
订阅频道的默认方法是调用客户端对象的 subscribe
方法
let myChannel = pusher.subscribe("my-channel")
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-”命名空间中。这意味着在频道名称前加上前缀
let myPrivateChannel = pusher.subscribe("private-my-channel")
PusherChannel *myPrivateChannel = [pusher subscribeWithChannelName:@"private-my-channel"];
订阅私有频道涉及客户端的身份验证。有关更多信息,请参阅配置部分中的已验证频道示例。
与私有频道类似,您也可以订阅私有加密频道。此库现在完全支持端到端加密。这意味着只有您和您连接的客户端才能读取您的消息。Pusher 无法解密它们。
与私有频道一样,您必须提供身份验证端点。该端点必须使用支持端到端加密的服务器客户端。有一个演示端点,用于查看如何使用 nodejs。
用于解密事件的共享密钥是从用于授权您的订阅的同一身份验证端点请求中加载的。如果您的加密主密钥发生更改,还有一种重新加载共享密钥的机制。如果遇到无法解密的事件,则会向您的身份验证端点发出请求,以尝试加载新的共享密钥。如果该请求失败,或者返回的密钥仍然无法解密该事件,则将跳过该事件,将调用 failedToDecryptEvent
连接委托函数,并且将处理下一个收到的事件。
由于需要按需重新加载共享密钥,因此您只能使用以下身份验证方法:endpoint
、authRequestBuilder
、authorizer
。如果您要订阅加密频道,则无法将 PusherAuth
的实例传递给 subscribe
函数。
let privateEncryptedChannel = pusher.subscribe(channelName: "private-encrypted-my-channel")
PusherChannel *privateEncryptedChannel = [pusher subscribeWithChannelName:@"private-encrypted-my-channel"];
连接委托中还有一个可选的回调,当您可以监听任何解密失败事件时
optional func failedToDecryptEvent(eventName: String, channelName: String, data: String?)
Presence 频道是名称以 presence-
为前缀的频道。
订阅 Presence 频道的推荐方法是使用 subscribeToPresenceChannel
函数,而不是标准的 subscribe
函数。使用 subscribeToPresenceChannel
函数意味着您将获得返回的 PusherPresenceChannel
对象,而不是标准的 PusherChannel
。此 PusherPresenceChannel
对象具有一些额外的 Presence 频道特定功能,例如 members
、me
和 findMember
。
let myPresenceChannel = pusher.subscribeToPresenceChannel(channelName: "presence-my-channel")
PusherPresenceChannel *myPresenceChannel = [pusher subscribeToPresenceChannelWithChannelName:@"presence-my-channel"];
如前所述,您仍然可以使用 subscribe
方法订阅 Presence 频道,但您获得的频道对象将无法访问 Presence 频道特定功能,除非您选择将频道对象强制转换为 PusherPresenceChannel
。
let myPresenceChannel = pusher.subscribe("presence-my-channel")
PusherChannel *myPresenceChannel = [pusher subscribeWithChannelName:@"presence-my-channel"];
您还可以提供在成员添加到频道或从频道中删除时将调用的函数。这些函数可用作 subscribe
和 subscribeToPresenceChannel
的参数。
let onMemberChange = { (member: PusherPresenceChannelMember) in
print(member)
}
let chan = pusher.subscribeToPresenceChannel("presence-channel", onMemberAdded: onMemberChange, onMemberRemoved: onMemberChange)
void (^onMemberChange)(PusherPresenceChannelMember*) = ^void (PusherPresenceChannelMember *member) {
NSLog(@"%@", member);
};
PusherChannel *myPresenceChannel = [pusher subscribeWithChannelName:@"presence-my-channel" onMemberAdded:onMemberChange onMemberRemoved:onMemberChange];
注意:PusherPresenceChannel
对象的 members
和 myId
属性(以及获取这些属性值的函数)仅在成功订阅频道后才会设置。
找出频道何时成功订阅的最简单方法是绑定到您感兴趣的频道上名为 pusher:subscription_succeeded
的事件。它看起来像这样
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)")
})
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
委托方法来接收成功订阅的通知。
以下是使用委托的示例
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")
@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 对象时。
我们建议您在绝大多数用例中使用身份验证端点,而不是在您的应用程序中包含您的应用程序密钥。如果您完全确定在您的应用程序中包含您的应用程序密钥对您没有任何风险,例如,如果您的应用程序仅供您公司内部使用,那么它可以比设置身份验证端点更容易。
可以通过在调用 subscribe
或 subscribeToPresenceChannel
时提供身份验证信息来订阅需要身份验证的频道。这样做如下所示
let pusherAuth = PusherAuth(auth: yourAuthString, channelData: yourOptionalChannelDataString)
let chan = self.pusher.subscribe(channelName, auth: pusherAuth)
如果订阅的是私有频道,则可以使用仅包含身份验证 (String) 值的 PusherAuth 对象进行初始化,如果订阅的是 Presence 频道,则可以使用包含 auth (String)
和 channelData (String)
值对的 PusherAuth 对象进行初始化。
这些 auth
和 channelData
值是您在调用我们的各种服务器库之一中的 pusher.authenticate(...) 创建的 json 对象时收到的值。
请记住,为了为订阅生成有效的身份验证值,在生成身份验证值时,必须存在 socketId
(即与 Pusher 服务器的 websocket 连接的唯一标识符)。因此,使用此功能的可能流程类似于在尝试订阅任何需要身份验证的频道之前,检查连接状态何时变为 connected
。
事件可以在 2 个级别绑定:全局和按频道。在绑定到事件时,您可以选择保存返回值,该返回值是为创建的事件处理程序提供的唯一标识符。保存此标识符的唯一原因是为了在稍后的时间点取消绑定事件。下面有一个示例。
这些事件绑定到特定频道,这意味着您可以在客户端应用程序的不同部分重用事件名称。
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)")
}
}
})
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);
}];
您可以将行为附加到这些事件,而无需考虑事件广播到的频道。
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)")
}
}
})
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
属性 | 类型 | 描述 |
---|---|---|
eventName |
String |
事件的名称。 |
channelName |
String? |
触发事件的频道的名称。 |
data |
String? |
传递给 trigger 的数据,编码为字符串。如果您传递了一个对象,那么该对象将被序列化为 JSON 字符串,您可以根据需要解析该字符串。请参阅解析事件数据。 |
userId |
String? |
触发事件的用户的 ID。这仅适用于在 Presence 频道上触发的客户端事件。 |
函数 | 参数 | 返回类型 | 描述 |
---|---|---|---|
property |
withKey: String - 属性的键 |
Any? |
用于从 websocket 事件访问原始属性的辅助函数。此函数返回的数据不应被视为稳定,建议您改用上面的属性。 |
PusherEvent
的 data
属性包含您在触发事件时传递的数据的字符串表示形式。如果您传递了一个对象,那么该对象将被序列化为 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 数据类型
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)")
}
})
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
。可以使用以下代码接收和处理这些错误。显然,如何处理它们的具体细节留给开发人员,但这显示了一般模式。
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)")
}
}
})
[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
函数从对象中删除先前绑定的处理程序。例如,
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)
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"])
客户端触发的事件称为客户端事件。由于它们是从可能不受信任的客户端触发的,因此在使用它们时会强制执行许多规则。其中一些规则包括
client-
前缀有关完整详细信息,请参阅客户端事件文档。
有一组库的测试可以使用标准方法(在 Xcode 中按 Command-U)运行。
测试也在 Github Actions 上运行,请参阅 CI Action
PusherSwift 由 Pusher 拥有和维护。它最初由 Hamilton Chapman 创建。
它使用了以下存储库中的代码
这些库的各个许可证包含在相应的 Swift 文件中。
PusherSwift 在 MIT 许可证下发布。有关详细信息,请参阅 LICENSE。