Ably iOS、tvOS 和 macOS Objective-C 和 Swift 客户端库 SDK

Check Pod Integration Test: iOS 14.4 Integration Test: macOS 10.15 Integration Test: tvOS 14.3 Features

CocoaPods SPM Swift Compatibility SPM Platform Compatibility

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 测试

已知限制

此客户端库目前与某些 Ably 功能不兼容

功能
自定义 transportParams
记住失败期间的备用主机
ErrorInfo URL 用于帮助调试问题

文档

访问 ably.com/docs 以获取完整的 API 参考和更多示例。

安装指南

您可以通过包管理器、CocoaPods、Carthage 或手动安装适用于 iOS 和 macOS 的 Ably。

通过 Swift Package Manager 安装

 .package(url: "https://github.com/ably/ably-cocoa", from: "1.2.39"),

通过 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

然后运行

来构建框架,并将构建好的 (在 [PROJECT_ROOT]/Carthage/Build 中) 拖到

到 Xcode 目标设置的 General 选项卡的“Frameworks, Libraries, and Embedded Content”部分。如果您的目标是一个应用程序,请选择“Embed & Sign”,否则选择“Do Not Embed”。

如果您看到,例如,一个 dyld: Library not loaded: @rpath/AblyDeltaCodec.framework/AblyDeltaCodec 错误,那么很可能您忘记将所有依赖项添加到您的项目中。您可以在这里获得更详细的信息。

手动安装

  1. 从 GitHub 从发布页面获取代码,或者克隆它以获取最新的、不稳定的和可能未充分记录的版本:git clone git@github.com:ably/ably-cocoa.git
  2. 将目录 ably-cocoa/ably-cocoa 作为组拖到您的项目中。
  3. Ably 依赖于我们的 MessagePack Fork 0.2.0;从发布页面获取它并将其链接到您的项目中。

线程安全

该库做出以下线程安全保证

所有内部操作都分派到单个串行 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:) 中调用的方法,并且还要将一个配置了认证设置和所需其它选项的 ARTRestARTRealtime 实例传递给它们。

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)
}

一次只能激活一个 ARTRestARTRealtime 实例来 接收推送通知。一次激活多个实例可能会产生意想不到的后果。

位置推送

从 iOS 15 开始,Apple 支持通过 位置推送服务扩展 来高效地请求位置。

有关如何实现此功能的示例,请参阅 嵌入 APNs 示例应用程序

macOS & tvOS

请注意,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")
}

可用演示:macOStvOS

使用 Realtime 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 压缩。这是一种客户端订阅频道的方式,以便发送的消息有效负载仅包含当前消息和频道上先前消息之间的差异(即 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
}];

频道上的 Presence

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

查询 presence 历史记录

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

一个用于获取签名令牌请求的回调。
可以按如下方式实例化 ARTClientOptionsARTRealtime 对象

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

频道上的 Presence

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

查询 presence 历史记录

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