Ably 是一个为实时同步数字体验提供支持的平台。无论是参加虚拟场所的活动、接收实时金融信息,还是监控实时汽车性能数据——消费者都希望将实时数字体验作为标准。Ably 提供了一套 API,用于构建、扩展和交付强大的实时数字体验,每月为 80 个国家/地区的超过 2.5 亿台设备提供服务。Bloomberg、HubSpot、Verizon 和 Hopin 等组织依赖 Ably 的平台来卸载在全球范围内至关重要的实时数据同步的日益增长的复杂性。有关更多信息,请参阅 Ably 文档。
这是一个用于 Ably 的 iOS、tvOS 和 macOS Objective-C 和 Swift 客户端库 SDK,使用 Objective-C 编写。该库当前面向 Ably 客户端库功能规范 1.2 版本。您可以跳转到“已知限制”部分,查看此客户端库尚不支持的功能。
此 SDK 与面向以下目标的项目兼容
我们维护兼容性并明确支持这些平台版本。
我们没有明确维护与旧平台版本的兼容性。任何已知的与旧版本的不兼容性都可以在这里找到。
如果您发现任何不支持的平台版本的问题,请在此存储库中提出问题或联系 Ably 客户支持寻求建议。
我们在以下操作系统上执行 CI 测试
macos-13
runner image指定的 macOS 版本。此客户端库目前与某些 Ably 功能不兼容
功能 |
---|
自定义 transportParams |
记住失败期间的备用主机 |
ErrorInfo URL 用于帮助调试问题 |
访问 ably.com/docs 以获取完整的 API 参考和更多示例。
您可以通过包管理器、CocoaPods、Carthage 或手动安装适用于 iOS 和 macOS 的 Ably。
ably-cocoa
包https://github.com/ably/ably-cocoa
粘贴到Swift Packages搜索框中。(Xcode 项目→Swift Packages... → +
按钮)Ably
SDK。ably-cocoa
包,请将以下内容添加到您的 Package.Swift
中 .package(url: "https://github.com/ably/ably-cocoa", from: "1.2.39"),
如果您打算使用 Swift,建议在 Podfile 中使用 use_frameworks!
(这将创建一个可以本机在 Swift 中使用的框架)。
将此行添加到您的应用程序的 Podfile 中
# For Xcode 7.3 and newer
pod 'Ably', '>= 1.2'
然后安装依赖项
$ pod install
将此行添加到您的应用程序的 Cartfile 中
# For Xcode 7.3 and newer
github "ably/ably-cocoa" >= 1.2
然后运行
carthage update --use-xcframeworks --platform iOS --no-use-binaries
carthage update --use-xcframeworks --platform macOS --no-use-binaries
carthage update --use-xcframeworks --platform tvOS --no-use-binaries
来构建框架,并将构建好的 (在 [PROJECT_ROOT]/Carthage/Build
中) 拖到
Ably.xcframework
AblyDeltaCodec.xcframework
msgpack.xcframework
到 Xcode 目标设置的 General 选项卡的“Frameworks, Libraries, and Embedded Content”部分。如果您的目标是一个应用程序,请选择“Embed & Sign”,否则选择“Do Not Embed”。
如果您看到,例如,一个 dyld: Library not loaded: @rpath/AblyDeltaCodec.framework/AblyDeltaCodec
错误,那么很可能您忘记将所有依赖项添加到您的项目中。您可以在这里获得更详细的信息。
git clone git@github.com:ably/ably-cocoa.git
ably-cocoa/ably-cocoa
作为组拖到您的项目中。该库做出以下线程安全保证
ARTTokenDetails
、消息中的数据)可以安全地读取和写入。所有内部操作都分派到单个串行 GCD 队列。您可以为此指定一个自定义队列,该队列必须是串行的,使用 ARTClientOptions.internalDispatchQueue
。
默认情况下,对用户提供的回调的所有调用都将分派到主队列。这允许您通过直接执行 UI 操作来响应 Ably 的输出。您可以使用 ARTClientOptions.dispatchQueue
指定不同的队列。它不应与 ARTClientOptions.internalDispatchQueue
的队列相同,因为这可能会导致死锁。
如果您尚未这样做,您应该首先查看详细的文档。还有一个可用的 推送通知示例应用程序。
有关更多信息,请参阅 推送通知 - 设备激活和订阅。
ARTPushRegistererDelegate
定义了 4 个委托方法来处理推送激活、停用和更新事件的结果。默认情况下,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
实例来 接收推送通知。一次激活多个实例可能会产生意想不到的后果。
从 iOS 15 开始,Apple 支持通过 位置推送服务扩展 来高效地请求位置。
要接收位置推送通知,您需要专门为位置推送目的请求另一个设备令牌。您可以通过在 ARTPushRegistererDelegate.didActivateAblyPush(:)
委托方法(它是 ARTRealtime.push.activate()
调用的回调)中调用 CLLocationManager.startMonitoringLocationPushes(completion:)
来执行此操作。
一旦您收到位置推送令牌,请通过调用新的 ARTPush.didRegisterForLocationNotifications(withDeviceToken:realtime:)
方法来保存它(注意此方法名称中的“Location”一词)。Ably 将在成功或未能保存令牌时调用 ARTPushRegistererDelegate.didUpdateAblyPush:
回调。
有关如何实现此功能的示例,请参阅 嵌入 APNs 示例应用程序。
请注意,macOS 和 tvOS 当前不支持推送通知。您只能使用 Push Admin 功能,例如
let recipient: [String: Any] = [
"clientId": "C04BC116-8004-4D78-A71F-8CA31.2.35DB"
]
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")
}
所有示例都假定已按以下方式创建客户端
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
}];
}];
一个用于获取签名令牌请求的回调。
可以按如下方式实例化 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];
所有示例都假定已按以下方式创建客户端和/或频道
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
}];
}];
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。