Subscribe
Subscribe to Email updates

Please use a valid email address.

SendBird's
Privacy Policy.
Search
Layer to SendBird Migration
Share

Layer to SendBird Migration

Aug 28, 2019

Since Layer announced it will shut down its chat service on October 30, we’ve created a set of resources to make it easy for businesses and developers to migrate their chat provider without interruptions in service.

Our solutions engineering team has already developed a sync server that will migrate your historical chat data, while synchronizing your realtime chat data with SendBird’s servers. This sync server allows you to migrate your chat without service interruptions or data loss.  

In this guide, we offer a guide for implementing the iOS client and compare the key differences between Layer and SendBird’s iOS client. We’ll use Layer’s Atlas and Atlas Messenger throughout and share comparable code in SendBird’s sample.

Although this guide intends to make the client implementation of SendBird as easy as possible for developers familiar with Layer Atlas and Layer’s SDK, the two products differ in many ways, making a perfect 1:1 match difficult. This guide highlights differences between the client-side implementation and suggests workarounds, where possible. Links throughout this guide refer you to SendBird’s iOS documentation and reference material.

If you need to migrate your data from Layer to SendBird, please contact technical support.

Key differences between Layer and SendBird iOS client implementation

The following section highlights key differences between the iOS client implementation of SendBird and Layer. This article will compare code directly later, but for now, emphasizes qualitative differences in:

  • Authentication
  • Layer’s Conversation and SendBird’s Group Channel
  • The architecture of messages for Layer and SendBird
  • Receiving messages in realtime
  • Read receipts

Authentication

When a client (the user of your app) uses Layer, it needs a nonce for identification. Then the identity provider creates an identity token using the nonce and user credentials

With SendBird, each user has an access token, generated by an authentication server using SendBird’s Platform API. This guide assumes you have a user authentication server that can create a user via Platform API because it is the only way to generate an access token for the user. 

After creating a user with an access token, the user authentication server must save the access token and the user in the server's database. When the client signs in with a user ID and password, the user authentication server returns the user’s access token. The client can connect to the SendBird servers with both the user ID and the access token.

Layer Conversation vs. SendBird’s Group Channel

Layer’s Conversation is most like SendBird’s Group Channel, so previous customers of Layer will want to understand the differences and similarities between Conversation and Group Channel – how to create a Group Channel, query and leave it. There are, of course, differences between the two. But for our purposes, think of Conversation and Group Channel as different terminology for a similar purpose. 

The Layer Conversation object is created when two or more users send the first message to each other. It has a unique ID, a name, users who message each other, read receipts and typing indicators. 

Create a Conversation || Create a Group Channel

You can create Group Channels in SendBird similar to how you would create a Conversation in Layer. The main difference is that SendBird uses a callback method to return the new channel, so it requires a connection to SendBird. 

Since Layer uses a local DB and creates the Conversation in the local DB first, it synchronizes the Conversation with the remote server after the connection is established. As a result, Layer does not use a callback.

Additional channel type in SendBird

SendBird also has an Open Channel with different properties than the Group Channel. The Open Channel is ideal for a large number of participants or when you only need a small and fixed number of channels. You can read more at the documentation for Open Channels.

Message structure

Layer and SendBird use a different structure of classes to represent message objects. 

LYRMessage consists of several LYRMessagePart and each LYRMessagePart has a MIME type for content. So a single message, for example, may consist of text and an image. In this case, LYRMessage comprises two LYRMessagePart. For the text message, Layer uses MIMETypeTextPlain or text/plain, and MIMETypeImagePNG or image/png.

On the other hand, SendBird uses two different classes for text and files: SBDUserMessage and SBDFileMessage. You can specify a CUSTOM_TYPE for each to further subclassify, send structured data like font size or type, or another customer JSON object for a text message. As we’ll see below, this also allows you to send location data with a user message.

To send a file, SendBird uses SBDFileMessage to represent a file message. SendBird uploads a file to the server, receives a URL for the file and sends an SBDFileMessage object with the URL.

Read Receipts

There are significant differences between Layer and SendBird’s read receipts. Layer marks messages read for each message. SendBird, on the other hand, marks messages read for a single channel. When markAsRead of the SBDGroupChannel object is called, SendBird indicates that the user has read all messages in the channel.

Layer also has 5 separate statuses: Invalid, Pending, Sent, Delivered and Read.

SendBird returns only two statuses: Unread and Read. 

We can guarantee that a message is sent and delivered if the sendUserMessageWithParams:completionHandler: or sendFileMessageWithParams:progressHandler:completionHandler: return an error as nil via their completion handler callback. If the callback returns an error, you can implement something to resend the message.

Receiving messages in realtime

When Layer receives a new message, LYRQueryController instance calls the queryControllerDidChangeContent: method of the LYRQueryControllerDelegate delegate. The LYRQueryController instance has the new message and the client can display the message.

SendBird uses a delegate to receive many types of events (see the full list at SBDChannelDelegate). To receive a realtime message, the client has to implement the channel:didReceiveMessage: method of SBDChannelDelegate delegate. The delegate method receives every message in every channel the current user has joined. It means that the client has to filter the message by the channel.

Install the SendBird SDK for iOS

SendBird offers the SDK on CocoaPods (https://cocoapods.org/) and Carthage (https://github.com/Carthage/Carthage). You can also use the Quick Start guide in SendBird’s iOS documentation.

Initialization

You must initialize SendBird and Layer before using the SDKs. 

Layer's App ID is a URL format. SendBird’s App ID is a unique string.

Layer Initialization

Layer creates the LYRClient instance with the app ID.

From Atlas Messenger:

...waiting for Gist...

SendBird Initialization

SendBird initializes itself with the app ID via initWithApplicationId: and it generates a singleton instance. This means that _layerClient in the above is similar to the singleton instance inside the SendBird SDK. The application:didFinishLaunchingWithOptions: is the best place to put the initialization.

...waiting for Gist...

Authentication

Recall the difference between Layer and SendBird during authentication described above.

Layer authentication

Layer’s client needs a nonce for authentication. The identity provider generates an

identity token with the user’s credentials and the nonce. The client connects to Layer with the identity token.

From Atlas Messenger:

...waiting for Gist...

SendBird authentication

SendBird does not use a nonce or an identity token. Instead, each user has an access token, which is similar to a password that the authentication server generates from SendBird’s Platform API (Create User).

This guide assumes that you have a user authentication server that can create a user on SendBird via Platform API (Create User). This API is the only way to generate an access token for the user. After creating a user with an access token, the user authentication server must save the access token with the user ID in the server's database.

When the client signs in with user ID and password, the user authentication server returns the user’s access token. The client can connect to SendBird and begin messaging with the user ID and the access token.

...waiting for Gist...

Layer Conversation vs. SendBird Group Channel

Recall differences and similarities between Layer’s Conversation and SendBird’s Group Channel described above. This section guides you through creating, querying and leaving both Layer’s Conversation and SendBird’s Group Channel.

Creating a Conversation || Group Channel

Create a Conversation in Layer

To create a new Conversation in Layer, you need the participants. The newConversationWithParticipants:participantIdentifiers:error: method returns a new Conversation. This method doesn't have a callback because Layer uses a local DB and creates the Conversation in the local DB first. It synchronizes the Conversation with the remote server after the connection is established. Layer creates Conversations with several options. One option in LYRConversationOptions is distinctByParticipants. The default value is YES.

From Atlas:

...waiting for Gist...

Create a Group Channel in SendBird

SendBird also requires the user_id of participants to create a group channel. Unlike Layer, SendBird uses a callback method. SendBird returns the new channel via the callback, so it requires a connection to successfully create a channel.

...waiting for Gist...

Querying a Conversation || Group Channel

Query a Conversation in Layer

Atlas, or an app that uses LayerKit, uses LYRQueryController to query objects in Layer. To query Conversations, the client needs to create a LYRQuery instance for the LYRConversation class. This creates the LYRQueryController instance at the same time as the LYRQuery instance. To see the result of the query, the LYRQueryController updates a set of objects. To notify the query is finished, the LYRQueryController uses a delegate, LYRQueryControllerDelegate. The queryController:didChangeObject:atIndexPath:forChangeType:newIndexPath: notifies the client that there are updates in the set of objects in LYRQueryController.

From Atlas:

...waiting for Gist...

Query a Group Channel in SendBird

SendBird also has a class for querying objects. To query channels, the client needs to create a SBDGroupChannelListQuery instance with the createMyGroupChannelListQuery class method of SBDGroupChannel

The SBDGroupChannelListQuery has loadNextPageWithCompletionHandler: to request the next page of channels. Since the channels are returned via the completionHandler(), the query instance does not contain any set of the channels. Instead, the query instance has a flag to check whether or not there is a next page. To load the channel’s next page, use the same instance of the query and call loadNextPageWithCompletionHandler: again.

...waiting for Gist...

If you want to learn how to receive any channel events, please read the documentation on advanced implementations of the group channel.

Leaving a Conversation || Group Channel

Leaving a Conversation in Layer

Layer has the leave: method in the LYRConversation instance.

From Atlas Messenger:

...waiting for Gist...

Leaving a Group Channel in SendBird

SendBird has the same feature in SBDGroupChannel. If a user leaves the channel, the user does not receive messages from the channel.

...waiting for Gist...

Sending different message types

Refer to the differences between between Layer and SendBird’s message architecture described above. 

Sending a text Message

Text messages in Layer

Layer needs a conversation instance and a message instance to send a message. LYRMessage consists of several parts, or LYRMessagePart. Each LYRMessagePart has a MIME type for its content. For text messages, Layer uses ATLMIMETypeTextPlain for text/plain MIME type.

From Atlas:

...waiting for Gist...

Text messages in SendBird

SendBird's SBDUserMessage represents a text message. The sendUserMessageWithParams:completionHandler: method returns a temporary message and the completion handler of the method returns the result of the message transmission. You can use the temporary message to show the message that is in the process of being sent. The temporary message has a request ID. The method’s callback returns a message with the same request ID as the temporary message. If these request IDs match, the client can determine that the temporary message was sent. SendBird recommends that the client replace temporary messages with the returned messages in the data source used by the table view or the collection view.

...waiting for Gist...

Sending a location

While Layer creates a location object that becomes a part of LYRMessage, SendBird uses the data property of a user message to send location. Refer to the architectural differences in messages above.

Sending a location in Layer

Atlas sends a location message containing latitude-longitude coordinate. It is not a feature of the Layer SDK, but it is a useful feature for messaging. To represent location, Atlas generates a message part that contains JSON data for latitude and longitude.

From Atlas:

...waiting for Gist...

The above method returns an ATLLocationMediaAttachment object of Atlas. Then, the object becomes a part of LYRMessage and can be sent. 

Sending a location in SendBird

SendBird uses a user message for location. Every message type in SendBird has a data property. It is an NSString type and it can hold any string. So, the client stringifies JSON data containing the coordinates and sets it to the data property.

...waiting for Gist...

When the client displays the message as a location view, the customType of the message is the crucial property because the text message and the location message are the same class type, SBDUserMessage.

Sending a file

Sending a file in Layer

The LYRMessagePart can hold binary data or stream data. Layer uses it to send files. Atlas creates an ATLAssetMediaAttachment object that contains the binary data.

From Atlas:

...waiting for Gist...

Then, Layer sends the LYRMessage object that has the ATLAssetMediaAttachment object.

From Atlas:

...waiting for Gist...

Sending a file in SendBird

SendBird has a SBDFileMessage class to represent a file message. To send a file message, the client creates a SBDFileMessageParams object that contains both the binary file data and some additional information. The sendFileMessageWithParams:progressHandler:completionHandler: method sends the file message with the SBDFileMessageParams object. SendBird uploads the file to the server, receives a URL for the file and sends a SBDFileMessage object with the corresponding URL. 

If you’ve activated the premium feature, auto-thumbnail generation, then the server generates thumbnails for the image or video and puts the URLs for the thumbnails in the SBDFileMessage object. The SBDFileMessage only has the file URLs for the original and the thumbnails. The client must use the URLs to show the file or the thumbnail.

...waiting for Gist...

Receiving messages in realtime

Receiving realtime messages in Layer

When Layer receives a new message, the LYRQueryController instance calls the queryControllerDidChangeContent: method of the LYRQueryControllerDelegate delegate. Once the LYRQueryController instance has the new message, the client can display the message.

From Atlas:

...waiting for Gist...

Receiving realtime messages in SendBird

SendBird uses a delegate to receive many types of events. You can see a list of all the events in the reference to SBDChannelDelegate. To receive a realtime message, the client must implement the channel:didReceiveMessage: method of SBDChannelDelegate delegate. Since the delegate method receives every message in every channel that the current user has joined, the client must filter the message by channel.

...waiting for Gist...
...waiting for Gist...

Read receipts

Read receipts have very different implementations in Layer and SendBird. Refer to the differences in the section above for an overview.

Read receipts in Layer

The LYRMessage object in Layer has a dictionary property for the read receipt of itself. It uses a user ID for the key and a status as the value.

From LayerKit:

...waiting for Gist...

Atlas uses the recipientStatusByUserID property to display a recipient’s status. Atlas calls conversationViewController:attributedStringForDisplayOfRecipientStatus: of ATLConversationViewControllerDataSource. The view controller implements the method and shows the recipient’s status for each message. Layer has 5 types of the recipient status in LYRRecipientStatus.

From Atlas Messenger:

...waiting for Gist...

Layer can mark as read for each message via markAsRead: method of LYRMessage object

From Atlas Messenger:

...waiting for Gist...

Read receipts in SendBird

SendBird has only two statuses: read and unread. 

Unlike Layer, SendBird does not have “Pending,” “Sent,” and “Delivered” statuses. SendBird can guarantee that the message is sent and delivered if the sendUserMessageWithParams:completionHandler: or sendFileMessageWithParams:progressHandler:completionHandler: return nil via their completion handler callback. If the callback returns an error, implement something to resend the message. 

To learn how many people have not read a message, SendBird uses getReadReceiptOfMessage: of the SBDGroupChannel object.

...waiting for Gist...

There is a key difference between SendBird and Layer for marking messages as read. 

Layer marks as read for each message. 

On the other hand, SendBird marks as read for each channel. It means that SendBird determines the user has read all messages after the markAsRead of the SBDGroupChannel object is called.

...waiting for Gist...

Typing indicators

Typing indicators in Layer

Layer indicates who is typing in the conversation. The client calls the sendTypingIndicator: method of the LYRConversation object when the user is typing a message.

From Atlas:

...waiting for Gist...

The client receives the typing event via NSNotificationCenter.

From Atlas:

...waiting for Gist...

Typing indicators in SendBird

SendBird has a similar method for notifying when a user is typing to the group channel. The SBDGroupChannel object has two methods for it. When the user is typing, the client should call the startTyping method. When the user finishes typing, the client should call the endTyping.

...waiting for Gist...
...waiting for Gist...

To receive the typing event, the client has to implement SBDChannelDelegate and add the delegate to SendBird. Then, the delegate method, channelDidUpdateTypingStatus:, receives the typing event. The method returns a channel object that has the latest typing status.

...waiting for Gist...
...waiting for Gist...

Conclusion 

This concludes our guide to implementing the iOS client in SendBird after migrating from Layer. While this guide highlights many of the key qualitative differences and compares code side-by-side, migrating from one service to another is never an easy undertaking. If you need any technical assistance migrating your client implementation to SendBird, contact our technical support.

We're Hiring!
Help SendBird build the world's no. 1 messaging platform
We're Hiring!
Help SendBird build the world's no. 1 messaging platform
Related articles
Migrating chat made easy with Sync Server
Introduction Part of the challenge of migrating from one chat provider to another is not having a live migration solution ready. Building a live migration solution can be cost
ROMMEL SUNGA
Solutions Engineer
Implementing Single Sign-on (SSO) for a product dashboard
SSO offers a user the ability use a single credential to allow access to several inter-related or integrated systems of software. Given that companies use 16 SaaS applications
JACK PARK
Software Engineer - Web Applications
Introducing the new SendBird Docs page
We’re very excited to announce that the SendBird Documentation is now redesigned, up-to-date, and even more intuitive.   Every month, thousands of individual developers and cu
JEFF LIM
Developer Advocate
© SendBird 2019. All rights reserved.
Follow us