/ SDKs / iOS
SDKs
Chat SDKs iOS v4
Chat SDKs iOS
Chat SDKs
iOS
Version 4

Collection migration guide

Copy link

Sendbird Chat SDK v4 for iOS has introduced group channel collection and message collection to enhance efficiency and improve developer experience. This guide assists you in transitioning from low-level interface implementations to more efficient, collection-based solutions. We strongly recommend integrating collections into your existing codebase for improved efficiency and user experience.

Key benefits of using collection

Copy link
  1. View-driven interface: The collection interface organizes delegate methods and event callbacks around the currently displayed view. Event callbacks are triggered only for the channels or messages that are currently being displayed.

  2. Effortless event handling: By assigning a collection delegate, the data source for your channel and message views are automatically updated when an event is triggered. You can set your delegate, and the collection handles the event updates to your data source.

  3. Automated updates upon reconnection: Collections handle channel and message updates automatically upon app reconnection, eliminating the need for manual intervention.

  4. Local caching capabilities: Collections also handle local caching of chat data for offline messaging.


Implementing group channel collection

Copy link

The following section further explains how to implement and use group channel collection.

Step 1 Create a group channel collection instance

Copy link

To use the group channel collection interfaces, a GroupChannelCollection instance must be created first.

class GroupChannelListViewController: ViewController {
    var collection: GroupChannelCollection?

    func createGroupChannelCollection() {
        // First, create a GroupChannelListQuery instance.
        let query = GroupChannel.createMyGroupChannelListQuery { params in
            params.includeEmptyChannel = true
            params.order = .latestLastMessage   // Acceptable values are chronological, latestLastMessage, channelNameAlphabetical, and channelMetaDataValueAlphabetical.
            params.includeMemberList = true
            params.includeMetaData = true
            params.includeFrozenChannel = true
            // You can add other params setters.
        }

        // Then create a GroupChannelCollection instance.
        self.collection = SendbirdChat.createGroupChannelCollection(query: query)
    }
}

Step 2 Retrieve a list of group channels

Copy link

You can load more channels by using the loadMore(completionHandler:) method of GroupChannelCollection created in step 1. You don't need to create a GroupChannelListQuery instance and utilize the query to load the next page anymore.

With collectionWithout collection
class GroupChannelListViewController: UIViewController, GroupChannelCollectionDelegate {
  var collection: GroupChannelCollection?
  var channels = [GroupChannel]()
  
  // ...
  
  func loadMore() {
    guard let collection = self.collection else {
          return
      }

      // Check whether there are more channels to load before calling loadMore().
      if collection.hasNext {
          collection.loadMore { channels, error in
              if error != nil {
                  // Handle error.
                  return
              }

              // Add channels to your data source.
              self.channels.append(channels)
          }
      }
  }
}

Step 3 Handle group channel events

Copy link

With collections, numerous group channel event callbacks have been consolidated into three streamlined GroupChannelCollectionDelegate methods. The new methods are designed to only pass events that are relevant to the group channel list view.

Example

Copy link

You're managing the channel list view and want to show a typing indicator when a member starts typing and update the last message in real-time.

Before collections, you had to implement different GroupChannelDelegate methods to keep up with these changes.

With group channel collection, however, all changes from events are already applied to updatedChannels in the channelCollection(_:context:updatedChannels:) method. Now, all you need to do is refresh your channel list with the updated channels as shown in the example code below.

With collectionWithout collection
class GroupChannelListViewController: UIViewController, GroupChannelCollectionDelegate {
  var channels = [GroupChannel]()

  func channelCollection(_ collection: GroupChannelCollection, context: ChannelContext, updatedChannels channels: [GroupChannel]) {
    channels.update(updatedChannels)  // `updatedChannels` carries latest typing status and last message for channels that have had updates.
  }
}

Handle specific events

Copy link

To react to specific events, such as when a member starts typing, check ChannelContext.source in channelCollection(_:context:updatedChannels:). The following code is an example of triggering an alert when a member starts typing.

class GroupChannelListViewController: UIViewController, GroupChannelCollectionDelegate {
  func channelCollection(_ collection: GroupChannelCollection, context: ChannelContext, updatedChannels channels: [GroupChannel]) {
    switch context.source {
    case .eventTypingStatusUpdated:
      showAlert()
    ....
    }
  }

  private func showAlert() {
    print("Someone started typing!")
  }
}

To learn more, see a full list of CollectionEventSource.

Step 4 Keep cached channels synced with the Sendbird server

Copy link

When a user is online, all data associated with the group channels they are a member of are automatically updated. However, when a user is disconnected from the Sendbird server and reconnects later, data associated with group channels need to be updated.

Previously, you had to call SendbirdChat.getMyGroupChannelChangeLogs(timestamp:params:completionHandler:) or SendbirdChat.getMyGroupChannelChangeLogs(token:params:completionHandler:) to manually update the data upon reconnection.

With collections, however, it automatically keeps your cached channels' information up-to-date without requiring additional code implementation. If you wish to be notified when this update is completed in order to do any custom updates, you can check CollectionEventSource.channelChangelog of each GroupChannelCollectionDelegate method.

With collectionWithout collection
class GroupChannelListView: UIViewController, GroupChannelCollectionDelegate {
  func channelCollection(_ collection: GroupChannelCollection, context: ChannelContext, addedChannels channels: [GroupChannel]) {
    switch context.source {
    ...
    case .channelChangelog: 
      updateGroupChannels()
    }
  }

  func channelCollection(_ collection: GroupChannelCollection, context: ChannelContext, updatedChannels channels: [GroupChannel]) {
    switch context.source {
    ...
    case .channelChangelog: 
      updateGroupChannels()
    }
  }
  
  func channelCollection(_ collection: GroupChannelCollection, context: ChannelContext, deletedChannels channels: [GroupChannel]) {
    switch context.source {
    ...
    case .channelChangelog: 
      updateGroupChannels()
    }
  }
}

Implementing message collection

Copy link

The following section explains how to implement and use message collection.

Step 1 Create a message collection instance

Copy link

To use message collection interfaces, a MessageCollection instance must be created first. You can do so by calling the createMessageCollection() method. Then, you must start the collection to load the first batch of messages through startCollection.

import SendbirdChatSDK

class CustomViewController: UIViewController {
    // The following properties should be initialized before using them.
    var collection: MessageCollection?
    var channel: GroupChannel?
    var startingPoint: Int64!

    func createMessageCollection() {
        // You can use a MessageListParams instance for MessageCollection.
        let params = MessageListParams()
        params.reverse = false
        params.isInclusive = false
        params.messageTypeFilter = .all
        params.includeMetaArray = false
        params.includeReactions = false
        // You can add other params setters.
        // ...
        
        guard let channel = self.channel else {
            return
        }

        self.collection = SendbirdChat.createMessageCollection(channel: channel, startingPoint: self.startingPoint, params: params)
        self.collection?.delegate = self
    }

    // Initialize messages from startingPoint.
    func initialize() {
        guard let collection = self.collection else {
            return
        }
        
        collection.startCollection(initPolicy: .cacheAndReplaceByApi, cacheResultHandler: { messages, error in
            // Messages are retrieved from the local cache.
            // They might be too outdated compared to startingPoint.
        }, apiResultHandler: { messages, error in
            // Messages are retrieved from the Sendbird server through the API.
            // According to MessageCollectionInitPolicy.cacheAndReplaceByApi,
            // the existing data source needs to be cleared
            // before adding retrieved messages to the local cache.
        })
    }

    // ...
}

Step 2 Retrieve a list of messages

Copy link

You can load more messages by using the hasNext or hasPrevious method of MessageCollection created in step 1. Previously, you had to call either getMessagesByTimestamp() or getMessagesByMessageId and manually manage the data source for the message list after loading.

With collectionWithout collection
// GroupChannelViewController

var messages = [BaseMessage]()

if collection.hasNext {
    collection.loadNext { messages, error in
        guard error == nil else {
            // Handle error.
            return
        }
        // Add messages to your data source.
        messages.add(messages)
    }
}
    
if collection.hasPrevious {
    collection.loadPrevious { messages, error in
        guard error == nil else {
            // Handle error.
            return
        }
        // Add messages to your data source.
        messages.add(messages)
    }
}

Step 3 Handle channel events

Copy link

With collections, numerous message event callbacks have been consolidated into six streamlined MessageCollectionDelegate methods. Step 3 explains how to handle events on the currently displayed group channel.

Example of group channel event

Copy link

If you wanted to handle events when new members join or leave a channel, you had to implement two separate GroupChannelDelegate methods shown in the code below to handle the events. With message collection, however, all changes from both events are already applied to the updatedChannels parameter in the messageCollection(_:channelContext:updatedChannel:). All you need to do is refresh with the updated channels as shown in the example code below.

With collectionWithout collection
class GroupChannelViewController: UIViewController, MessageCollectionDelegate {
  func messageCollection(_ collection: MessageCollection, channelContext: ChannelContext, updatedChannel channel: GroupChannel) {
    updateChannel(updatedChannel)  // updatedChannel holds latest members data  
  }
  
  private func updateChannel(_ channel: GroupChannel) {
    // update channel with latest member data.
  }
}

Handle specific events

Copy link

For use cases where you need to specifically check whether a member has joined or left a channel separately, you can check ChannelContext.source in channelCollection(_:channelContext:updatedChannels:).

class GroupChannelView: UIViewController, MessageCollectionDelegate {
  func messageCollection(_ collection: MessageCollection, channelContext: ChannelContext, updatedChannel channel: GroupChannel) {
    updateChannel(updatedChannel)
    
    switch context.source {
    case .eventUserJoined:
      customUpdate(updatedChannel.members)
    }
  }
  
  private func updateChannel(_ channel: GroupChannel) {
    // Update channel with the latest member data.
  }
  
  private func customUpdate(_ members: [Member]) {
    // Custom update on view when members join the channel.
    print("Someone new joined the channel!")
  }
}

To learn more, see a full list of CollectionEventSource.

Step 4 Handle message events

Copy link

With collection, you can set the completionHandler as nil and listen to changes to the message collection using the messageCollection(_:context:channel:updatedMessages:) method of MessageCollectionDelegate. Through the delegate method, message events are handled accordingly depending on their message status. This allows you to send a message and track their statuses separately, making it easier to manage your implementation.

With collectionWithout collection
func sendUserMessage() {
  let mParams = UserMessageCreateParams(message: "hello")
  _ = channel.sendUserMessage(params: mParams, completionHandler: nil)
}

func messageCollection(_ collection: MessageCollection, context: MessageContext, channel: GroupChannel, addedMessages: [BaseMessage]) {
    switch context.sendingStatus {
    case .pending:
        // Add pending messages to your data source.
    default:
    }
}

func messageCollection(_ collection: MessageCollection, context: MessageContext, channel: GroupChannel, updatedMessages: [BaseMessage]) {
  switch context.sendingStatus {
  case .succeeded:
      // Update the send status of sent messages.
  case .failed:   (pending -> failed)
      // Update the send status from pending to failed in your data source.
      // Remove the pending message from the data source.
  case .canceled: (pending -> canceled)
      // Remove the pending message from the data source.
      // Implement codes to process canceled messages on your end.
      // The Chat SDK doesn't store canceled messages.
  default:
  }
}

Other message events

Copy link

Previously, different events that affect your message list data had to be handled separately. For example, to update your message list data with the latest message update events, reaction events, and poll update events, you had to use three different GroupChannelDelegate delegate methods - channel(_:didUpdate:), channel(_:updatedReaction:), and channel(_:didUpdatePoll:). In each, you had to separately implement how to handle your message list.

Using message collection, you can handle all message events by simply updating your message data through updatedMessages of messageCollection(_:context:channel:updatedMessages:). This is because updatedMessages already carries all the updated information from each message update, reaction, and poll update event.

With collectionWithout collection
var messages = [BaseMessage]()

func messageCollection( _ collection: MessageCollection, context: MessageContext, channel: GroupChannel, addedMessages: [BaseMessage]) { 
  // updatedMessages already carries all latest information from message update, reaction, and poll update events.
  // Update your message list data source.
  messages.update(updatedMessages) 
}

Handle specific events

Copy link

To react to specific events, such as displaying an alert when a poll has been updated, check MessageContext.source.

func messageCollection(_ collection: MessageCollection, context: MessageContext, channel: GroupChannel, updatedMessages: [BaseMessage]) {
  switch context.source {
  case .eventPollUpdated:
    print("Poll is updated!")
  ...
  }
}

To learn more, see a full list of CollectionEventSource.

Step 5 Keep cached channel and messages synced with the Sendbird server

Copy link

When a user is online, all data associated with the group channels they are a member of and its messages are automatically updated. However, when a user is disconnected from the Sendbird server and reconnects later, data associated with group channels need to be updated.

Refresh cached channel upon reconnection

Copy link

With message collection, it automatically keeps your cached channel information up-to-date without requiring additional code implementation. Previously, you had to call channel.refresh(completionHandler:) to manually update the data upon reconnection.

class GroupChannelViewController: UIViewController, ConnectionDelegate {
  func didSucceedReconnection() {
    channel.refresh { error in
        guard error == nil else {
            // Handle error.
            return
        }
        
        // Channel is refreshed to the latest info.
    }
  }
}

Refresh cached messages upon reconnection

Copy link

Previously, to keep your cached messages up-to-date, you had to manually call either getMessageChangeLogs(token:params:completionHandler:) or getMessageChangeLogs(timestamp:params:completionHandler:) upon reconnection to Sendbird server.

With message collection, however, it automatically fetches the changelogs of message internally. Then, it passes the results onto messageCollection(_:context:channel:updatedMessages:) or messageCollection(_:context:channel:deletedMessages:) with context.source set as CollectionEventSource.messageChangelog.

With collectionWithout collection
class GroupChannelViewController: UIViewController, MessageCollectionDelegate {
  var messages = [BaseMessage]()
  
  func messageCollection(
      _ collection: MessageCollection,
      context: MessageContext,
      channel: GroupChannel,
      addedMessages: [BaseMessage]
  )
      switch context.source {
        ...
        case .messageFill, .messageChangelog:
          messages.update(updatedMessages)
      }
    }
  }
  
  func messageCollection(
      _ collection: MessageCollection,
      context: MessageContext,
      channel: GroupChannel,
      updatedMessages: [BaseMessage]
  )
      switch context.source {
        ...
        case .messageChangelog: 
          messages.update(updatedMessages)
      }
    }
  }
  
  func messageCollection(
      _ collection: MessageCollection,
      context: MessageContext,
      channel: GroupChannel,
      deletedMessages: [BaseMessage]
  )
      switch context.source {
        ...
        case .messageChangelog: 
          messages.update(deletedMessages)
      }
    }
  }
}

Collection event sources

Copy link

The following table shows a list of CollectionEventSources that trigger GroupChannelCollectionDelegate and MessageCollectionDelegate methods to be called. The event sources are passed to ChannelContext and MessageContext inside delegate methods.

NameEvent description

eventMessageReceived

A message has been received in a group channel.

eventMessageUpdated

A message has been updated in a group channel.

eventMention

A user has been mentioned in a message sent in a group channel.

eventUserMuted

A user has been muted in a group channel.

eventUserUnmuted

A user has been unmuted in a group channel.

eventUserBanned

A user has been banned from a group channel.

eventUserUnbanned

A user has been unbanned from a group channel.

eventChannelFrozen

A group channel has been frozen.

eventChannelUnfrozen

A group channel has been unfrozen.

eventChannelChanged

A group channel has the following changes:
- The last message has been updated.
- The unread count of a group channel has been updated.
- A new message has been pinned.
- A pinned message has been updated.
- A group channel has been unhidden.

eventChannelDeleted

A group channel has been deleted.

eventMessageDeleted

A message has been deleted in a group channel.

eventMetaDataCreated

Metadata for a group channel has been created.

eventMetaDataUpdated

Metadata for a group channel has been updated.

eventMetaDataDeleted

Metadata for a group channel has been deleted.

eventMetaCounterCreated

A metacounter for a group channel has been created.

eventMetaCounterUpdated

A metacounter for a group channel has been updated.

eventMetaCounterDeleted

A metacounter for a group channel has been deleted.

eventReactionUpdated

A message reaction has been updated.

eventOperatorUpdated

A channel operator has been updated.

eventThreadInfoUpdated

A reply message has been created or deleted from a thread in a group channel.

eventReadStatusUpdated

A message has been read by a reader who is not the currentUser.

eventDeliveryStatusUpdated

The delivery status of a message sent to a group channel has been updated.

eventTypingStatusUpdated

A user starts typing a message to a group channel.

eventChannelMemberCountChanged

The member count of one or more group channels has changed.

eventUserReceivedInvitation

A user has been invited to a group channel.

eventUserDeclinedInvitation

A user has declined an invitation to a group channel.

eventUserJoined

A user has joined a group channel.

eventUserLeft

A user has left a group channel.

eventChannelHidden

A group channel has been hidden.

eventPinnedMessageUpdated

A message has been pinned or unpinned in a group channel.

eventPollUpdated

A poll has been updated in a group channel.

eventPollVoted

A user has voted on a poll in a group channel.

messageChangelog

Message changelog has been received in the following cases:
- The messageCollection is created.
- Client app is initially connected.
- Client app is reconnected after disconnection.

messageFill

A message gap has been filled.

channelChangelog

Channel changelog has been received.

channelRefresh

A group channel has been refreshed through startMessageCollection() method.

eventMessageSent

A message has been sent in a group channel.

eventPollChangeLog

Poll changelog has been received.