==

Q-Consultation for every industry

Securely hold virtual meetings and video conferences

Learn More>

Want to learn more about our products and services?

Speak to us now

How to Build a Messaging App for iOS and Android with Meteor / Cordova

Hamza Mousa
20 Nov 2020
Building a messaging app with QuickBlox, Cordova and Meteor

QuickBlox is a messaging infrastructure for enterprises that offers messaging and communication as a service (IaaS). The messaging is packed with file and content sharing, real-time video conferencing, and push notification. QuickBlox offers multiple SDKs for mobile and web development with a primary focus on enterprise customers. Its SDKs include Flutter SDK, native iOS SDK, native Android SDK, JavaScript SDK, and Server REST-API. In this tutorial, we will use QuickBlox JavaScript SDK with Meteor and Cordova to build a messaging mobile application. But first, let’s talk about Meteor and Cordova.

So What are Meteor and Cordova?

Meteor is an agonistic JavaScript platform, designed to work well with other JavaScript frameworks like React, Angular, Blaze, and Vue. It uses MongoDB as a database which is a NoSQL document database.

The advantage of using Meteor is that it can be used to build iOS and Android messaging applications with ease, using any preferred framework. Its main features include:

  1. DDP (Distributed Data Protocol)
  2. NoSQL Database (MongoDB)
  3. Supports multiple frameworks
  4. Live-update
  5. Rich double ecosystem (Meteor’s Atmosphere and NPM)
  6. Mobile ready
  7. Supports many frameworks
  8. Ability to use Cordova extensions with ease

Cordova (or Apache Cordova) is an open-source application development framework that allows developers to build mobile apps with web technologies such as JavaScript, HTML, CSS, and other JavaScript frameworks. With Cordova, web developers can build Android or iOS messaging applications with full access to device services. Apache Cordova has a rich ecosystem of plugins and extensions and has been used as a core for many other mobile frameworks like Ionic. Cordova is an essential part of Meteor. Developers can easily add a platform iOS or Android and build their application with one command, $ meteor build, which will generate 3 projects: web, iOS, and Android.

I like to use Meteor/ Cordova to build mobile apps, simply because I take advantage of the awesome meteor client functionalities like a client-side database (minimongo), localStorage, reactive components, full CSS control, many CSS frameworks options, and overall real-time tracker.

Build QuickBlox messaging application with Meteor/ Cordova

Setting up Meteor for Mobile development

  1. Remove the unnecessary packages
  2. Adding mobile required packages
  3. Setting the UI CSS Framework
  4. Client-side database
$ meteor remove autopublish insecure
$ meteor npm install materialize-css@next
$ meteor npm install moment
$ meteor add hamzamu:settings 
$ meteor add frozeman:persistent-minimongo2 
$ meteor add tracker

QB JavaScript SDK setup

Installing QuickBlox JavaScript SDK is an easy task:

$ meteor npm install quickblox3- Connect to QB platform

Create a QuickBlox application

QuickBlox dashboard

To connect to QuickBlox you are required to create an application and get your app credentials. You can register for a developer account and create your first application here.

Also let’s set up our first user, public room, and private groups.

As soon as you have everything ready you can get your app, public room, private group and user credentials. Then create a file for these parameters:

CREDENTIALS = {
   appId : '',
   authKey : '',
   authSecret : '',
   // Public
   roomjID : '',
   // Group:
   groupRId: '',
   userId: '',
   login: '',
   password: '',
   // another test user
   testUserId: '',
   testUserLogin: '',
   testUserPassword: ''
}

Please take a note, that is a practical example not a best practice to save such data.
Import this file in main.js

$ meteor npm install quickblox

Set-up the UI library and the file.

I will be using a setting library I have created to save settings and preferences in a client-side collection: hamzamu:settings. This package will save records temporarily as long as the application is open.

$import {
 Template
} from 'meteor/templating';
import {
 ReactiveVar
} from 'meteor/reactive-var';
import QB from 'quickblox'
import _ from 'lodash'
import './auth.js'
import '../node_modules/materialize-css/dist/js/materialize.js'
import '../node_modules/materialize-css/dist/css/materialize.css'
import './main.html';
// Create App Configuration Collection
AppConfigs = new Mongo.Collection('AppConfigs', {connection: null});
// create a local persistence observer
var appconfigsObs = new PersistentMinimongo2(AppConfigs, 'appConfigs');
// Messages Collection
Messages = new Mongo.Collection('Messages', {connection: null});
// create a local persistence observer
var messages = new PersistentMinimongo2(Messages, 'messages');
 
// Init QuickBlox SDK
QB.init(CREDENTIALS.appId, CREDENTIALS.authKey, CREDENTIALS.authSecret);

In this step I created a two client-side MongoDB collection which will warp MiniMongo with localStorage.

You can see as well that we initiated the QuickBlox application at the last line with the appID, authKey and authSecret.

Note that you can create the dialog, users pragmatically using QuickBlox SDK API, however, we will focus here about integrating send/ receive messages and Meteor/ Cordova integration.

Application-1
Application-2

Here are the basic steps that you need to create a functional secure messaging application with QuickBlox:

  1. Initiate QuickBlox SDK
  2. Create a login form
  3. Connect to a certain dialog
  4. Retrieve the messages
  5. Send a new message
  6. Subscribe to the new messages
  7. Render messages

Full JavaScript Code

import {
 Template
} from 'meteor/templating';
import {
 ReactiveVar
} from 'meteor/reactive-var';
import QB from 'quickblox'
import moment from 'moment'
import './auth.js'
import '../node_modules/materialize-css/dist/js/materialize.js'
import '../node_modules/materialize-css/dist/css/materialize.css'
import './main.html';
 
 
// Create a temporart collection
Temp = new Mongo.Collection('temp', {
 connection: null
})
// Create App Configuration Collection
AppConfigs = new Mongo.Collection('AppConfigs', {
 connection: null
});
// create a local persistence observer
var appconfigsObs = new PersistentMinimongo2(AppConfigs, 'appConfigs');
// Messages Collection
Messages = new Mongo.Collection('Messages', {
 connection: null
});
// create a local persistence observer
var messages = new PersistentMinimongo2(Messages, 'appConfigs');
//
 
Users = new Mongo.Collection('Users', {
 connection: null
});
// create a local persistence observer
var users = new PersistentMinimongo2(Users, 'users');
 
/**
* QuickBlox Init
*/
QB.init(CREDENTIALS.appId, CREDENTIALS.authKey, CREDENTIALS.authSecret);
//
 
 
 
 
/**
* Login
* @param {*} params
*  params.login
*  params.password
*/
function login(params) {
 
 QB.createSession(params, function (err, result) {
   // callback function
   if (err) {
     console.log('Session error: Can`t login to QB')
     App.setSetting({
       login: false,
       message: 'loginError: createSession is not successful'
     })
     alert('Session error: Can`t login to QB')
   } else {
     console.log('Session Created User login: ', params.login, {
       result
     })
     App.setSetting({
       login: true
     })
     getUserId(params.login,params.password)
   }
 });
}
 
 
function getUserId(login,password){
 var searchParams = {
   login: login
 };
 
 QB.users.get(searchParams, function (error, user) {
   if(!error){
     console.log('Getting User Id: ',user)
     App.setSetting({currentUser: user})
 
     connect({userId:user.id, password:password})
   }
 });
}
 
/** Login */
Template.login.events({
 'submit #login'(e) {
   e.preventDefault();
   var username = $('input[ name = "username"]').val()
   var password = $('input[name = "password"]').val()
   login({
     login: username,
     password: password
   })
 }
})
 
 
 
function connect(userCredentials) {
 QB.chat.connect(userCredentials, function (error, contactList) {
   if (!error) {
     App.setSetting({connected:true})
     console.log('Connected')
     console.log('Contact list: ', contactList)
     var roomId = App.getSetting('roomjID')
     // Join Specific Room
     console.log('Joining Dialog*')
     QB.chat.muc.join(roomId, function (error, result) {
       if (error) {
         console.error('Could not join the dialog', roomId)
         return
       }
       console.log(result)
       var dialogId = result.dialogId
       App.setSetting({
         dialogId: dialogId
       })
       // Getting Messages List
       getMessages(dialogId)
     });
   } else {
     console.error('QB Connection Error')
     alert('Could not connect to the QB')
   }
 });
}
 
// ============================
// Send a Message
// ============================
function sendMessage(roomId, message) {
 message.id = QB.chat.send(roomId, message);
 console.log('Message sent Successful:', message.id)
}
// ============================
// Get Latest Messates
// ============================
function getMessages(dialogId) {
 //Getting Messages
 //
 console.log('Getting the Dialog Messages...')
 var params = {
   chat_dialog_id: dialogId,
   // sort_desc: 'date_sent',
   sort_desc: 'date_sent',
   limit: 10,
   skip: 0
 };
 QB.chat.message.list(params, function (error, messages) {
   if (!error) {
     console.log(messages)
     if (messages.limit > 0) {
       _.each(messages.items, (msg) => {
         if (!Messages.findOne({
             _id: msg._id
           })) {
           console.log(msg)
           Messages.insert(msg)
         }
       })
     }
   } else {
     console.log({error})
   }
 });
}
 
 
 
 
/**
* ================================
* Subscribe To Messages
* ===============================
* *** */
QB.chat.onMessageListener = onMessage;
 
function onMessage(userId, message) {
 console.log('New Message: ',{
   userId,
   message
 })
 Messages.insert(message)
 setUser(userId)
 
 
}
/**
* ================================
* Set Users
* ===============================
* *** */
function setUser(userId) {
 var user = Users.findOne({
   id: userId
 });
 if (!user) {
   console.log('user does not exist')
   console.log('Getting User`s data')
   //
   var searchParams = {
     filter: {
       field: 'id',
       param: 'in',
       value: [userId]
     }
   };
   // Get user
   QB.users.listUsers(searchParams, function (error, result) {
     if (!error) {
       var user = result.items[0];
       Users.insert(user.user)
     } else {
       console.error('Could not get the user details [QB.users.listUsers]')
     }
   });
 }
}
 
 
/**
* Message Helpers
*/
Template.messages.helpers({
 messages() {
   return Messages.find({}, {
     sort: {
       date_sent: 1
     },
     limit: 40
   })
 },
 getUser(id, key) {
   var user = Users.findOne({
     id: id
   })
   if (!user) {
     return
   }
   return user[key];
 }
})
/**
* Sending a new Message
*/
Template.newMessage.events({
 'click #setNewMessage': (e) => {
   e.preventDefault();
   var message = $('#newMessageBody').val()
   if (!message) {
     alert('Please write a message')
   }
   var roomId = App.getSetting('roomjID')
   var dialogId = App.getSetting('dialogId')
   var message = {
     type: "groupchat",
     body: message,
     extension: {
       save_to_history: 1,
       dialog_id: roomId
     },
     markable: 1
   };
   sendMessage(roomId, message)
   $('#newMessageBody').val(" ")
 
 
 }
})
 
/**
* Moment Helper
*/
 
Template.registerHelper('moFromNow', function (date) {
 return moment(date).fromNow();
})
 
/**
*  Get Self.messages
*/
 
Template.registerHelper('mine', function (userId) {
 if (userId == App.getSetting('userId')) {
   return true
 }
})

The main.html file

<head>
 <title>QuickBlox Meteor Messages Application</title>
 <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
 
<body>
 {{#unless getSetting 'connected'}}
 {{>login}}
 {{else}}
 {{>mainView}}
 {{/unless}}
</body>
<template name="mainView">
 {{>messages}}
</template>
<template name="messages">
 <div id="messages" class="">
   {{#each messages}}
   <div class="{{#if mine this.sender_id}}mine{{else}}yours{{/if}} messages">
     <div class="message">
       <b>{{getUser this.sender_id 'login'}} - {{moFromNow created_at withoutSuffix}}</b> <br />
       {{message}} {{body}}
     </div>
   </div>
   {{/each}}
 </div>
 <div id="newMessage">
   <div class="row">
     {{>newMessage}}
   </div>
 </div>
</template>
<template name="newMessage">
 <form name="newMessage" id="newMessage">
   <div class="input-field col s8">
     <input type="text" id="newMessageBody" class="" placeholder="Write something" />
   </div>
   <div class="input-field col s2">
     <a href="#" class="btn" id="setNewMessage">Submit</a>
   </div>
 </form>
</template>
<!--  -->
<template name="login">
 <!-- login Windows -->
 <div class="valign-wrapper vh card-panel teal lighten-2">
   <div class="card ">
     <form class="card-content col s12" id="login">
       <div class="row">
         <div class="s12">
           <h4>Login</h4>
         </div>
         <div class="input-field col s12">
           <input id="username" type="text" name="username" class="validate">
           <label for="username">login</label>
         </div>
         <div class="input-field col s12">
           <input id="password" type="password" name="password" class="validate">
           <label for="password">Password</label>
         </div>
         <div class="input-field col s12">
           <button class="btn waves-effect waves-light" type="submit" name="action">Login
           </button>
         </div>
       </div>
     </form>
   </div>
 </div>
</template>

The style.css file

body {
 margin: 0px;
 padding: 0px;
 font-family: sans-serif;
 overflow: hidden;
 
}
 
.vh{
 height: 100vh;
}
 
#messages{
 height: 90vh;
 width: 90vw;
 overflow-x: auto !important;
 overflow-y: auto !important;
 
}
 
#newMessage{
 height: 5vh;
}
 
 
.messages {
 margin-top: 10px;
 display: flex;
 flex-direction: column;
}
 
.message {
 border-radius: 20px;
 padding: 8px 15px;
 margin-top: 5px;
 margin-bottom: 5px;
 display: inline-block;
}
 
.yours {
 align-items: flex-start;
}
 
.yours .message {
 margin-right: 25%;
 background-color: #eee;
 position: relative;
}
 
.yours .message.last:before {
 content: "";
 position: absolute;
 z-index: 0;
 bottom: 0;
 left: -7px;
 height: 20px;
 width: 20px;
 background: #eee;
 border-bottom-right-radius: 15px;
}
.yours .message.last:after {
 content: "";
 position: absolute;
 z-index: 1;
 bottom: 0;
 left: -10px;
 width: 10px;
 height: 20px;
 background: white;
 border-bottom-right-radius: 10px;
}
 
.mine {
 align-items: flex-end;
}
 
.mine .message {
 color: white;
 margin-left: 25%;
 background: linear-gradient(to bottom, #00D0EA 0%, #0085D1 100%);
 background-attachment: fixed;
 position: relative;
}
 
.mine .message.last:before {
 content: "";
 position: absolute;
 z-index: 0;
 bottom: 0;
 right: -8px;
 height: 20px;
 width: 20px;
 background: linear-gradient(to bottom, #00D0EA 0%, #0085D1 100%);
 background-attachment: fixed;
 border-bottom-left-radius: 15px;
}
 
.mine .message.last:after {
 content: "";
 position: absolute;
 z-index: 1;
 bottom: 0;
 right: -10px;
 width: 10px;
 height: 20px;
 background: white;
 border-bottom-left-radius: 10px;
}

Build

Build your Meteor app for iOS and Android. We need to add the specific platforms for the target build and make sure it works. Please note, some adding the platform does not add the SDK or environment variables.

$ meteor add-platform android
$ meteor add-platform ios

To build the app simply run

$ meteor build ../{build location}

As you open the specified build directory you will find your Android and/or XCode package ready to be polished.

Amazing Meteor tools:

With Meteor, you can use Tracker, Sessions, ReactiveVar to add more real-time functionalities as well as countless packages to its ecosystem.

Add more QuickBlox functionalities

You can add more functionalities easily with QuickBlox JavaScript SDK, such as video calling, file sharing, and push notification.

To sum up:

Meteor is a great tool for building real-time reactive web applications, as well as mobile apps for Android and iOS devices. Simply because with Meteor, Cordova is on anabolic reaction and much faster speed. In this tutorial we took a look at how to prepare Meteor for mobile development, integrate QuickBlox JavaScript SDK as we built a nice quick chatting application with it.

Have Questions? Need Support?

Join the QuickBlox Developer Discord Community, where you can share ideas, learn about our software, & get support.

Join QuickBlox Discord
  1. Ewan says:

    This excellent website definitely has all of the info I wanted about this subject and didn't know who to ask.

Leave a Comment

Your email address will not be published. Required fields are marked *

Read More

Ready to get started?

QUICKBLOX
QuickBlox post-box