XMPP real-time chat in React Native
Here we are going to explain how to use XMPP protocol for real-time chat in React Native apps.
Create React Native App
To create a React Native app — use the Getting Started guide for reference:
npm install -g create-react-native-appcreate-react-native-app AwesomeProject cd AwesomeProject npm start
So we have a skeleton project and now we can add an XMPP library dependency into it.
Add XMPP lib depdendency
Here we are going to use xmpp.js lib since the same code base can work on multiple environments — Node.js, React Native, Native Script.
The npm package of this lib is not updated properly (last update published a year ago), so we are forced to use the latest master code which is actually the most stable version of the lib:
git clone git@github.com:xmppjs/xmpp.js.git
cd xmpp.js
make
Prepare App UI
Our app will have 2 buttons: to connect to XMPP server and to send a message. Also it will contain a text view to which we will post statuses about connection and messages.
Here is the whole content of our skeleton App.js file:
import React from 'react';
import { StyleSheet, Text, TouchableHighlight, View, ScrollView } from 'react-native';export default class App extends React.Component {
constructor(props) {
super(props); this.state = {
output: ''
}
} onStartConnect() { } onSendMessage() { } render() { var buttons = (
<View style={styles.container}>
<TouchableHighlight
onPress={this.onStartConnect.bind(this)}
style={styles.button}>
<Text style={styles.buttonText}>
Connect to XMPP server (login)
</Text>
</TouchableHighlight>
<TouchableHighlight
onPress={this.onSendMessage.bind(this)}
style={styles.button}>
<Text style={styles.buttonText}>
Send a message
</Text>
</TouchableHighlight>
</View>
); return (
<View style={styles.container}>
<Text style={styles.output_result}>
{this.state.output}
</Text>
{buttons}
</View>
);
}
}const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
button: {
height: 50,
backgroundColor: '#48BBEC',
alignSelf: 'stretch',
marginTop: 10,
justifyContent: 'center',
},
buttonText: {
fontSize: 22,
color: '#FFF',
alignSelf: 'center'
},
output_result: {
color: '#000',
marginTop: 20,
}
});
Initialise XMPP client
Now open App.js file and initialise an XMPP client:
var client = require('./xmpp.js/packages/client');
Also we need to connect a base-64 dependency since this lib uses it inside:
In your package.js do the following:
"dependencies": {
…
"base-64": "0.1.0"
}
Then do npm install in terminal.
Then add the following code in your App.js:
var base64 = require('base-64');
global.btoa = base64.encode;
global.atob = base64.decode;
Implement the ‘Connect to Server’ logic
Modify a constructor and add the following code:
constructor(props) {
super(props); this.state = {
output: ''
} this.xmppClientListeners = [];
this.xmppClient = XMPP.xmpp().client; // you XMPP server endpoints
//
this.XMPPServerOptions = {uri: 'wss://chat.connectycube.com:5291',
domain: 'chat.connectycube.com'}; // Demo user credentials (25045–19@chat.connectycube.com)
//
this.XMPPUserCredentials = {jidLocalPart: '25045–19',
password: 'securepwd123'};
}
Here we initialise an xmppClient and also setup XMPP server endpoint and a demo user credentials.
Next, we implement a connect method which will initiate a connection to XMPP server with above demo user:
connect(options){
this.xmppClient.start(options);
}
Next, implement an addListeners method that will setup all needed connection callbacks e.g. for success connect, for incoming messages etc.:
addListeners() {
var self = this; var removeAllListeners = function(){
self.xmppClientListeners.forEach(function(listener){
self.xmppClient.removeListener(listener.name,
listener.callback);
});
self.xmppClientListeners = [];
} removeAllListeners(); const callbackConnect = function() {
self.log('CONNECTING');
};
this.xmppClient.on('connect', callbackConnect);
this.xmppClientListeners.push({name: 'connect',
callback: callbackConnect}); const callbackOnline = function(jid) {
self.log('ONLINE');
};
this.xmppClient.on('online', callbackOnline);
this.xmppClientListeners.push({name: 'online',
callback: callbackOnline}); const callbackStatus = function(status, value) {
// self.log('status: ' + status);
};
this.xmppClient.on('status', callbackStatus);
this.xmppClientListeners.push({name: 'status',
callback: callbackStatus}); // this.xmppClientReconnect.on('reconnecting', function() {
// Utils.DLog('[Chat]', 'RECONNECTING');
// });
//
// this.xmppClientReconnect.on('reconnected', function() {
// Utils.DLog('[Chat]', 'RECONNECTED');
// }); const callbackStanza = function(stanza) {
// console.log('stanza', stanza.toString())
// after 'input' and 'element' (only if stanza, not nonza) if (stanza.is('presence')) {
self.log("On PRESENCE: " + stanza);
} else if (stanza.is('iq')) {
self.log("On IQ: " + stanza);
} else if(stanza.is('message')) {
self.log("On MESSAGE: " + stanza);
}
};
this.xmppClient.on('stanza', callbackStanza);
this.xmppClientListeners.push({name: 'stanza',
callback: callbackStanza}); const callbackError = function(err) {
self.log('ERROR:', err);
};
this.xmppClient.on('error', callbackError);
this.xmppClientListeners.push({name: 'error',
callback: callbackError}); // this.xmppClient.on('element', function(element) {
// // console.log('element', element.toString())
// // after 'input'
// }); // this.xmppClient.on('send', function(element) {
// // console.log('send', element.toString())
// // after write to socket
// }); // this.xmppClient.on('outgoing', function(element) {
// // before send
// // console.log('outgoing', element.toString())
// }); const callbackOutput = function(str) {
// self.log('SENT:', str);
};
this.xmppClient.on('output', callbackOutput);
this.xmppClientListeners.push({name: 'output',
callback: callbackOutput}); const callbackInput = function(str) {
// self.log('RECV:', str);
};
this.xmppClient.on('input', callbackInput);
this.xmppClientListeners.push({name: 'input',
callback: callbackInput}); const callbackAuthenticate = function(authenticate) {
self.log('AUTHENTICATING'); return authenticate(self.XMPPUserCredentials.jidLocalPart,
self.XMPPUserCredentials.password)
};
this.xmppClient.handle('authenticate', callbackAuthenticate);
this.xmppClientListeners.push({name: 'authenticate',
callback: callbackAuthenticate});
}log(text){
console.log(text); this.setState({output: this.state.output + "\n" + text})
}
And finally we can implement an onStartConnect button callback where we can initiate a connection & authorisation process with the XMPP server:
onStartConnect() {
this.addListeners(); this.connect(this.XMPPServerOptions);
}
Now you can run the app, press on Connect to XMPP server (login) button and check it. You should see something like this:
Implement ‘Send a Message’ functionality
Now we will implement an onSendMessage method which will build a message and send it to current user:
onSendMessage() {
// he we send a message for the same user var stanzaParams = {
from: this.XMPPUserCredentials.jidLocalPart + "@" +
this.XMPPServerOptions.domain,
to: this.XMPPUserCredentials.jidLocalPart + "@" +
this.XMPPServerOptions.domain,
type: ‘chat’,
id: Math.floor(Math.random() * Math.floor(999999999))
};
var messageStanza = XMPP.xml("message", stanzaParams); messageStanza.c('body', {
xmlns: 'jabber:client',
}).t("Hello Amigo").up(); this.xmppClient.send(messageStanza);
}
Then you will receive the message using above callback methods.
Conclusion
The complete source code of the app is located at GitHub repo https://github.com/ConnectyCube/react-native-xmpp-demo
Need a complete cross-platform real-time Chat app?
ConnectyCube provides a complete ConnectyCube React Native Chat samples as an example.
All the modern Chat features are supported:
- 1–1 messaging
- Group messaging
- Cross-platform
- Sent/Delivered/Read statuses
- ‘Is typing’ statuses
- File attachments
- Automatic push notifications to offline users
- Contact list
- Black list
- End-to-end Encryption via additional plugins
- Chat moderation (via Trust & Safety (TnS) feature)