Ably iOS、tvOS 和 macOS Objective-C 和 Swift 客户端库 SDK
Ably 是一个平台,它可以实现实时同步的数字体验。无论是参加虚拟场所的活动,接收实时金融信息,还是监控实时汽车性能数据,消费者都期望标准化的实时数字体验。每月,Ably 为 80 个国家的超过 2.5 亿台设备提供一套 API,以构建、扩展和实时提供强大的数字体验。像 Bloomberg、HubSpot、Verizon 和 Hopin 这样的组织依赖 Ably 的平台来进行全球规模的业务关键实时数据同步的日益增长的复杂性。有关更多信息,请参阅 Ably 文档。
这是为 Ably 编写的 iOS、tvOS 和 macOS Objective-C 和 Swift 客户端库 SDK,用 Objective-C 编写。库目前针对的 Ably 客户端库功能规范 版本 1.2。您可以通过跳转到 '已知的局限性' 部分来看这个客户端库目前不支持的功能,或者 查看我们的客户端库 SDK 功能支持矩阵,以查看所有可用的功能列表。
支持的平台
此 SDK 与以下目标项目兼容
- iOS 10.0+
- tvOS 10.0+
- macOS 10.12+
我们保持与这些平台版本的兼容性,并明确支持这些版本。
我们不明确维护与旧平台版本的兼容性。关于旧版本已知的不兼容性,可以在以下位置找到:这里。
如果您找到与不支持的平台版本相关的问题,请在此存储库中提出问题,或联系 Ably 客户支持寻求建议。
持续集成测试
我们在以下操作系统上执行 CI 测试
- iOS 16.2
- tvOS 16.1
- 由 GitHub Actions 的
macos-latest
运行器镜像所指定的 macOS 版本。
已知限制
此客户端库当前不与 Ably 的以下功能兼容
功能 |
---|
自定义 transportParams |
记住失败时的回退主机 |
错误信息 URL 有助于调试问题 |
文档
访问ably.com/docs,以获取完整的API参考和更多示例。
安装指南
您可以通过软件包管理器、CocoaPods、Carthage或手动方式安装Ably for iOS和macOS。
Swift Package Manager安装
通过- 要将
ably-cocoa
软件包安装到您的Xcode项目中- 将
https://github.com/ably/ably-cocoa
粘贴到Swift Packages搜索框中。(在项目下→Swift Packages .. →+
按钮) - 选择为您的目标选择
Ably
SDK。 - 本Apple指南详细说明了这些步骤。
- 将
- 要在其他Swift包中安装ably-cocoa软件包,请将以下内容添加到您的
Package.swift
.package(url: "https://github.com/ably/ably-cocoa", from: "1.2.20"),
CocoaPods安装
通过如果您打算使用Swift,建议在Podfile中包含use_frameworks!
(这将创建一个可以原生在Swift中使用的框架)。
将此行添加到您应用程序的Podfile中
# For Xcode 7.3 and newer
pod 'Ably', '>= 1.2'
然后安装依赖项
$ pod install
Carthage安装
通过将此行添加到您的应用程序的 Cartfile 中
# For Xcode 7.3 and newer
github "ably/ably-cocoa" >= 1.2
然后运行
- iOS:
carthage update --use-xcframeworks --platform iOS --no-use-binaries
- macOS:
carthage update --use-xcframeworks --platform macOS --no-use-binaries
- tvOS:
carthage update --use-xcframeworks --platform tvOS --no-use-binaries
来构建框架并将生成的 (在 [项目根目录]/Carthage/Build
)
Ably.xcframework
AblyDeltaCodec.xcframework
msgpack.xcframework
拖入您的 Xcode 项目中。
如果例如您看到 dyld: Library not loaded: @rpath/AblyDeltaCodec.framework/AblyDeltaCodec
错误,那么很可能您忘记将所有依赖项添加到项目中了。您可以在 此处 找到更多信息。
注意: 对于 macOS 目标,您必须在选择您的目标时选择 不嵌入
选项,并确保 Ably.xcframework
出现在 构建阶段
选项卡中的 链接二进制与库
列表中。
手动安装
- 从 GitHub 的发布页 获取代码,或克隆它以获取最新、不稳定且可能未充分文档化的版本:
git clone [email protected]:ably/ably-cocoa.git
- 将目录
ably-cocoa/ably-cocoa
作为组拖入您的项目中。 - Ably 依赖于我们的 MessagePack Fork 0.2.0;从 发布页面 获取它,并将其链接到您的项目中。
线程安全
该库提供以下线程安全保证
- 整个公共接口可以从任何线程安全地访问,无论是读取还是写入。
- 由库返回的 "值" 对象(例如
ARTTokenDetails
,来自消息的数据)可以从和写入是安全的。 - 传递给库的对象之后不得进行修改。它们可以安全地传递或读取;库不会将数据写入它们。
所有内部操作都调度到单个串行 GCD 队列。您可以使用 ARTClientOptions.internalDispatchQueue
指定自定义队列。
默认情况下,将用户提供的回调的所有调用调度到主队列。这允许您通过执行 UI 操作直接响应 Ably 的输出。您可以使用 ARTClientOptions.dispatchQueue
指定不同的队列。这个队列不应该与 ARTClientOptions.internalDispatchQueue
相同,因为这可能导致死锁。
推送通知
如果您还没有,请首先查看详细的文档。推送通知的示例应用程序也可用。
激活和设备注册
更多信息,请参阅推送通知 - 设备激活和订阅。
ARTPushRegistererDelegate
定义了处理推送激活、去激活和更新事件的3个委托方法。默认情况下,Ably SDK将检查是否UIApplication.sharedApplication.delegate
遵循ARTPushRegistererDelegate
,并在适当的时候调用委托方法。因此,指定ARTPushRegistererDelegate
是可选的。要使用实现了ARTPushRegistererDelegate
的另一个类,必须通过设置ARTClientOptions#pushRegistererDelegate
委托将其提供给Ably。在SwiftUI应用程序中,必须设置ARTClientOptions#pushRegistererDelegate
委托属性。
不要忘记,ARTPush
有两个对应的方法,您应从您的application(_:didRegisterForRemoteNotificationsWithDeviceToken:)和application(_:didFailToRegisterForRemoteNotificationsWithError:)中调用这些方法,并将ARTRest
或ARTRealtime
实例传递给它们,该实例已配置您所需的身份验证设置和其他选项
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
ARTPush.didRegisterForRemoteNotifications(withDeviceToken: deviceToken, rest: rest)
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
ARTPush.didFailToRegisterForRemoteNotificationsWithError(error, rest: rest)
}
在任一时刻只能激活一个ARTRest
或ARTRealtime
实例以接收推送通知。
在不必要的时刻激活多个实例可能导致意外的后果。
macOS & tvOS
请注意,目前不支持macOS和tvOS的推送通知。您可以只能使用推送管理功能,例如
let recipient: [String: Any] = [
"clientId": "C04BC116-8004-4D78-A71F-8CA3122734DB"
]
let data: [String: Any] = [
"notification": [
"title": "Hello from Ably!",
"body": "Example push notification from Ably."
],
"data": [
"foo": "bar",
"baz": "qux"
]
]
realtime.push.admin.publish(recipient, data: data) { error in
print("Push published:", error ?? "nil")
}
使用实时API
简介
所有示例都假设已经创建了一个客户端,如下所示
Swift
// basic auth with an API key
let client = ARTRealtime(key: "xxxx:xxxx")
// using token auth
let client = ARTRealtime(token: "xxxx")
Objective-C
// basic auth with an API key
ARTRealtime* client = [[ARTRealtime alloc] initWithKey:@"xxxx:xxxx"];
// using token auth
ARTRealtime* client = [[ARTRealtime alloc] initWithToken:@"xxxx"];
连接
通过实例化 ARTRealtime
默认开始一个连接。你可以通过监听连接状态变化来捕获连接成功或错误
Swift
client.connection.on { stateChange in
let stateChange = stateChange!
switch stateChange.current {
case .Connected:
print("connected!")
case .Failed:
print("failed! \(stateChange.reason)")
default:
break
}
}
Objective-C
[client.connection on:^(ARTConnectionStateChange *stateChange) {
switch (stateChange.current) {
case ARTRealtimeConnected:
NSLog(@"connected!");
break;
case ARTRealtimeFailed:
NSLog(@"failed! %@", stateChange.reason);
break;
default:
break;
}
}];
你也可以通过设置适当的选项手动连接。
Swift
let options = ARTClientOptions(key: "xxxx:xxxx")
options.autoConnect = false
let client = ARTRealtime(options: options)
client.connection.connect()
Objective-C
ARTClientOptions *options = [[ARTClientOptions alloc] initWithKey:@"xxxx:xxxx"];
options.autoConnect = false;
ARTRealtime *client = [[ARTRealtime alloc] initWithOptions:options];
[client.connection connect];
订阅频道
给定
Swift
let channel = client.channels.get("test")
Objective-C
ARTRealtimeChannel *channel = [client.channels get:@"test"];
订阅所有事件
Swift
channel.subscribe { message in
print(message.name)
print(message.data)
}
Objective-C
[channel subscribe:^(ARTMessage *message) {
NSLog(@"%@", message.name);
NSLog(@"%@", message.data);
}];
仅订阅某些事件
Swift
channel.subscribe("myEvent") { message in
print(message.name)
print(message.data)
}
Objective-C
[channel subscribe:@"myEvent" callback:^(ARTMessage *message) {
NSLog(@"%@", message.name);
NSLog(@"%@", message.data);
}];
以增量模式订阅频道
在delta模式下订阅频道可以启用delta压缩。这是一种客户端订阅频道的方式,消息有效负载只包含当前消息和通道上之前消息之间的差异(即delta)。
获取频道时,使用频道选项请求Vcdiff格式的delta流。
Swift
let channelOptions = ARTRealtimeChannelOptions()
channelOptions.params = [
"delta": "vcdiff"
]
let channel = client.channels.get("test", options: channelOptions)
Objective-C
ARTRealtimeChannelOptions *channelOptions = [[ARTRealtimeChannelOptions alloc] init];
channelOptions.params = @{
@"delta": @"vcdiff"
};
ARTRealtimeChannel *channel = [client.channels get:@"test" options:channelOptions];
除了指定频道选项之外,其余内容是透明的,无需对您的应用程序进行任何进一步更改。发送到您的订阅回调函数的message.data
实例仍然包含最初发布的值。
如果您想检查ARTMessage
实例以确定它们呈现的data
是否由Ably的delta消息渲染,您可以查看message.extras["delta"]["format"]
是否等于"vcdiff"
。
发布到频道
Swift
channel.publish("greeting", data: "Hello World!")
Objective-C
[channel publish:@"greeting" data:@"Hello World!"];
查询历史
Swift
channel.history { messagesPage, error in
let messagesPage = messagesPage!
print(messagesPage.items)
print(messagesPage.items.first)
print((messagesPage.items.first as? ARTMessage)?.data) // payload for the message
print(messagesPage.items.count) // number of messages in the current page of history
messagesPage.next { nextPage, error in
// retrieved the next page in nextPage
}
print(messagesPage.hasNext) // true, there are more pages
}
Objective-C
[channel history:^(ARTPaginatedResult<ARTMessage *> *messagesPage, ARTErrorInfo *error) {
NSLog(@"%@", messagesPage.items);
NSLog(@"%@", messagesPage.items.firstObject);
NSLog(@"%@", messagesPage.items.firstObject.data); // payload for the message
NSLog(@"%lu", (unsigned long)[messagesPage.items count]); // number of messages in the current page of history
[messagesPage next:^(ARTPaginatedResult<ARTMessage *> *nextPage, ARTErrorInfo *error) {
// retrieved the next page in nextPage
}];
NSLog(@"%d", messagesPage.hasNext); // true, there are more pages
}];
频道上的存在状态
Swift
let channel = client.channels.get("test")
channel.presence.enter("john.doe") { errorInfo in
channel.presence.get { members, errorInfo in
// members is the array of members present
}
}
Objective-C
[channel.presence enter:@"john.doe" callback:^(ARTErrorInfo *errorInfo) {
[channel.presence get:^(ARTPaginatedResult<ARTPresenceMessage *> *result, ARTErrorInfo *error) {
// members is the array of members present
}];
}];
查询存在状态历史
Swift
channel.presence.history { presencePage, error in
let presencePage = presencePage!
if let first = presencePage.items.first as? ARTPresenceMessage {
print(first.action) // Any of .Enter, .Update or .Leave
print(first.clientId) // client ID of member
print(first.data) // optional data payload of member
presencePage.next { nextPage, error in
// retrieved the next page in nextPage
}
}
}
Objective-C
[channel.presence history:^(ARTPaginatedResult<ARTPresenceMessage *> *presencePage, ARTErrorInfo *error) {
ARTPresenceMessage *first = (ARTPresenceMessage *)presencePage.items.firstObject;
NSLog(@"%lu", (unsigned long)first.action); // Any of ARTPresenceEnter, ARTPresenceUpdate or ARTPresenceLeave
NSLog(@"%@", first.clientId); // client ID of member
NSLog(@"%@", first.data); // optional data payload of member
[presencePage next:^(ARTPaginatedResult<ARTPresenceMessage *> *nextPage, ARTErrorInfo *error) {
// retrieved the next page in nextPage
}];
}];
authCallback
使用 调用以获取已签名令牌请求的回调。
可以根据以下方式实例化 ARTClientOptions
和 ARTRealtime
对象
Swift
let clientOptions = ARTClientOptions()
clientOptions.authCallback = { params, callback in
getTokenRequestJSONFromYourServer(params) { json, error in
//handle error
do {
callback(try ARTTokenRequest.fromJson(json), nil)
} catch let error as NSError {
callback(nil, error)
}
}
}
let client = ARTRealtime(options:clientOptions)
Objective-C
ARTClientOptions *clientOptions = [[ARTClientOptions alloc] init];
clientOptions.authCallback = ^(ARTTokenParams *params, void(^callback)(id<ARTTokenDetailsCompatible>, NSError*)) {
[self getTokenRequestJSONFromYourServer:params completion:^(NSDictionary *json, NSError *error) {
//handle error
ARTTokenRequest *tokenRequest = [ARTTokenRequest fromJson:json error:&error];
callback(tokenRequest, error);
}];
};
ARTRealtime *client = [[ARTRealtime alloc] initWithOptions:clientOptions];
使用 REST API
介绍
所有示例假设已经创建了客户端和/或频道,如下所示
Swift
let client = ARTRest(key: "xxxx:xxxx")
let channel = client.channels.get("test")
Objective-C
ARTRest *client = [[ARTRest alloc] initWithKey:@"xxxx:xxxx"];
ARTRestChannel *channel = [client.channels get:@"test"];
向频道发布消息
Swift
channel.publish("myEvent", data: "Hello!")
Objective-C
[channel publish:@"myEvent" data:@"Hello!"];
查询历史记录
Swift
channel.history { messagesPage, error in
let messagesPage = messagesPage!
print(messagesPage.items.first)
print((messagesPage.items.first as? ARTMessage)?.data) // payload for the message
messagesPage.next { nextPage, error in
// retrieved the next page in nextPage
}
print(messagesPage.hasNext) // true, there are more pages
}
Objective-C
[channel history:^(ARTPaginatedResult<ARTMessage *> *messagesPage, ARTErrorInfo *error) {
NSLog(@"%@", messagesPage.items.firstObject);
NSLog(@"%@", messagesPage.items.firstObject.data); // payload for the message
NSLog(@"%lu", (unsigned long)[messagesPage.items count]); // number of messages in the current page of history
[messagesPage next:^(ARTPaginatedResult<ARTMessage *> *nextPage, ARTErrorInfo *error) {
// retrieved the next page in nextPage
}];
NSLog(@"%d", messagesPage.hasNext); // true, there are more pages
}];
频道中的存在状态
Swift
channel.presence.get { membersPage, error in
let membersPage = membersPage!
print(membersPage.items.first)
print((membersPage.items.first as? ARTPresenceMessage)?.data) // payload for the message
membersPage.next { nextPage, error in
// retrieved the next page in nextPage
}
print(membersPage.hasNext) // true, there are more pages
}
Objective-C
[channel.presence get:^(ARTPaginatedResult<ARTPresenceMessage *> *membersPage, ARTErrorInfo *error) {
NSLog(@"%@", membersPage.items.firstObject);
NSLog(@"%@", membersPage.items.firstObject.data); // payload for the message
[membersPage next:^(ARTPaginatedResult<ARTMessage *> *nextPage, ARTErrorInfo *error) {
// retrieved the next page in nextPage
}];
NSLog(@"%d", membersPage.hasNext); // true, there are more pages
}];
查询存在历史
Swift
channel.presence.history { presencePage, error in
let presencePage = presencePage!
if let first = presencePage.items.first as? ARTPresenceMessage {
print(first.clientId) // client ID of member
presencePage.next { nextPage, error in
// retrieved the next page in nextPage
}
}
}
Objective-C
[channel.presence history:^(ARTPaginatedResult<ARTPresenceMessage *> *presencePage, ARTErrorInfo *error) {
ARTPresenceMessage *first = (ARTPresenceMessage *)presencePage.items.firstObject;
NSLog(@"%@", first.clientId); // client ID of member
NSLog(@"%@", first.data); // optional data payload of member
[presencePage next:^(ARTPaginatedResult<ARTPresenceMessage *> *nextPage, ARTErrorInfo *error) {
// retrieved the next page in nextPage
}];
}];
生成令牌
Swift
client.auth.requestToken(nil, withOptions: nil) { tokenDetails, error in
let tokenDetails = tokenDetails!
print(tokenDetails.token) // "xVLyHw.CLchevH3hF....MDh9ZC_Q"
let client = ARTRest(token: tokenDetails.token)
}
Objective-C
[client.auth requestToken:nil withOptions:nil callback:^(ARTTokenDetails *tokenDetails, NSError *error) {
NSLog(@"%@", tokenDetails.token); // "xVLyHw.CLchevH3hF....MDh9ZC_Q"
ARTRest *client = [[ARTRest alloc] initWithToken:tokenDetails.token];
}];
检索您的应用统计
Swift
client.stats { statsPage, error in
let statsPage = statsPage!
print(statsPage.items.first)
statsPage.next { nextPage, error in
// retrieved the next page in nextPage
}
}
Objective-C
[client stats:^(ARTPaginatedResult<ARTStats *> *statsPage, ARTErrorInfo *error) {
NSLog(@"%@", statsPage.items.firstObject);
[statsPage next:^(ARTPaginatedResult<ARTStats *> *nextPage, ARTErrorInfo *error) {
// retrieved the next page in nextPage
}];
}];
检索Ably服务时间
Swift
client.time { time, error in
print(time) // 2016-02-09 03:59:24 +0000
}
Objective-C
[client time:^(NSDate *time, NSError *error) {
NSLog(@"%@", time); // 2016-02-09 03:59:24 +0000
}];
支持、反馈和故障排除
请访问https://support.ably.com/以访问我们的知识库并寻求任何协助。
您还可以查看社区报告的 Github 问题。
贡献
详细信息请见CONTRIBUTING.md。