Developing a chat capability within your application can be a complex chore. Even assuming you will rely on third-party frameworks and services, how do you create a chat experience that is fully customized to your needs while providing all the features your users expect?
Enter the PubNub Chat SDK
Building on the success of our initial release for JavaScript / TypeScript, we have recently launched support for additional platforms so more developers can take advantage of the benefits of the Chat SDK:
JavaScript / TypeScript Chat SDK
Unity Chat SDK
Unreal Chat SDK
Android Kotlin Chat SDK
Swift (iOS) Chat SDK
Why choose the PubNub Chat SDK?
The PubNub Chat SDK allows you to create chat apps from scratch or add chat functionality to your existing applications. You can focus on creating the best user experience while PubNub takes care of scalability, reliability, security, and global legislative compliance. Chat SDKs offer customizable, specialized abstractions for chat use cases letting you easily add chat to new or existing applications.
Let’s take a look at my picks for the 7 best features in the Chat SDK that make it trivial for developers to create a professional chat experience:
1. Typing Indicator
When your active users are chatting with each other, they need to have some way of knowing when the other user is typing. How you present this information to your users is up to you, you might have some animated dots, or you could list out the names of every user who is typing, but either way, you need to consider:
When should you show that a user is typing?
Which user or users are typing? In a group chat, multiple people might be typing.
When do users stop typing? Again, in a group chat, different users will stop typing at different times, so you will need to manage multiple timers.
What does ‘stop typing’ mean? How long should you wait after the last keystroke before assuming the user has stopped typing their message? Too short, and the indicator will flash at the recipient, but too long will look as though the user is typing when they have given up.
Where does your timeout logic live? On the sending client? The receiving client? Or on the server?
How the Chat SDK makes it trivial to show a typing indicator
The Chat SDK presents a simple set of messaging APIs that let you implement a typing indicator without worrying about the underlying infrastructure.
For both 1:1 conversations and group messaging, the workflow is as follows:
Invoke
Channel.startTyping()
whenever your user presses a key in your message input field. Do not worry about how often you call this, PubNub will ensure that the recipient only receives a single typing notification.At your receiving client, call
Channel.getTyping()
and provide a callback which will be invoked whenever a user starts typing. This callback will return a string array of user IDs listing who is currently typing on the channel. The SDK also provides aChat.getUser(userId)
API that returns the user’s name, avatar, etc.When you initialized the Chat SDK, you provided a
typingTimeout
that defaulted to 5 seconds, this controls how the recipient knows that users stop typing.Optionally the
Channel.stopTyping()
API can be called to force a stop, for example, if the user leaves the chat.
Any concerns you had about running multiple timers, synchronizing indicators as new users join a group chat, or making sure your solution runs at scale efficiently are eliminated - PubNub takes care of all of that for you.
2. Unread Messages
A user of your chat application will be in more than one conversation at once, and they need some way of knowing when messages are received and how many unread messages each conversation has. Also, when the user first launches your chat app, they need to know how many messages have been received since they last logged in.
Some questions you might start to ask yourself as you design a solution:
What does it mean to ‘read a message’? Does the user have to scroll to the bottom of the chat window? Or is it sufficient that they just have the conversation open?
How can a user manually mark a message as read? There needs to be a way of marking all messages as read.
Different users will have different ‘read’ states for the same message, but when those ‘read’ states update, that update needs to be communicated to every user in the chat.
The ‘read’ states need to be stored somewhere, that way, a user can catch up on who has read their messages.
When the user launches their app, they need some way of asking which channels have unread messages on them and how many unread messages there are.
How the Chat SDK makes it trivial to show unread messages
The Chat SDK exposes a simple but flexible mechanism to handle unread messages that works as follows.
As users join channels to receive messages, a Membership between the User and Channel is created. This membership is used to communicate information about the user/channel relationship, such as which messages the user has read in the channel.
When a user reads a message, the
Membership.setLastReadMessage(Message)
API is called. When you call this is entirely up to your application design: you might choose to mark a message as read when it is within the user’s scroll window for example.If you want to mark all messages as read across all channels, the
Chat.markAllMessagesAsRead()
API can be calledWhen the application is first launched, the
Chat.getUnreadMessagesCounts()
will return an array describing all channels, and how many unread messages that channel contains.A
Channel.connect()
handler can detect when new messages are received in closed chats. You can also use Push notifications (GCM / APNS) with mobile apps.
You no longer need to worry about maintaining a database backend to track who has read which messages or notifying recipients when new messages are available - PubNub takes care of all of that for you.
3. Read Receipts
Users expect to be notified when recipients have read their messages, typically by showing a double-tick on their message or displaying the tick in a different color. For group chats, users need to know who in the group has read their message since not everyone will read the message at the same time.
As you start to think about displaying read receipts in your app, you’ll start to consider:
What does it mean to ‘read a message’? This same question came up when considering how to create a count of unread messages.
For group chats, how will the solution manage the message being read by only some of the intended recipients?
If two messages are received, and the second one is marked as read, does that mean that the first message is also read?
How can I distinguish between ‘sent,’ ‘received,’ and ‘delivered’?
How the Chat SDK makes it easy to show read receipts
The Chat SDK builds on the logic described previously for unread messages to provide simple APIs enabling you to display read receipts.
When a user reads a message, the
Membership.setLastReadMessage(Message)
API is called exactly the same as previously described for unread messages. Similarly, theChat.markAllMessagesAsRead()
API can be called to mark all messages as read across all channels.Each client is able to invoke the
Channel.streamReadReceipts()
method which will be invoked whenever the read receipt status changes for the specified channel. This will return a map, keyed to the message ID, whose value is an array of every user who has read the message.As users mark messages as read, the
streamReadReceipts callback
will notify you of the change, so you can update your application UI as required, e.g., draw double-ticks against each read message.The ‘sent’ and ‘read’ message states are supported by the Chat SDK as described, but a message can be easily marked as ‘received’ using a
custom event
.
Keeping track of who has read each of your user’s messages across all channels can be tricky, and synchronizing that state across all clients in a scalable manner is especially hard. PubNub makes this process simple, and assuming you already have the ‘mark message as read’ logic from the previous section, you only need a single additional API call.
4. Reactions / Emoji :)
Love them or hate them, the ability to react to messages using emojis is an expected feature of all chat applications.
Assuming you are designing a chat solution yourself, you will need to consider the following:
What will the UI look like? How will users select which emoji to react with? How will the emoji be displayed on the message?
The solution needs some way of tracking which emojis are applied to each message and which user(s) have applied them. In other words, each message can have multiple emojis, and each emoji can have multiple users.
You need a way of updating every client in real time when emojis have been added to a message.
Users need a way of removing their reactions.
Multiple people can react to the same message simultaneously, so you need to handle conflict resolution.
The reactions for each message need to be persisted along with that message and restored when the user relaunches the app.
The solution needs to be future proof. New emojis are being introduced frequently, so the solution should handle any Unicode character(s)
How the Chat SDK makes it easy to show emoji reactions
The Chat SDK offers a deliberately simple API that allows you to easily add and remove emojis to messages.
How you allow your users to select which emoji to react with is part of your UX, and the Chat SDK does not impose any limitations on which emojis you apply to messages. Similarly, message rendering is handled by the host platform - the Chat SDK will tell you which Unicode character(s) have been reacted with, but rendering these are the responsibility of the client app.
Emojis can be applied or removed using the
Message.toggleReaction(emoji)
API.Each
Message
object contains areactions
parameter. The reactions parameter is a map, keyed by the emoji, whose value is an array of JSON objects describing which user applied the emoji and when it was applied. For example, the screenshot above will have areactions
map with 2 keys, the ‘smile face with hearts’ and the ‘salute’ emojis. The former will map to an array of length 3 and the latter will map to an array of length 2.Whenever a user in the chat toggles an emoji, the
Message.streamUpdatesOn
callback is invoked to notify you which message has been updated.When the user loads the previous messages in the chat, the Chat SDK will automatically load emojis and reactions associated with those historical messages, so no further action is required on your part.
As your solution scales, keeping track of who has reacted with which message and notifying everyone else in the chat when reactions update can be very challenging, luckily, with the Chat SDK PubNub takes care of all of that for you.
5. Editing and Deleting Messages
After a message is sent, your users will want to edit and delete those messages, but as a chat app developer, this can add complication and lead to a whole slew of questions:
Should I allow my users to edit or delete their messages?
If my users edit their messages, should the recipient be able to see that the message was edited?
Do I need to retain all messages in their unaltered form for legal or analytics reasons? What is the best way to do that? How do I then track that the message was edited.
How can I propagate edits or deletes to all users who are part of the chat?
I want edits and deletes to update at the recipient in real time as the change is probably time-sensitive.
What happens to the thread associated with a deleted message?
Is any user allowed to edit and delete messages? Or only privileged users?
How the Chat SDK makes it easy to edit & delete messages
The Chat SDK offers an opinionated way to edit and delete messages that will work for all chat use cases.
The Chat SDK will store the original message alongside any edits made to the message, which users made those edits, and the time they were made. To relieve the client developer from most of the burden, the final edited message is provided through a single API along with a way to determine whether the message was edited or not.
Out of the box, a user can edit a message’s text with the
Message.editText(“”)
APIBefore deploying to production, enabling Access Control on your keyset will prevent unintended edits from unauthorized users.
Message edits are notified to all clients using the
Message.streamUpdates
API; this is the same API that clients use to receive new messages, so in the majority of cases, edits will be propagated automatically with no additional code.Clients can interrogate the
message.actions.edited
field on any message: If absent, the message has not been edited, but if present, the message contains edits. The edits are listed in a map keyed with the edit text, with the values of the map listing the user ID who made the edits, along with a timetoken describing when the edit was made.
Messages can be deleted using the dedicated Message.delete(soft?: bool)
API that allows both a ‘soft’ deletion and a ‘hard’ deletion. Softly deleting a message will just mark that message deleted so clients can ignore it, but the original message is still retained in storage. A hard delete will delete the message entirely.
The PubNub Chat SDK handles the most complicated aspects of editing and deleting messages for you, propagating message edits and deletions immediately to all interested clients regardless of scale, and retaining a history of edits.
6. User Presence
Many customers select PubNub because of our Presence feature, which enables solution developers to track whether users or devices are online or offline and will scale to any number of clients. PubNub Presence's strength and flexibility have been introduced to our Chat SDK and enhanced with the concept of ‘global presence.’
Consider the challenges involved if you build a solution to track a User’s online or offline state from scratch:
What does it mean for a user to be ‘online’? Are they actively participating in the chat or just viewing it?
How long is a period of inactivity needed to consider the user ‘offline’?
As your solution scales, with thousands or even hundreds of thousands of users frequently going on or offline, how will you handle and route all that data to the recipient clients?
How can we implement Presence efficiently? Especially for very large group chats.
Are you interested in whether the user is present in a specific conversation (so they can be online in one conversation but offline in another)? Or does my application logic only consider a user’s presence globally?
How the Chat SDK makes it easy to handle user presence
The Chat SDK offers two ways of dealing with presence, so all use cases are covered.
The individual Channel presence of the User can be tracked, akin to the existing PubNub Presence feature. This is very powerful & flexible, and developers already familiar with PubNub presence will find it easy to get started.
Some Chat SDK platforms (such as TypeScript) also offer a ‘global presence’ feature which offers a simple User.active
flag to determine whether the user has recently interacted with the app or not. The global presence feature works on a configurable timer rather than channel subscription so is less flexible than the channel approach but is simpler to use, for example your Avatar display logic might contain a property as follows: present={user.active ? PresenceIcon.ONLINE : PresenceIcon.OFFLINE}
.
For channel-based presence, the user is considered online if they are connected to the channel,
Channel.connect()
, the same API used to register to receive new messages.At start-up, you can determine a user’s channel-based presence in a number of ways, including the
User.wherePresent()
andchat.wherePresent(userId)
APIs.To register for updates for a user’s channel-based presence, the
Channel.streamPresence(callback(userIds))
API can be used which will be invoked whenever a user goes online or offline.Global presence, where available, can be accessed through the
User.active
property, which will return true for online and false for offline.To enable global presence, specify both ‘storeUserActivityInterval’ and ‘storeUserActivityTimestamps’ when initializing your Chat instance.
Regardless of your use case, you can easily add user presence to your app using the PubNub Chat SDK regardless of how many users are in your group chat.
7. Message Threads
The PubNub Chat SDK makes it easy to add threaded conversations.
The Chat SDK exposes two dedicated types to handle threads, the ThreadChannel
and ThreadMessage
, which are inherited from their parent Channel
and Message
types. This means that threads offer all the standard capabilities, such as the ability to send and receive messages and read the channel history. You can also use all the features mentioned in this blog within a threaded conversation: emoji reactions, read receipts, unread messages, typing indicators, and more.
Threads also offer some additional capabilities:
You can reply to messages in a thread. Updates to threaded messages are handled in a separate callback, allowing you to keep the logic separate and only show changes when needed.
A
ThreadChannel
andChannel
will have a 1:1 relationship based on the parent's channel ID.ThreadMessages
can be pinned to either theThreadChannel
or the parentChannel
.Management methods such as
Message.getThread()
andMessage.createThread()
Threaded messages and threaded channels are considered first-class citizens in the Chat SDK. Although not a complex addition to the SDK, since threads will be used by the majority of chat implementations, it made sense for us to provide a native implementation within the PubNub Chat SDK.
8 (Bonus). User Profiles
I couldn’t restrict myself to just 7 features, so I had to add a bonus extra.
Allowing your users to manage their profiles is not strictly part of any chat solution, but it is something you need to offer as part of your wider app. Some developers will already have a user profile system in place, which is great as the Chat SDK can live alongside your existing solution. But if your application does not yet have the concept of user profiles, the good news is that you get profile management for free with the PubNub Chat SDK.
Create new users with the
Chat.createUser()
API. Of course, not every user will have permission to create other users, so lock this down with Access Control.The Chat SDK’s secure serverless storage (known as App Context) allows you to store any custom data about your users, such as ‘friendly name,’ ‘avatar,’ ‘externalId, ’ or ‘email address.’ These are examples, you can store whatever data you require and scale to meet your needs.
Manage existing users with the
User.update()
API. All other participants can register to receive updates immediately in real-time with theUser.streamUpdatesOn()
APIs.
As a developer, you have flexibility: Either integrate the Chat SDK user profiles with your wider user management system or use them stand-alone, in conjunction with an external identity/authentication provider—the choice is yours.
Where do I start?
Documentation: Check out the documentation for our JavaScript / TypeScript, Unreal, Unity, Android (Kotlin), and Swift Chat SDKs
Tutorials: Our Chat SDK tutorial supports a growing number of platforms and walks you through creating a simple app with our Chat APIs
Demo: Our Chat Demo, written in Next.js, shows what sort of application you can create using the Chat SDK
Pricing? See our pricing page - add messaging features to your app on our free tier.
Learn more about PubNub’s Chat platform: PubNub provides real-time chat and in-app chat for all customer use cases, from customer support to live streaming to healthcare. Our low latency real-time communication infrastructure allows you to add chat features and file sharing to your app and augment the user experience with moderation tools and profanity filtering. Your user data is secure with our SOC2, GDPR & HIPAA compliant infrastructure, and our messaging SDKs support cross-platform development for mobile applications and web applications with over 30 SDKs (software development kits), including JavaScript/TypeScript, Kotlin, Swift, React Native, and Flutter.
PubNub Integrations allow you to go beyond in-app messaging with advanced features to enhance user engagements—we have templates for video calling/voice calls, chatbots, SMS, or social media integration.