Build and run
For clarity, only code from the PubNub SDK is shown in this tutorial. The video will show this code in context with the React Native source
Watch the video or follow the steps below to build and run your application.
//This code box will update soon as you
//progress through the video
//Use the copy icon in the top right to quickly copy
//code as you're following alongStep 1. Add PubNub Keys
You now have your PubNub publish and subscribe keys - copy them from the above section.
Open the PubNubKeys.js file (in the same directory as App.js) and enter the publish and subscribe keys at the relevant location
export const PUBNUB_PUBLISH_KEY = "REPLACE WITH YOUR PUBNUB PUBLISH KEY"
export const PUBNUB_SUBSCRIBE_KEY = "REPLACE WITH YOUR PUBNUB SUBSCRIBE KEY"Step 2. Adding chat to your application with PubNub
The application on the main branch is fully functional and contains all the changes described in the following steps.
If you want to follow along with the tutorial and build the functionality into a templated version of the app, please checkout the following branch:
git checkout tutorial_step_0To run your application at any point use the following:
Android:
npm run android
iOS:
cd ios
pod install
cd ../
npm run iosIf you have installed watchman when setting up your development environment, the application should automatically refresh as you save your code changes. To force a refresh you can bring up the debug menu as detailed in the React Native documentation - you may find this necessary especially if you have a list populated with live data.
Step 2a. Creating the PubNub object
Before interacting with PubNub, you first need to create a PubNub configuration instance. This instance is your entry point into the PubNub SDK and needs to be configured to identify your client to PubNub. The docs lists everything you can do with the instance including access control (access manager) and reconnect settings but this tutorial will only cover basic configuration.
The PubNub instance requires 3 things:
The publish key allows you to send messages using PubNub
The subscribe keys allows you to receive messages with PubNub
The device identifier uniquely identifies your client to PubNub.
The PubNub object is instantiated at the top of the App.js file
const pubnub = new PubNub({
subscribeKey: PubNubKeys.PUBNUB_SUBSCRIBE_KEY,
publishKey: PubNubKeys.PUBNUB_PUBLISH_KEY,
uuid: 'DummyValue',
})The device identifier you pass in should represent a unique instance of the user or device that connects to PubNub. If you maintain your own user registration workflow then you could use whichever unique identifier you assigned the user during registration. If your users do not already have an assigned ID, as in the case of this app, you should assign a device-specific identifier. More information on the device identifier can be found in this PubNub support article.
Obtaining a device-specific identifier requires platform-specific code but there is a useful React Native plugin that does this for us, device-info. Expo users can use the Expo device API, which serves the same purpose.
deviceId = await DeviceInfo.getUniqueId()
pubnub.setUUID(deviceId)Step 2b. Subscribing to a channel
The concepts of publish and subscribe are fundamental to PubNub where messages are sent from a publishing source and received by subscribers over a channel. You do not need to create the channel separately, it will be created automatically for you by PubNub whenever the first user publishes or subscribes to it.
Despite the apparent simplicity, the Pub/Sub model is able to accommodate the most complex of communication use cases.
To keep this application simple we will only have a single channel to represent the application’s single "chat room". The application will subscribe to receive messages from the channel that have been published by other users.
On mobile applications, you cannot rely on your application being active in the background so most chat developers will take a two stage approach:
Subscribe to receive messages when the app comes to the foreground
Unsubscribe when the app goes into the background
Receive a mobile push notification whilst in the background to notify the user that a message is available. This is outside of the scope of this tutorial but please see our Push Notifications tutorial for more information.
Since we want to receive presence events when users join or leave the channel, specify ‘withPresence’ to be true.
Subscribe to the channel when the app comes to the foreground, this is detected by the handleChange function, in App.js:
pubnub.subscribe({channels: [groupChatChannel], withPresence: true})Unsubscribe from the channel when the app goes into the background, also detected by the handleChange function, in App.js:
pubnub.unsubscribe({channels: [groupChatChannel]})Step 2c. Publishing messages to a channel
Messages published to a channel will be received by all subscribers on that channel. Note that we are using the same channel name that we previously subscribed to.
To keep this application simple, the message payload is a String. Your application will likely use a JSON object rather than a string, which allows you to add custom ‘meta’ information to the message.
Add this code to the handleSend() function, the send button click handler in App.js (React Native component code has been omitted from this tutorial for readability, this also includes state management, i.e. useState()):
pubnub.publish({channel: groupChatChannel, message: input})Step 2d. Listening for PubNub events
PubNub event listeners are used to receive:
Messages: A message has been received.
Presence information: A user has joined or left a channel.
Signals: A signal has been received.
App Context: PubNub's serverless storage has been updated
Message Reactions: Used to modify messages after they are published, for example adding an emoji reaction.
App Context requires the 'App Context' feature to be enabled on the keyset, which was done during the ‘Create Keys’ section of this tutorial.
This tutorial only uses a subset of the possible listeners which will be populated in upcoming steps. Production apps will also often use status events to detect any communication or network errors.
The JavaScript SDK allows you to define a single listener object to receive all event types. The event listener is defined when the app first starts and the sample does this within a useEffect() as part of initialization, in App.js (React Native component code has been omitted from this tutorial for readability):
const listener = {
message: receivedMsg => {
// todo (Tutorial step 2e)
},
presence: presenceMsg => {
// todo (Tutorial step 2f 1/2))
},
objects: objMsg => {
// todo (Tutorial step 2i)
},
}
pubnub.addListener(listener)Step 2e. Listening for new messages
Once an application subscribes to messages on a channel, the messages will be received by the message listener, created at step 2d.
Update the ‘message:’ condition as follows to update the List of chat messages which are defined in a Hook (React Native component code has been omitted from this tutorial for readability):
setMessages(msgs => [
...msgs,
{
id: receivedMsg.timetoken,
author: receivedMsg.publisher,
content: receivedMsg.message,
timetoken: receivedMsg.timetoken,
},
])Step 2f. Listening for presence events
By subscribing to a channel’s presence events, as was done in step 2b, we will also receive events whenever a user joins or leaves the channel.
Update the ‘presence:’ condition with the following cases:
if (presenceMsg.action == 'join') {
addMember(presenceMsg.uuid)
} else if (presenceMsg.action == 'leave') {
removeMember(presenceMsg.uuid)
} else if (presenceMsg.action == 'interval') {
if (presenceMsg['join'] != undefined) {
for (var i = 0; i < presenceMsg['join'].length; i++) {
addMember(presenceMsg['join'][i])
}
}
if (presenceMsg['leave'] != undefined) {
for (var i = 0; i < presenceMsg['leave'].length; i++) {
removeMember(presenceMsg['leave'][i])
}
}
}When the application first starts, it needs to populate the list of users currently in the channel. Since presence events only apply to users newly joining or leaving, the application calls the ‘hereNow’ function on launch to retrieve a list of device IDs already in the group chat. This should be added to the same useEffect() code described earlier which is invoked during initialization, in App.js (React Native component code has been omitted from this tutorial for readability):
pubnub
.hereNow({
channels: [groupChatChannel],
includeUUIDs: true,
})
.then(devicesHereNow => {
try {
var occupants =
devicesHereNow['channels'][groupChatChannel]['occupants']
occupants.forEach(function (member, index) {
addMember(occupants[index].uuid)
})
} catch (error) {
// Error executing hereNow
}
})Step 2g. Retrieving past messages
When a user first joins the chat, it is often useful to provide some recently sent messages, to give some context of any conversation in progress.
Alternatively, if a user leaves and then rejoins the chat they will need to get up to speed on any messages they missed whilst they were away.
Both of these use cases are handled by the PubNub Message Persistence API.
During the initialisation of the app, just after where we added the listener in step 2d, add the following code:
pubnub
.fetchMessages({
channels: [groupChatChannel],
includeUUID: true,
count: 8,
})
.then(historicalMessages => {
var historicalMessagesArray =
historicalMessages['channels'][groupChatChannel]
for (var i = 0; i < historicalMessagesArray.length; i++) {
var message = historicalMessagesArray[i]
lookupMemberName(message.uuid)
setMessages(msgs => [
...msgs,
{
id: message.timetoken,
author: message.uuid,
content: message.message,
timetoken: message.timetoken,
},
])
}
})Step 2h. Assign a friendly name to the device and persist it with PubNub Object storage
App Context is a very powerful serverless storage mechanism provided by PubNub and designed to meet many use cases of a standard chat app.
For example, you can choose to use App Context to store:
User meta data such as name, avatar, email
Channel metadata such as name and description as well as who the members of that channel are
This application does not take full advantage of App Context but does store a single piece of information, the ‘friendly name’ that the user can assign themselves. Assigning a friendly name avoids having to display the long device ID in the member list.
Setting the ‘friendly name’ is handled by a textfield towards the top of the application, modify the ‘save’ button click handler, ‘handleSaveFriendlyName’, in App.js to save the name to the PubNub User meta data.
try {
const result = await pubnub.objects.setUUIDMetadata({
data: {
name: myFriendlyName,
},
})
} catch (status) {
console.log('Save friendly name status: ' + status)
}Step 2i. Lookup someone else's friendly name using PubNub Object storage
The PubNub App Context store is considered the master copy of ‘friendly names’ set by anyone involved in the chat, as detailed in step 2h.
The presence listener, described in step 2f, only tells you the device IDs of users joining or leaving the chat. To update the list of member friendly names in the chat, add the following code to the lookupMemberName() function, in App.js
try {
const result = await pubnub.objects.getUUIDMetadata({
uuid: deviceIdentifier,
})
var newFriendlyNames = friendlyNames
newFriendlyNames[deviceIdentifier] = result.data.name
setFriendlyNames({...newFriendlyNames})
if (deviceIdentifier == deviceId) {
setMyFriendlyName(result.data.name)
}
// Force an update of the messages view with the new name
setMessages(msgs => [...msgs])
} catch (error) {
// This happens if the UUID is not known on the server which is
// a common occurrence so just swallow this
}What about if another user changes their friendly name? To be notified when the friendly name changes, populate the ‘objects’ condition in the listener callback you defined back in step 2d. This listener will fire whenever any member user meta data is updated.
if (objMsg.message.type == 'uuid') {
replaceMemberName(objMsg.message.data.id, objMsg.message.data.name)
}One additional point about the ‘objects’ (App Context) listener: You must have “User Metadata Events” applied to your keyset, as below.
UI Notes and other caveats
For efficiency, PubNub handles presence slightly differently depending on how many occupants you have in the channel. If using this app with a number of users above the ‘Announce Max’ setting, which defaults to 20, be sure to enable the ‘Presence Deltas’ option in your admin dashboard. This causes PubNub to report the device IDs who have joined or left as part of the interval event, which this app uses to maintain who is in the channel.
As stated earlier, mobile applications rely on push notification to receive messages whilst in the background. That is outside of the scope of this tutorial, but please see our Push Notifications tutorial for more information.
This tutorial did not cover read receipts. For information on adding read receipts to your application, please see our documentation for message reactions.
As I created the sample app, I focused on showing PubNub functionality rather than creating an impressive UI... as you can tell, user interface design is not my strongest skill :)