{"id":27657081,"url":"https://github.com/sendbird/sendbird-syncmanager-ios","last_synced_at":"2025-04-24T06:54:05.729Z","repository":{"id":56125235,"uuid":"168500000","full_name":"sendbird/sendbird-syncmanager-ios","owner":"sendbird","description":"Sendbird SyncManager for iOS is an add-on for reliable chat data caching with Chat SDK features.","archived":false,"fork":false,"pushed_at":"2023-03-08T00:44:54.000Z","size":127489,"stargazers_count":5,"open_issues_count":11,"forks_count":2,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-04-24T06:53:59.289Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Objective-C","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sendbird.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-01-31T09:39:32.000Z","updated_at":"2024-11-02T18:05:25.000Z","dependencies_parsed_at":"2022-08-15T13:20:32.946Z","dependency_job_id":null,"html_url":"https://github.com/sendbird/sendbird-syncmanager-ios","commit_stats":null,"previous_names":[],"tags_count":47,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sendbird%2Fsendbird-syncmanager-ios","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sendbird%2Fsendbird-syncmanager-ios/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sendbird%2Fsendbird-syncmanager-ios/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sendbird%2Fsendbird-syncmanager-ios/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sendbird","download_url":"https://codeload.github.com/sendbird/sendbird-syncmanager-ios/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250580706,"owners_count":21453531,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-04-24T06:54:05.000Z","updated_at":"2025-04-24T06:54:05.722Z","avatar_url":"https://github.com/sendbird.png","language":"Objective-C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# [Sendbird](https://sendbird.com) SyncManager for iOS\n\n[![Platform](https://img.shields.io/badge/platform-iOS-orange.svg)](https://cocoapods.org/pods/SendBirdSyncManager)\n[![Languages](https://img.shields.io/badge/language-Objective--C%20%7C%20Swift-orange.svg)](https://github.com/sendbird/sendbird-syncmanager-ios)\n[![CocoaPods](https://img.shields.io/badge/CocoaPods-compatible-green.svg)](https://cocoapods.org/pods/SendBirdSyncManager)\n[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n[![Commercial License](https://img.shields.io/badge/license-Commercial-brightgreen.svg)](https://github.com/sendbird/sendbird-syncmanager-ios/blob/master/LICENSE.md)\n\n## Table of contents\n\n  1. [Introduction](#introduction)\n  1. [Before getting started](#before-getting-started)\n  1. [Getting started](#getting-started)\n  1. [Implementation guide](#implementation-guide)\n\n\u003cbr /\u003e\n\n## Introduction\n\n**Sendbird SyncManager** for iOS is a [Chat SDK](https://github.com/sendbird/sendbird-ios-framework) add-on that optimizes the user caching experience by interlinking the synchronization of the local data storage with the chat data in Sendbird server through an event-driven structure.\n\n### How it works\n\nSyncManager leverages local caching and synchronizes the chat data between the local storage and Sendbird server. By handling the operations in an event-driven structure, the add-on provides a simplified Chat SDK integration and a better user experience. \n\n### Operations\n\n- **Background sync** occurs whenever there is a connection and automatically stores data fetched from Sendbird server into the local cache. \n- **Real time sync** occurs all the time; it identifies, stores, and delivers the real-time events received from WebSocket connection. \n- **Offline mode** ensures your client app is operational during offline mode, meaning that even without background sync, the view can display cached data. \n\n### More about Sendbird SyncManager for iOS\n\nFind out more about Sendbird SyncManager for iOS on [SyncManager for iOS doc](https://sendbird.com/docs/syncmanager/v1/ios/getting-started/about-syncmanager). If you have any comments or questions regarding bugs and feature requests, visit [Sendbird community](https://community.sendbird.com). \n\n\u003cbr /\u003e\n\n## Before getting started\n\nThis section shows the prerequisites you need to check to use Sendbird SyncManager for iOS.\n\n### Requirements \n\nThe minimum requirements for SyncManager for iOS are:\n\n- iOS 8.0+\n- Sendbird Chat SDK for iOS v3.0.178+\n\n\u003cbr /\u003e\n\n## Getting started\n\nThis section gives you information you need to get started with Sendbird SyncManager for iOS. \n\n### Try the sample app\n\nDownload the sample app to test the core features of SyncManager for iOS. \n\n- https://github.com/sendbird/SyncManager-iOS-Swift\n\n\u003e **Note**: The fastest way to test our SyncManager is to build your chat app on top of our sample app. Make sure to change the application ID of the sample app to your own. Go to the [Create a Sendbird application from your dashboard](https://sendbird.com/docs/chat/v3/ios/getting-started/install-chat-sdk#2-step-1-create-a-sendbird-application-from-your-dashboard) section to learn more.\n\n### Install SendBirdSyncManager framework from CocoaPods\n\nAdd below into your Podfile on Xcode.\n\n```bash\nplatform :ios, '8.0'\nuse_frameworks!\n\ntarget YOUR_PROJECT_TARGET do\n  pod 'SendBirdSyncManager'\nend\n```\n\nInstall `SendBirdSyncManager` framework through `CocoaPods`.\n\n```bash\npod install\n```\n\nUpdate `SendBirdSyncManager` framework through `CocoaPods`.\n\n```bash\npod update SyncManager\n```\n\nNow you can see installed `SendBirdSyncManager` framework by inspecting `YOUR_PROJECT.xcworkspace`.\n\n\u003e **Note**: `SendBirdSyncManager` is dependent with `SendBird SDK`. If you install `SendBirdSyncManager`, `Cocoapods` automatically install `SendBird SDK` as well. And the minimum version of `SendBird SDK` is **3.0.203**.\n\n### Install SendBirdSyncManager framework from Carthage\n\n1. Add `github \"sendbird/sendbird-syncmanager-ios\"` to your `Cartfile`.\n2. Run `carthage update`.\n3. Go to your Xcode project's **General** settings. Open `\u003cYOUR_XCODE_PROJECT_DIRECTORY\u003e/Carthage/Build/iOS` in Finder and drag `SendBirdSyncManager.framework` to the **Embedded Binaries** section in Xcode. Make sure `Copy items if needed` is selected and click **Finish**.\n\n\u003cbr /\u003e\n\n## Implementation guide\n\n### Initialization\n\n`SBSMSyncManager` is a singlton class. And when `SBSMSyncManager` was initialized, a instance for `Database` is set up. So if you want to initialize `Database` as soon as possible, call `setup(_:)` first just after you get a user's ID. we recommend it is in `application(_:didFinishLaunchingWithOptions:)`.\n\n```swift\n// swift\n// AppDelegate.swift\nfunc application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -\u003e Bool {\n\n    // after getting user's ID or login\n    SBSMSyncManager.setup(withUserId: userId)\n}\n```\n```objc\n// objective-c\n// AppDelegate.m\n- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\n\n    // after getting user's ID or login\n    [SBSMSyncManager setupWithUserId:userId];\n}];\n```\n\n### Collection\n\n`Collection` is a container to manage Sendbird objects(`SBDGroupChannel`, `SBDBaseMessage`) related to a view. `SBSMChannelCollection` is attached to channel list view contoller and `SBSMMessageCollection` is attached to message list view contoller accordingly. The main purpose of `Collection` is,\n\n- To listen data event and deliver it as view event.\n- To fetch data from cache or Sendbird server and deliver the data as view event.\n\nEach collection has event subscriber and data fetcher. Event subscriber listens to data events so that it could apply these data updates into view, while data fetcher loads data from cache or server and sends the data to an event handler.\n\n#### - Channel collection\n\nChannel is a mutable data where chat is active. There are frequent updates on the channel's last message unread message count and also drastic changes in the position of each channel since many apps sort channels by the most recent message. For that reason, `SBSMChannelCollection` depends mostly on server sync. Here's the process `SBSMChannelCollection` synchronizes data:\n\n1. It loads channels from cache and the view shows them.\n2. Then it fetches the most recent channels from Sendbird server and merges with the channels in view.\n3. It fetches from Sendbird server every time `fetch(_:)` is called in order to view previous channels.\n\n\u003e **Note**: Channel data sync mechanism could change later.\n\n`SBSMChannelCollection` requires `SBDGroupChannelListQuery` instance of [Sendbird SDK](https://github.com/sendbird/sendbird-ios-framework) as it binds the query into the collection. Then the collection filters data with the query. Here's the code to create new `SBSMChannelCollection` instance. The creation of channel collection is usually in `viewDidLoad()` of group channel list view controller.\n\n```swift\n// swift\noverride func viewDidLoad() {\n    let query: SBDGroupChannelListQuery? = SBDGroupChannel.createMyGroupChannelListQuery()\n    // limit, order, ... setup your query here. \n    let channelCollection: SBSMChannelCollection? = SBSMChannelCollection.init(query: query)\n    self.channelCollection? = channelCollection // Recommands to set a property of view controller\n}\n```\n```objc\n// objective-c\n- (void)viewDidLoad {    \n    SBDGroupChannelListQuery *query = [SBDGroupChannel createMyGroupChannelListQuery];\n    // limit, order, ... setup your query here. \n    SBSMChannelCollection *channelCollection = [SBSMChannelCollection collectionWithQuery:query];\n    self.channelColletion = channelCollection; // Recommands to set a property of view controller\n}\n```\n\nIf the view is closed, which means the collection is obsolete and no longer used, remove collection explicitly. In viewcontroller, it will be in `deinit`(`dealloc`).\n\n```swift\n// swift\ndeinit {\n    channelCollection?.delegate = nil\n    channelCollection?.remove()\n}\n```\n```objc\n// objective-c\n- (void)dealloc {\n    if (channelCollection != nil) {\n        channelCollection.delegate = nil;\n    }\n    \n    [channelCollection remove];\n}\n```\n\n`SBSMChannelCollection` provides event handlers with delegates. An event handler is named as `SBSMChannelCollectionDelegate` and it receives `SBSMChannelEventAction` and list of `channels` with the arrival of an event. The `SBSMChannelEventAction` is a keyword to notify what happened to the channel list, and the `channel` is a type of `SBDGroupChannel` instance. You can create an view controller instance and implement the event handler and add it to the collection.\n\n```swift\n// swift\n\n// add delegate\nimport SendBirdSyncManager\n\nclass GroupChannelListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, SBSMChannelCollectionDelegate {\n    override func viewDidLoad() {\n        // ...\n        channelCollection?.delegate = self\n        // ...\n    }\n\n    // channel collection delegate\n    func collection(_ collection: SBSMChannelCollection, didReceiveEvent action: SBSMChannelEventAction, channels: [SBDGroupChannel]) {\n        switch (action) {\n        case SBSMChannelEventAction.insert:\n            // Insert channels on list\n            break\n        case SBSMChannelEventAction.update:\n            // Update channels of list\n            break\n        case SBSMChannelEventAction.remove:\n            // Remove channels of list\n            break\n        case SBSMChannelEventAction.move:\n            // Move channel of list\n            break\n        case SBSMChannelEventAction.clear:\n            // Clear(Remove all) channels\n            break\n        case SBSMChannelEventAction.none:\n            break\n        default:\n            break\n        }\n    }\n}\n```\n```objc\n// objective-c\n\n// add delegate\n#import \u003cSendBirdSyncManager/SendBirdSyncManager.h\u003e\n@interface GroupChannelListViewController : UIViewController \u003cUITableViewDelegate, UITableViewDataSource, SBSMChannelCollectionDelegate\u003e\n@end\n\n@implementation GroupChannelListViewController\n- (void)viewDidLoad {    \n    channelCollection.delegate = self;\n    // ..\n}\n\n// channel collection delegate\n- (void)collection:(SBSMChannelCollection *)collection didReceiveEvent:(SBSMChannelEventAction)action channels:(NSArray\u003cSBDGroupChannel *\u003e *)channels {\n    guard collection == self.channelCollection, channels.count \u003e 0 else {\n        return\n    }\n\n    switch (action) {\n        case SBSMChannelEventActionInsert: {\n            // Insert channels on list\n            break;\n        }\n        case SBSMChannelEventActionUpdate: {\n            // Update channels of list\n            break;\n        }\n        case SBSMChannelEventActionRemove: {\n            // Remove channels of list\n            break;\n        }\n        case SBSMChannelEventActionMove: {\n            // Move channel of list\n            break;\n        }\n        case SBSMChannelEventActionClear: {\n            // Clear(Remove all) channels\n            break;\n        }\n        case SBSMChannelEventActionNone:\n        default: {\n            break;\n        }\n    }\n}\n\n```\n\n**- Data fetcher**\n\nFetched channels would be delivered to the delegate method. The fetcher determines the `SBSMChannelEventAction` automatically so you don't have to consider duplicated data in view. Generally `fetch(_:)` is called when view was created and the user requests the next page of the channel list and also wants to refresh the channel list.\n\n```swift\n// swift\noverride viewDidLoad() {\n    channelCollection.fetch(completionHandler: {(error) in\n        // This callback is optional and useful to catch the moment of loading ended.\n    })\n}\n\nfunc refreshChannel() {\n    // begin loading progress\n    channelCollection?.remove()\n    channelCollection? = nil\n    // create channel collection\n    channelCollection?.fetch(completionHandler: { (error) in\n        // end load progress\n    })\n}\n\nfunc tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -\u003e UITableViewCell {\n    // .. dequeue reusable cell        \n    if self.channels.count \u003e 0 \u0026\u0026 indexPath.row + 1 == self.channels.count {\n        channelCollection?.fetch(completionHandler: { (error) in\n            // end load progress\n        })\n    }\n    // ...\n}\n```\n```objc\n// objective-c\n- (void)viewDidLoad {    \n    [channelCollection fetchWithCompletionHandler:^(SBDError * _Nullable error) {\n        // This callback is optional and useful to catch the moment of loading ended.\n    }];\n}\n\n- (void)refreshChannel {\n    // begin loading progress\n    [channelCollection remove];\n    channelCollection = nil;\n    // create channel collection \n    [channelCollection fetchWithCompletionHandler:^(SBDError * _Nullable error) {\n        // end loading progress\n    }];\n}\n\n- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath\n{\n    // .. dequeue reusable cell\n\n    if (self.channels.count \u003e 0 \u0026\u0026 indexPath.row + 1 == self.channels.count) {\n        // start loading progress\n        [self.channelCollection fetchWithCompletionHandler:^(SBDError * _Nullable error) {\n            // end loading progress\n        }];\n    }\n\n    // ...\n}    \n\n```\n\n#### - Message collection\n\nMessage is relatively static data and SyncManager supports **full-caching** for messages. `SBSMMessageCollection` conducts background synchronization so that it synchronizes all the messages until it reaches to the first message. Background synchronization **DOES NOT** affect view directly but store it for local cache. For view update, explicitly call `fetch(_:_:)` with direction, which fetches data from cache and sends the data into collection handler. \n\nIf the synchronization is done or a synchronization request is failed, background synchronization ceases. \n\n\u003e **Note**: Background synchronization run in background thread.\n\nFor various viewpoint(`viewpointTimestamp`) support, `SBSMMessageCollection` sets a timestamp for when to fetch messages. The `viewpointTimestamp` is a timestamp to start background synchronization in both previous and next direction (and also the point where a user sees at first). Here's the code to create `SBSMMessageCollection`.\n\nThe creation of message collection is usually in `viewDidLoad()` of message list view controller as well as in the channel collection.\n\n```swift\n// swift\noverride viewDidLoad() {\n    // ...\n    let filter: SBSMMessageFilter = SBSMMessageFilter.init(messageType: SBDMessageTypeFilter, customType: customTypeFilter, senderUserIds: senderUserIdsFilter)\n    let viewpointTimestamp: Int64 = getLastReadTimestamp()\n    // or LONG_LONG_MAX if you want to see the most recent messages\n\n    let messageCollection: SBSMMessageCollection? = SBSMMessageCollection.init(channel: channel, filter: filter, viewpointTimestamp: viewpointTimestamp)\n    // ...\n}\n```\n```objc\n// objective-c\n- (void)viewDidLoad {\n    // ...\n    SBSMMessageFilter *filter = [SBSMMessageFilter filterWithMessageType:SBDMessageTypeFilter customType:customtypeFilter senderUserIds:senderUserIdsFilter];\n    long long viewpointTimestamp = getLastReadTimestamp();\n    // or LONG_LONG_MAX if you want to see the most recent messages\n\n    SBSMMessageCollection *messageCollection = [SBSMMessageCollection collectionWithChannel:self.channel filter:filter viewpointTimestamp:viewpointTimestamp];\n    // ...\n}\n```\n\nYou can dismiss the collection when the collection is obsolete and no longer used. It is recommended for `remove()` to be in `deinit` of the message view contorller.\n\n```swift\n// swift\ndeinit {\n    messageCollection?.delegate = nil\n    messageCollection?.remove()\n}\n```\n```objc\n- (void)dealloc {\n    if (self.messageCollection != nil) {\n        self.messageCollection.delegate = nil;\n    }\n\n    [messageCollection remove];\n}\n```\n\n`SBSMMessageCollection` has an event handler for delegates, which can be implemented and added to the collection. An event handler is named as `SBSMMessageCollectionDelegate` and it receives `SBSMMessageEventAction` and list of `messages` with the arrival of an event. The `SBSMMessageEventAction` is a keyword to notify what happened to the message, and the `message` is a kind of `SBDBaseMessage` instance of [Sendbird SDK](https://github.com/sendbird/sendbird-ios-framework).\n\n```swift\n// swift\n\n// add delegate\nimport SendBirdSyncManager\nclass GroupChannelChattingViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, SBSMMessageCollectionDelegate {\n    override func viewDidLoad() {\n        // ...\n        messageCollection.delegate = self\n        // ...\n    }\n\n    // message collection delegate\n    func collection(_ collection: SBSMMessageCollection, didReceiveEvent action: SBSMMessageEventAction, messages: [SBDBaseMessage]) {\n        guard collection == self.messageCollection, messages.count \u003e 0 else {\n            return\n        }\n\n        switch action {\n        case SBSMMessageEventAction.insert:\n            self.chattingView?.insert(messages: messages, completionHandler: nil)\n            break\n        case SBSMMessageEventAction.update:\n            self.chattingView?.update(messages: messages, completionHandler: nil)\n            break\n        case SBSMMessageEventAction.remove:\n            self.chattingView?.remove(messages: messages, completionHandler: nil)\n            break\n        case SBSMMessageEventAction.clear:\n            self.chattingView?.clearAllMessages(completionHandler: nil)\n            break\n        case SBSMMessageEventAction.none:\n            break\n        default:\n            break\n        }\n    }\n}\n```\n```objc\n// objective-c\n\n// add delegate\n#import \u003cSendBirdSyncManager/SendBirdSyncManager.h\u003e\n@interface GroupChannelChattingViewController : UIViewController \u003cUITableViewDelegate, UITableViewDataSource, SBSMMessageCollectionDelegate, SBDConnectionDelegate\u003e\n@end\n\n@implementation GroupChannelChattingViewController\n- (void)viewDidLoad {\n    // ..\n    messageCollection.delegate = self;\n    // ..\n}\n\n// message collection delegate\n- (void)collection:(SBSMMessageCollection *)collection didReceiveEvent:(SBSMMessageEventAction)action messages:(NSArray\u003cSBDBaseMessage *\u003e *)messages {\n    if (self.messageCollection != collection || messages.count == 0) {\n        return;\n    }\n\n    switch (action) {\n        case SBSMMessageEventActionInsert: {\n            //\n            break;\n        }\n        case SBSMMessageEventActionUpdate : {\n            //\n            break;\n        }\n        case SBSMMessageEventActionRemove: {\n            //\n            break;\n        }\n        case SBSMMessageEventActionClear: {\n            //\n            break;\n        }\n        case SBSMMessageEventActionNone:\n        default:\n            break;\n    }\n}\n```\n\n`SBSMMessageCollection` has a data fetcher by direction: `SBSMMessageDirection.previous` and `SBSMMessageDirection.next`. It only fetches data from cache and never directly requests to Sendbird server. If no more data is available in a certain direction, it internally waits for the background synchronization and fetches the synced messages right after the progression of the synchronization. When view is created, generally call `fetch(_:_:)` to make users request the previous/next page of the message list, refresh the message list, and receive an event of reconnection success.\n\n\u003e **NOTE**: You can get as many messages as your calling of `fetch(_:_:)` method if your device stores enough messages. So you should make sure that you do not call `fetch(_:_:)` more than you intended. We control it with `loading` flag in our sample project.\n\n```swift\n// swift\noverride func viewDidLoad() {\n    messageCollection.fetch(in: SBSMMessageDirection.previous, completionHandler: { (error) in\n        // Fetching from cache is done\n    })\n    messageCollection.fetch(in: SBSMMessageDirection.next, completionHandler: { (error) in\n        // Fetching from cache is done\n    })\n}\n\nfunc tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -\u003e UITableViewCell {\n    // .. dequeue reusable cell        \n    if self.channels.count \u003e 0 \u0026\u0026 indexPath.row + 1 == self.channels.count {\n        messageCollection?.fetch(in: direction, completionHandler: { (error) in\n            // Fetching from cache is done\n        })\n    }\n    // ...\n}\n\nfunc refreshMessages() {\n    messageCollection?.resetViewpointTimestamp(getLastReadTimestamp())\n    messageCollection?.fetch(in: direction, completionHandler: { (error) in\n        // Fetching from cache is done\n    })\n}\n\n// MARK SendBird Connection Delegate\nfunc didSucceedReconnection() {\n    messageCollection?.resetViewpointTimestamp(getLastReadTimestamp())\n    messageCollection?.fetch(in: direction, completionHandler: { (error) in\n        // Fetching from cache is done\n    })\n}\n```\n```objc\n// objective-c\n- (void)viewDidLoad {\n    // ..\n    [messageCollection fetchInDirection:SBSMMessageDirectionPrevious completionHandler:^(SBDError * _Nullable error) {\n        // Fetching from cache is done\n    }];\n    [messageCollection fetchInDirection:SBSMMessageDirectionNext completionHandler:^(SBDError * _Nullable error) {\n        // Fetching from cache is done\n    }];\n    // ..\n}\n\n- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {\n    // .. dequeue reusable cell\n\n    if (self.messages.count \u003e 0 \u0026\u0026 indexPath.row + 1 == self.messages.count) {\n        [messageCollection fetchInDirection:direction completionHandler:^(SBDError * _Nullable error) {\n            // fetching from cache is done\n        }];\n    }\n\n    // ...\n}\n\n- (void)refreshMessages {\n    [messageCollection resetViewpointTimestamp:getLastReadTimestamp()];\n    [messageCollection fetchInDirection:direction completionHandler:^(SBDError * _Nullable error) {\n        // Fetching from cache is done\n    }];\n}\n\n#pragma mark - SendBird Connection Delegate\n- (void)didSucceedReconnection {\n    [messageCollection resetViewpointTimestamp:getLastReadTimestamp()];\n    [messageCollection fetchInDirection:direction completionHandler:^(SBDError * _Nullable error) {\n        // Fetching from cache is done\n    }];\n}\n\n```\n\nFetched messages would be delivered to a delegate. The fetcher determines the `SBSMMessageEventAction` automatically, so you don't have to consider duplicated data in view.\n\n### Handle uncaught messages\n\nSyncManager listens to message event such as `channel(_:didReceive:)` and `channel(_:didUpdate:)`, and applies the change automatically. But they would not be called if the message is sent by `currentUser`. You can keep track of the message by calling related function when the `currentUser` sends or updates the message. `SBSMMessageCollection` provides methods to apply the message event to the collections.\n\n```swift\n// swift \n\n// call collection.appendMessage() after sending message\nvar previewMessage: SBDUserMessage?\nchannel.sendUserMessage(with: params, completionHandler: { (theMessage, theError) in\n    guard let message: SBDUserMessage = theMessage, let _: SBDError = theError else {\n        // delete preview message if sending message fails\n        messageCollection.deleteMessage(previewMessage)\n        return\n    }\n    \n    messageCollection.appendMessage(message)\n})\n\nif let thePreviewMessage: SBDUserMessage = previewMessage {\n    messageCollection.appendMessage(thePreviewMessage)\n}\n\n\n// call collection.updateMessage() after updating message\nchannel.sendUserMessage(with: params, completionHandler: { (theMessage, error) in\n    guard let message: SBDUserMessage = theMessage, let _: SBDError = error else {\n        return\n    }\n    \n    messageCollection.updateMessage(message)\n})\n```\n```objc\n// objective-c \n\n// call [collection appendMessage:] after sending message\n__block SBDUserMessage *previewMessage = [channel sendUserMessageWithParams:params completionHandler:^(SBDUserMessage * _Nullable userMessage, SBDError * _Nullable error) {\n    if (error != nil) {\n        [messageCollection deleteMessage:previewMessage];\n        return;\n    }\n    \n    [self.messageCollection appendMessage:userMessage];\n}];\n\nif (previewMessage.requestId != nil) {\n    [messageCollection appendMessage:previewMessage];\n}\n\n\n// call [collection updateMessage:] after updating message\n[channel sendUserMessageWithParams:params completionHandler:^(SBDUserMessage * _Nullable userMessage, SBDError * _Nullable error) {    \n    [self.messageCollection updateMessage:userMessage];\n}];\n```\n\nIt works only for messages sent by `currentUser`, which means the message sender should be `currentUser`.\n\n### Connection lifecycle\n\nYou should let SyncManager start synchronization after being connected to Sendbird server. Call `resumeSynchronization()` on connection, and `pauseSynchronization()` on disconnection. Here's the code:\n\n```swift\n// swift\nlet manager: SBSMSyncManager = SBSMSyncManager()\nmanager.resumeSynchronize()\n\nlet manager: SBSMSyncManager = SBSMSyncManager()\nmanager.pauseSynchronize()\n```\n```objc\n// objective-c\nSBSMSyncManager *manager = [SBSMSyncManager manager];\n[manager resumeSynchronize];\n\nSBSMSyncManager *manager = [SBSMSyncManager manager];\n[manager pauseSynchronize];\n```\n\nThe example below shows relation of connection status and resume synchronization. \n\n```swift\n// swift\n\n// Request Connect to Sendbird\nSBDMain.connect(withUserId: userId) { (user, error) in\n    if let theError: NSError = error {\n        return\n    }\n    \n    let manager: SBSMSyncManager = SBSMSyncManager()\n    manager.resumeSynchronize()\n}\n\n// Sendbird Connection Delegate\nfunc didSucceedReconnection() {\n    let manager: SBSMSyncManager = SBSMSyncManager()\n    manager.resumeSynchronize()\n}\n```\n```objc\n// objective-c\n\n// Request Connect to Sendbird\n[SBDMain connectWithUserId:userId completionHandler:^(SBDUser * _Nullable user, SBDError * _Nullable error) {\n    if (error != nil) {\n        // \n        return;\n    }\n    \n    SBSMSyncManager *manager = [SBSMSyncManager manager];\n    [manager resumeSynchronize];\n}];\n\n// Sendbird Connection Delegate\n- (void)didSucceedReconnection {\n    SBSMSyncManager *manager = [SBSMSyncManager manager];\n    [manager resumeSynchronize];\n}\n```\n\nYou should choose an action after execute `disconnect()` explicitly. You can clear the current user's database or stop synchronizing.\n\n```swift\n// swift\nSBDMain.disconnect {\n\n    // clear cache\n    SBSMSyncManager().clearCache()\n\n    // stop synchronizing\n    SBSMSyncManager().pauseSynchronize()\n}\n```\n```objc\n// objective-c\n[SBDMain disconnectWithCompletionHandler:^{\n\n    // clear cache\n    [[SBSMSyncManager manager] clearCache];\n\n    // stop synchronizing\n    [[SBSMSyncManager manager] pauseSynchronize];\n}];\n```\n\n\u003e WARNING! DO NOT call `SBDMain.removeAllChannelDelegates()`. It does not only remove handlers you added, but also remove handlers managed by SyncManager.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsendbird%2Fsendbird-syncmanager-ios","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsendbird%2Fsendbird-syncmanager-ios","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsendbird%2Fsendbird-syncmanager-ios/lists"}