Sendbird SyncManager for iOS
目录
简介
Sendbird SyncManager for iOS 是一个基于Chat SDK的附加组件,通过事件驱动结构将本地数据存储与 Sendbird 服务器中的聊天数据同步,优化了用户缓存体验。
它的工作原理
SyncManager 利用本地缓存并同步本地存储与 Sendbird 服务器间的聊天数据。通过事件驱动结构处理操作,该附加组件提供了简化的 Chat SDK 集成和更好的用户体验。
操作
- 后台同步 发生在有连接的情况下,会自动将从 Sendbird 服务器获取的数据存储到本地缓存中。
- 实时同步 持续进行;它识别、存储和传送从 WebSocket 连接接收到的实时事件。
- 离线模式 确保在离线模式下客户端应用程序可以正常运行,这意味着即使没有后台同步,视图也可以显示缓存的数据。
关于 Sendbird SyncManager for iOS 的更多信息
在 SyncManager for iOS 文档 中了解更多关于 Sendbird SyncManager for iOS 的信息。如果您对错误和功能请求有任何评论或问题,请访问 Sendbird 社区。
开始之前
本节展示了您需要检查的使用 Sendbird SyncManager for iOS 的前提条件。
要求
SyncManager for iOS 的最低要求是
- iOS 8.0+
- Sendbird Chat SDK for iOS v3.0.178+
入门指南
本节提供了您在 Sendbird SyncManager for iOS 入门所需的信息。
试用示例应用程序
下载示例应用程序以测试 SyncManager for iOS 的核心功能。
注意:测试我们的 SyncManager 最快的方式是在我们的示例应用程序之上构建你的聊天应用程序。请确保将示例应用程序的应用程序 ID 更改为自己的。前往从仪表板创建 Sendbird 应用程序部分了解详细信息。
从 CocoaPods 安装 SendBirdSyncManager 框架
将以下内容添加到你的 Xcode 的 Podfile。
platform :ios, '8.0'
use_frameworks!
target YOUR_PROJECT_TARGET do
pod 'SendBirdSyncManager'
end
通过 CocoaPods
安装 SendBirdSyncManager
框架。
pod install
通过 CocoaPods
更新 SendBirdSyncManager
框架。
pod update SyncManager
现在,你通过检查 YOUR_PROJECT.xcworkspace
可以看到已安装的 SendBirdSyncManager
框架。
注意:
SendBirdSyncManager
依赖于SendBird SDK
。如果你安装了SendBirdSyncManager
,CocoaPods
会自动安装SendBird SDK
。并且,SendBird SDK
的最小版本是 3.0.203。
从 Carthage 安装 SendBirdSyncManager 框架
- 将
github "sendbird/sendbird-syncmanager-ios"
添加到你的Cartfile
。 - 运行
carthage update
。 - 转到 Xcode 项目的 通用 设置。在 Finder 中打开
<YOUR_XCODE_PROJECT_DIRECTORY>/Carthage/Build/iOS
并将SendBirdSyncManager.framework
拖到 Xcode 的 已嵌入的二进制文件 部分。确保选择了如有必要则复制项目
,并点击 完成。
实现指南
初始化
SBSMSyncManager
是一个单例类。当 SBSMSyncManager
初始化时,会创建一个Database
实例。因此,如果想尽快初始化Database
,可以在获取到用户ID后立即调用setup(_:)
。我们建议在application(_:didFinishLaunchingWithOptions:)
中执行。
// swift
// AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// after getting user's ID or login
SBSMSyncManager.setup(withUserId: userId)
}
// objective-c
// AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// after getting user's ID or login
[SBSMSyncManager setupWithUserId:userId];
}];
集合
Collection
是用于管理与视图相关的Sendbird对象(如SBDGroupChannel
,SBDBaseMessage
)的容器。SBSMChannelCollection
通常附加到频道列表视图控制器,而SBSMMessageCollection
则附加到消息列表视图控制器。Collection
的主要目的是:
- 监听数据事件并将其作为视图事件交付。
- 从缓存或Sendbird服务器获取数据,并将数据作为视图事件交付。
每个集合都有自己的事件订阅者和数据获取者。事件订阅者监听数据事件,以便将这些数据更新应用到视图中;而数据获取者则从缓存或服务器加载数据,并将数据发送给事件处理器。
- 频道集合
频道是当前进行聊天的可变数据。由于许多应用程序按最后一条消息对频道进行排序,因此频道最后一条消息的未读消息数量频繁更新,每个频道的位置也会发生剧烈变化。因此,SBSMChannelCollection
主要依赖于服务器同步。以下是SBSMChannelCollection
同步数据的步骤:
- 它从缓存中加载频道,并将它们显示在视图中。
- 然后,它从Sendbird服务器获取最新的频道,并将其与视图中的频道合并。
- 每次在
fetch(_:)
中调用以查看以前的频道时,它都会从Sendbird服务器获取数据。
注意:频道数据同步机制可能会在以后更改。
SBSMChannelCollection
需要一个来自Sendbird SDK的SBDGroupChannelListQuery
实例,因为它是将查询绑定到集合中。然后,集合使用查询过滤数据。下面是创建新的SBSMChannelCollection
实例的代码。通常在分组频道列表视图控制器的viewDidLoad()
中创建频道集合。
// swift
override func viewDidLoad() {
let query: SBDGroupChannelListQuery? = SBDGroupChannel.createMyGroupChannelListQuery()
// limit, order, ... setup your query here.
let channelCollection: SBSMChannelCollection? = SBSMChannelCollection.init(query: query)
self.channelCollection? = channelCollection // Recommands to set a property of view controller
}
// objective-c
- (void)viewDidLoad {
SBDGroupChannelListQuery *query = [SBDGroupChannel createMyGroupChannelListQuery];
// limit, order, ... setup your query here.
SBSMChannelCollection *channelCollection = [SBSMChannelCollection collectionWithQuery:query];
self.channelColletion = channelCollection; // Recommands to set a property of view controller
}
如果视图已关闭,即集合过时且不再使用,则显式删除集合。在视图控制器中,这将在deinit
(dealloc
)中完成。
// swift
deinit {
channelCollection?.delegate = nil
channelCollection?.remove()
}
// objective-c
- (void)dealloc {
if (channelCollection != nil) {
channelCollection.delegate = nil;
}
[channelCollection remove];
}
SBSMChannelCollection
提供了使用委托的事件处理程序。事件处理程序称为 SBSMChannelCollectionDelegate
,它在事件发生时接收 SBSMChannelEventAction
和 channels
列表。其中 SBSMChannelEventAction
用于通知通道列表中发生了什么,而 channel
是 SBDGroupChannel
实例的类型。您可以创建一个视图控制器实例,实现事件处理程序并将其添加到集合中。
// swift
// add delegate
import SendBirdSyncManager
class GroupChannelListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, SBSMChannelCollectionDelegate {
override func viewDidLoad() {
// ...
channelCollection?.delegate = self
// ...
}
// channel collection delegate
func collection(_ collection: SBSMChannelCollection, didReceiveEvent action: SBSMChannelEventAction, channels: [SBDGroupChannel]) {
switch (action) {
case SBSMChannelEventAction.insert:
// Insert channels on list
break
case SBSMChannelEventAction.update:
// Update channels of list
break
case SBSMChannelEventAction.remove:
// Remove channels of list
break
case SBSMChannelEventAction.move:
// Move channel of list
break
case SBSMChannelEventAction.clear:
// Clear(Remove all) channels
break
case SBSMChannelEventAction.none:
break
default:
break
}
}
}
// objective-c
// add delegate
#import <SendBirdSyncManager/SendBirdSyncManager.h>
@interface GroupChannelListViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, SBSMChannelCollectionDelegate>
@end
@implementation GroupChannelListViewController
- (void)viewDidLoad {
channelCollection.delegate = self;
// ..
}
// channel collection delegate
- (void)collection:(SBSMChannelCollection *)collection didReceiveEvent:(SBSMChannelEventAction)action channels:(NSArray<SBDGroupChannel *> *)channels {
guard collection == self.channelCollection, channels.count > 0 else {
return
}
switch (action) {
case SBSMChannelEventActionInsert: {
// Insert channels on list
break;
}
case SBSMChannelEventActionUpdate: {
// Update channels of list
break;
}
case SBSMChannelEventActionRemove: {
// Remove channels of list
break;
}
case SBSMChannelEventActionMove: {
// Move channel of list
break;
}
case SBSMChannelEventActionClear: {
// Clear(Remove all) channels
break;
}
case SBSMChannelEventActionNone:
default: {
break;
}
}
}
- 数据获取器
获取的通道将传递给委托方法。获取器会自动确定 SBSMChannelEventAction
,因此不需要在视图中考虑重复的数据。通常,当视图创建时,或者用户请求通道列表的下一页并希望刷新通道列表时,会调用 fetch(_:)
。
// swift
override viewDidLoad() {
channelCollection.fetch(completionHandler: {(error) in
// This callback is optional and useful to catch the moment of loading ended.
})
}
func refreshChannel() {
// begin loading progress
channelCollection?.remove()
channelCollection? = nil
// create channel collection
channelCollection?.fetch(completionHandler: { (error) in
// end load progress
})
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// .. dequeue reusable cell
if self.channels.count > 0 && indexPath.row + 1 == self.channels.count {
channelCollection?.fetch(completionHandler: { (error) in
// end load progress
})
}
// ...
}
// objective-c
- (void)viewDidLoad {
[channelCollection fetchWithCompletionHandler:^(SBDError * _Nullable error) {
// This callback is optional and useful to catch the moment of loading ended.
}];
}
- (void)refreshChannel {
// begin loading progress
[channelCollection remove];
channelCollection = nil;
// create channel collection
[channelCollection fetchWithCompletionHandler:^(SBDError * _Nullable error) {
// end loading progress
}];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// .. dequeue reusable cell
if (self.channels.count > 0 && indexPath.row + 1 == self.channels.count) {
// start loading progress
[self.channelCollection fetchWithCompletionHandler:^(SBDError * _Nullable error) {
// end loading progress
}];
}
// ...
}
- 消息集合
消息是相对静态的数据,SyncManager 支持消息的 全缓存。SBSMMessageCollection
执行后台同步,以便将所有消息同步到第一条消息。后台同步 不 直接影响视图,但它将其存储在本地缓存中。为了更新视图,请显式调用带有方向的 fetch(_:_:)
,这将从缓存中获取数据并将其发送到收集处理程序。
如果同步完成或同步请求失败,后台同步会停止。
注意:后台同步在后台线程运行。
为了支持各种视图(viewpointTimestamp
),SBSMMessageCollection
设置了何时获取消息的时间戳。其中 viewpointTimestamp
是开始前后方向后台同步的时间戳(以及用户首次看到的位置)。这是创建 SBSMMessageCollection
的代码。
通常在消息列表视图控制器的 viewDidLoad()
以及频道集合中创建消息集合。
// swift
override viewDidLoad() {
// ...
let filter: SBSMMessageFilter = SBSMMessageFilter.init(messageType: SBDMessageTypeFilter, customType: customTypeFilter, senderUserIds: senderUserIdsFilter)
let viewpointTimestamp: Int64 = getLastReadTimestamp()
// or LONG_LONG_MAX if you want to see the most recent messages
let messageCollection: SBSMMessageCollection? = SBSMMessageCollection.init(channel: channel, filter: filter, viewpointTimestamp: viewpointTimestamp)
// ...
}
// objective-c
- (void)viewDidLoad {
// ...
SBSMMessageFilter *filter = [SBSMMessageFilter filterWithMessageType:SBDMessageTypeFilter customType:customtypeFilter senderUserIds:senderUserIdsFilter];
long long viewpointTimestamp = getLastReadTimestamp();
// or LONG_LONG_MAX if you want to see the most recent messages
SBSMMessageCollection *messageCollection = [SBSMMessageCollection collectionWithChannel:self.channel filter:filter viewpointTimestamp:viewpointTimestamp];
// ...
}
如果集合过时不再使用,可以取消该集合。建议在消息视图控制器的 deinit
中调用 remove()
。
// swift
deinit {
messageCollection?.delegate = nil
messageCollection?.remove()
}
- (void)dealloc {
if (self.messageCollection != nil) {
self.messageCollection.delegate = nil;
}
[messageCollection remove];
}
SBSMMessageCollection
为委托提供了事件处理程序,可以将其实现并添加到集合中。事件处理程序名为 SBSMMessageCollectionDelegate
,它在事件发生时接收 SBSMMessageEventAction
和 messages
列表。其中 SBSMMessageEventAction
是一个关键字,用于通知消息中发生了什么,而 message
是 Sendbird SDK 的 SBDBaseMessage
实例。
// swift
// add delegate
import SendBirdSyncManager
class GroupChannelChattingViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, SBSMMessageCollectionDelegate {
override func viewDidLoad() {
// ...
messageCollection.delegate = self
// ...
}
// message collection delegate
func collection(_ collection: SBSMMessageCollection, didReceiveEvent action: SBSMMessageEventAction, messages: [SBDBaseMessage]) {
guard collection == self.messageCollection, messages.count > 0 else {
return
}
switch action {
case SBSMMessageEventAction.insert:
self.chattingView?.insert(messages: messages, completionHandler: nil)
break
case SBSMMessageEventAction.update:
self.chattingView?.update(messages: messages, completionHandler: nil)
break
case SBSMMessageEventAction.remove:
self.chattingView?.remove(messages: messages, completionHandler: nil)
break
case SBSMMessageEventAction.clear:
self.chattingView?.clearAllMessages(completionHandler: nil)
break
case SBSMMessageEventAction.none:
break
default:
break
}
}
}
// objective-c
// add delegate
#import <SendBirdSyncManager/SendBirdSyncManager.h>
@interface GroupChannelChattingViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, SBSMMessageCollectionDelegate, SBDConnectionDelegate>
@end
@implementation GroupChannelChattingViewController
- (void)viewDidLoad {
// ..
messageCollection.delegate = self;
// ..
}
// message collection delegate
- (void)collection:(SBSMMessageCollection *)collection didReceiveEvent:(SBSMMessageEventAction)action messages:(NSArray<SBDBaseMessage *> *)messages {
if (self.messageCollection != collection || messages.count == 0) {
return;
}
switch (action) {
case SBSMMessageEventActionInsert: {
//
break;
}
case SBSMMessageEventActionUpdate : {
//
break;
}
case SBSMMessageEventActionRemove: {
//
break;
}
case SBSMMessageEventActionClear: {
//
break;
}
case SBSMMessageEventActionNone:
default:
break;
}
}
SBSMMessageCollection
提供了按方向的数据获取器:SBSMMessageDirection.previous
和 SBSMMessageDirection.next
。它只从缓存中获取数据,永远不会直接请求 Sendbird 服务器。如果某个方向没有更多数据可用,它会内部等待后台同步,并在同步进度后立即获取同步消息。当视图创建时,通常调用 fetch(_:_:)
,使用户可以请求消息列表的前一页/下一页,刷新消息列表,并接收重连成功的事件。
注意:如果您的设备存储了足够多的消息,您可以从调用
fetch(_:_:)
方法中获取尽可能多的消息。因此,请确保不要调用比您打算调用的更多次数。我们通过样本项目中的loading
标志来控制它。
// swift
override func viewDidLoad() {
messageCollection.fetch(in: SBSMMessageDirection.previous, completionHandler: { (error) in
// Fetching from cache is done
})
messageCollection.fetch(in: SBSMMessageDirection.next, completionHandler: { (error) in
// Fetching from cache is done
})
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// .. dequeue reusable cell
if self.channels.count > 0 && indexPath.row + 1 == self.channels.count {
messageCollection?.fetch(in: direction, completionHandler: { (error) in
// Fetching from cache is done
})
}
// ...
}
func refreshMessages() {
messageCollection?.resetViewpointTimestamp(getLastReadTimestamp())
messageCollection?.fetch(in: direction, completionHandler: { (error) in
// Fetching from cache is done
})
}
// MARK SendBird Connection Delegate
func didSucceedReconnection() {
messageCollection?.resetViewpointTimestamp(getLastReadTimestamp())
messageCollection?.fetch(in: direction, completionHandler: { (error) in
// Fetching from cache is done
})
}
// objective-c
- (void)viewDidLoad {
// ..
[messageCollection fetchInDirection:SBSMMessageDirectionPrevious completionHandler:^(SBDError * _Nullable error) {
// Fetching from cache is done
}];
[messageCollection fetchInDirection:SBSMMessageDirectionNext completionHandler:^(SBDError * _Nullable error) {
// Fetching from cache is done
}];
// ..
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// .. dequeue reusable cell
if (self.messages.count > 0 && indexPath.row + 1 == self.messages.count) {
[messageCollection fetchInDirection:direction completionHandler:^(SBDError * _Nullable error) {
// fetching from cache is done
}];
}
// ...
}
- (void)refreshMessages {
[messageCollection resetViewpointTimestamp:getLastReadTimestamp()];
[messageCollection fetchInDirection:direction completionHandler:^(SBDError * _Nullable error) {
// Fetching from cache is done
}];
}
#pragma mark - SendBird Connection Delegate
- (void)didSucceedReconnection {
[messageCollection resetViewpointTimestamp:getLastReadTimestamp()];
[messageCollection fetchInDirection:direction completionHandler:^(SBDError * _Nullable error) {
// Fetching from cache is done
}];
}
获取的消息将传递给委托。获取器会自动确定 SBSMMessageEventAction
,因此不需要在视图中考虑重复的数据。
处理未捕获的消息
SyncManager监听消息事件,例如channel(_:didReceive:)
和channel(_:didUpdate:)
,并自动应用变化。但是,如果消息是由currentUser
发送的,则不会调用这些方法。你可以在currentUser
发送或更新消息时调用相关函数来跟踪消息。SBSMMessageCollection
提供将这些消息事件应用于集合的方法。
// swift
// call collection.appendMessage() after sending message
var previewMessage: SBDUserMessage?
channel.sendUserMessage(with: params, completionHandler: { (theMessage, theError) in
guard let message: SBDUserMessage = theMessage, let _: SBDError = theError else {
// delete preview message if sending message fails
messageCollection.deleteMessage(previewMessage)
return
}
messageCollection.appendMessage(message)
})
if let thePreviewMessage: SBDUserMessage = previewMessage {
messageCollection.appendMessage(thePreviewMessage)
}
// call collection.updateMessage() after updating message
channel.sendUserMessage(with: params, completionHandler: { (theMessage, error) in
guard let message: SBDUserMessage = theMessage, let _: SBDError = error else {
return
}
messageCollection.updateMessage(message)
})
// objective-c
// call [collection appendMessage:] after sending message
__block SBDUserMessage *previewMessage = [channel sendUserMessageWithParams:params completionHandler:^(SBDUserMessage * _Nullable userMessage, SBDError * _Nullable error) {
if (error != nil) {
[messageCollection deleteMessage:previewMessage];
return;
}
[self.messageCollection appendMessage:userMessage];
}];
if (previewMessage.requestId != nil) {
[messageCollection appendMessage:previewMessage];
}
// call [collection updateMessage:] after updating message
[channel sendUserMessageWithParams:params completionHandler:^(SBDUserMessage * _Nullable userMessage, SBDError * _Nullable error) {
[self.messageCollection updateMessage:userMessage];
}];
它只适用于由currentUser
发送的消息,这意味着消息发送者应该是currentUser
。
连接生命周期
你应该在连接到Sendbird服务器后启动SyncManager的同步。在连接上调用resumeSynchronization()
,在断开连接时调用pauseSynchronization()
。以下是代码示例:
// swift
let manager: SBSMSyncManager = SBSMSyncManager()
manager.resumeSynchronize()
let manager: SBSMSyncManager = SBSMSyncManager()
manager.pauseSynchronize()
// objective-c
SBSMSyncManager *manager = [SBSMSyncManager manager];
[manager resumeSynchronize];
SBSMSyncManager *manager = [SBSMSyncManager manager];
[manager pauseSynchronize];
下面示例显示了连接状态和恢复同步的关系。
// swift
// Request Connect to Sendbird
SBDMain.connect(withUserId: userId) { (user, error) in
if let theError: NSError = error {
return
}
let manager: SBSMSyncManager = SBSMSyncManager()
manager.resumeSynchronize()
}
// Sendbird Connection Delegate
func didSucceedReconnection() {
let manager: SBSMSyncManager = SBSMSyncManager()
manager.resumeSynchronize()
}
// objective-c
// Request Connect to Sendbird
[SBDMain connectWithUserId:userId completionHandler:^(SBDUser * _Nullable user, SBDError * _Nullable error) {
if (error != nil) {
//
return;
}
SBSMSyncManager *manager = [SBSMSyncManager manager];
[manager resumeSynchronize];
}];
// Sendbird Connection Delegate
- (void)didSucceedReconnection {
SBSMSyncManager *manager = [SBSMSyncManager manager];
[manager resumeSynchronize];
}
明确执行disconnect()
后,你应该选择一个操作。您可以清除当前用户的数据库或停止同步。
// swift
SBDMain.disconnect {
// clear cache
SBSMSyncManager().clearCache()
// stop synchronizing
SBSMSyncManager().pauseSynchronize()
}
// objective-c
[SBDMain disconnectWithCompletionHandler:^{
// clear cache
[[SBSMSyncManager manager] clearCache];
// stop synchronizing
[[SBSMSyncManager manager] pauseSynchronize];
}];
警告!不要调用
SBDMain.removeAllChannelDelegates()
。它不仅删除了你添加的处理程序,还删除了SyncManager管理的处理程序。