OnChildAdded called multiple times – Firebase

If you are working in Firebase, then you might have encountered an issue with child_added event or onChildAdded method. It is called multiple times on page load. However, it should have been called only when a new child is added. As per Firebase documentation, this is the expected behavior. It first returns all the existing childs and then listen for new added children. But there might be a scenario where you want to perform some action only on new child, not on existing children.

So I have a Firebase database and I have a users array in it.

firebase-database-users
Firebase database users

Step 1

The way I got around this problem, is by first I am looping through all the records in Firebase.

<script type="module">
  // Import the functions you need from the SDKs you need
  import { initializeApp } from "https://www.gstatic.com/firebasejs/10.12.2/firebase-app.js";
  import { getDatabase, ref, get, onChildAdded } from "https://www.gstatic.com/firebasejs/10.12.2/firebase-database.js";
  // TODO: Add SDKs for Firebase products that you want to use
  // https://firebase.google.com/docs/web/setup#available-libraries

  // Your web app's Firebase configuration
  const firebaseConfig = {
    apiKey: "",
    authDomain: "",
    projectId: "",
    storageBucket: "",
    messagingSenderId: "",
    appId: ""
  };

  // Initialize Firebase
  const app = initializeApp(firebaseConfig);
  const database = getDatabase(app);

  const usersRef = ref(database, "users")

  // [step 2]

  get(usersRef).then(function (snapshot) {

    if (snapshot.exists()) {

      snapshot.forEach(function (childSnapshot) {

        // [step 3]

      })

    }

  })

</script>

Note: Make sure to use your own Firebase configurations. If you do not know how to setup Firebase configurations, you can follow our guide.

Step 2

After that, we will create a variable that will count the length of users array. Write the following line in place of // [step 2].

let count = 0

This will initialize the variable “count”.

Step 3

Then increment that variable’s value by 1. Following code goes in place of // [step 3].

count++

if (snapshot.size == count) {

  // [step 4]

}

This will also checks when all the element of array has been looped.

Step 4

Initialize another variable that will tell if all the elements has been fetched.

let hasInitializedFirebase = false

Then we will set its value to true at // [step 4].

setTimeout(function () {
  hasInitializedFirebase = true;
}, 1000)

We are adding a delay of 1 second to prevent the onChildAdded event to be called immidiately.

Step 5

Last step is to call the onChildAdded event and check if the variable hasInitializedFirebase is true.

onChildAdded(usersRef, async function (data) {

  if (hasInitializedFirebase) {

    console.log(data)
    
  }

})

If you reload the page now, you will not see the onChildAdded logs, but as soon as you insert a new element in array, you will see that the console.log(data) has been called.

Complete code

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Test</title>
</head>
<body>

    <script type="module">
      // Import the functions you need from the SDKs you need
      import { initializeApp } from "https://www.gstatic.com/firebasejs/10.12.2/firebase-app.js";
      import { getDatabase, ref, get, set, push, onChildAdded, onChildChanged } from "https://www.gstatic.com/firebasejs/10.12.2/firebase-database.js";
      // TODO: Add SDKs for Firebase products that you want to use
      // https://firebase.google.com/docs/web/setup#available-libraries

      // Your web app's Firebase configuration
      const firebaseConfig = {
        apiKey: "",
        authDomain: "",
        projectId: "",
        storageBucket: "",
        messagingSenderId: "",
        appId: ""
      };

      // Initialize Firebase
      const app = initializeApp(firebaseConfig);
      const database = getDatabase(app);

      const usersRef = ref(database, "users")
      let count = 0
      let hasInitializedFirebase = false

      get(usersRef).then(function (snapshot) {

        if (snapshot.exists()) {

          snapshot.forEach(function (childSnapshot) {

            count++

            if (snapshot.size == count) {

              setTimeout(function () {
                hasInitializedFirebase = true;
              }, 1000)

            }

          })

        } else {

          setTimeout(function () {
            hasInitializedFirebase = true;
          }, 1000)
        }

      })

      onChildAdded(usersRef, async function (data) {

        if (hasInitializedFirebase) {

          console.log(data)
          
        }

      })
    </script>

</body>
</html>

That’s how you can prevent onChildAdded event to be called multiples times in Firebase. If you face any problem in this, feel free to contact me from chat widget on bottom right.

Android chat app in Kotlin and Node JS

We created a chat application in native Android using Kotlin and Node JS. We have used Mongo DB as a database.

Modern and professional UI

End-to-end encryption

Usually, we use encryption but on server side. It has a security issue i.e. plain messages are transmitted through a network that can be read or altered in-transit.

But in this app, we have implemented end-to-end encryption means the encryption and decryption is done on client side rather than on server side. Client will encrypt the message and send it to the server. Server will send the encrypted message to the receiver and the receiver client will do the decryption. So even the server will not be able to read your messages.

We will be using the AES-256-bit length key to generate secret keys. All messages are sent to the server after encryption. So data will remain safe in-transit.

Voice notes

Sending voice notes is a quick way to send messages especially when you are in places when you are in hurry. Instead of typing the message which might cause spelling mistake, you can simply record your audio and send it directly to the receiver inside the app. No need to record the audio from a separate app and attach the audio in a message. You can also listen to your sent or received voice notes within the app.

If you use WhatsApp, you already know how voice notes work. You will learn how to record audio from your android phone and save it in your phone’s storage. Once stored, we will send that MP3 file to the Node JS server. The server will save the file. The user will be able to play that audio from the URL.

All contacts

We are using android contact’s API to fetch all the contacts. So you will learn how to get runtime contacts permission in android.

Private chat

The user can chat with any of his contact numbers as long as the receiver is a registered user.

Chat attachments

Users can attach files in chat messages as well. Images and videos are not compressed at all. So the receiver will see the image in its original quality.

Search contacts

Group chat

Users can chat in groups. Create as many groups as you want. Add members to them and start chatting. What makes it different from other chat apps ? First, it does not allow anyone to add you to a group. You will be invited. You will only start receiving the group notifications when you accept the invitation. Second, this app displays a list of all groups separately. So you can know in which groups you are currently in.

Share status that disappears after 24 hours

In this tutorial, you will:

  • Be able to share your status with all your contacts
  • Exclude some contacts from viewing your status
  • Create multiple lists (e.g. friends, family, colleagues, etc.) so you won’t have to select the contacts each time you upload a status
  • See contacts who have viewed your status/story
  • Download the image or video of status

Seen/unseen messages (blue ticks)

In this part, you will learn how to:

  • Install sockets in android
  • Connect android sockets with the Node JS server
  • Get real-time events on new message
  • Mark messages as read and un-read
  • Get bluetick (like WhatsApp) when the user sees your message

User profile

Search messages

Learn how to apply case in-sensitive sub-string search on all the messages in a chat with any of your contact. You can write any part of text of message and the app will show you the messages that matched your searched string.

Although the messages are end-to-end encrypted, which means that the server will not be able to apply search because server can’t read your messages. But we still found a way to apply search on end-to-end encrypted messages.

How to install

Our TrustPilot Reviews

TrustPilot-reviews

Firebase Storage – Upload, Download, and Delete

In this tutorial, we are going to teach you, how you can upload, download and delete files from Firebase Storage. Firebase Storage is a service provided by Google that allows you to save files on their server. The free plan allows you to upload data up to 1 GB. More data can be bought from the Firebase Pricing page.

What you are going to learn:

  1. Upload files on Firebase Storage.
  2. Save the data in Realtime Database.
  3. Fetch files from the Firebase Storage.
  4. Download files from Firebase Storage.
  5. Fix CORS issue if working in localhost.
  6. Install gsutil.
  7. Delete files from Firebase Storage.

Video tutorial:

Upload file

We are going to upload the file to the Firebase storage and save its path in Firebase Realtime Database. It is another service provided by Google to save data in JSON format. To upload the file, we are going to show a form with an input type file that allows the user to select any type of file. We will be using Vue JS to render the HTML to view the data in real-time, for example, newly uploaded files will be displayed automatically and deleted files will be removed from the view without having to refresh the page.

<div id="app">
	<form id="upload-form">
		<input type="file" name="file" required />
		<input type="submit" value="Upload" />
	</form>

    [show all uploaded here]
</div>

<script src="vue.min.js"></script>

This will display a form with an input type file to upload a file. And a submit button which when clicked will submit the form. Vue JS production file can be downloaded from here. Now you need to attach a submit event listener to this form using Javascript.

<script type="module">

	// [initialize firebase here]

	window.addEventListener("load", function () {
		document.getElementById("upload-form").addEventListener("submit", function () {
			event.preventDefault();
			var form = event.target;

			var file = form.file.files[0];
			console.log(file);
			// [upload in storage here]
		});
	});
</script>

If you select the file and hit submit, you will see your selected file object in the browser console. Now you need to do 2 things; initialize firebase, and upload the file. To initialize firebase, you need to create an app at Firebase Console.

  1. Add new project.
  2. Enter name of project.
  3. When project is created, select “web”.
  4. Copy the firebaseConfig variable.

Replace the [initialize firebase here] section with the following:

import { initializeApp } from "https://www.gstatic.com/firebasejs/9.1.0/firebase-app.js";

import { getStorage, ref as stRef, uploadBytes } from "https://www.gstatic.com/firebasejs/9.1.0/firebase-storage.js";

import { getDatabase, ref as dbRef, push, set } from "https://www.gstatic.com/firebasejs/9.1.0/firebase-database.js";

// Your web app's Firebase configuration
const firebaseConfig = {
	apiKey: "",
	authDomain: "",
	projectId: "",
	storageBucket: "",
	messagingSenderId: "",
	appId: ""
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);

// Get a reference to the storage service, which is used to create references in your storage bucket
const storage = getStorage(app);

const database = getDatabase();
const databaseReference = dbRef(database, "files");

Replace the firebaseConfig variable with your firebase configurations. Now replace the [upload in storage here] section with the following:

const storageRef = stRef(storage, "files/" + file.name);

uploadBytes(storageRef, file).then(function (snapshot) {
	var newFileRef = push(databaseReference);

	set(newFileRef, {
		"name": file.name
	});
});

This will upload the file in Firebase Storage in a folder named “files”. You can see it in your project’s dashboard in left menu in “Storage” page. Also, it will create a new element in “files” array in “Realtime Database” page.

Displaying Uploaded Files in Firebase Storage

Now the file is being uploaded in Firebase Storage and it’s path is also being saved in realtime database. Now you need to show all uploaded files. Replace the [show all uploaded here] section with the following:

<table>
	<tr>
		<th>ID</th>
		<th>Name</th>
		<th>File</th>
		<th>Actions</th>
	</tr>

	<tr v-for="file in files">
		<td>{{ file.id }}</td>
		<td>{{ file.name }}</td>
		<td>
			[download link here]
		</td>
		<td>
			[delete button here]
		</td>
	</tr>
</table>

This will create a simple HTML table. Now you need to do the following things:

  1. Initialize Vue JS.
  2. Get all data from realtime database.
  3. Render in HTML table.

You can initialize the Vue JS from the following code:

var vueApp = new Vue({
	el: "#app",
	data: {
		files: []
	},
    // [updated event goes here]
});

Then include “onValue” in your “firebase-database” import. So your firebase database import line will become:

import { getDatabase, ref as dbRef, push, set, onValue } from "https://www.gstatic.com/firebasejs/9.1.0/firebase-database.js";

Note the “onValue” in the import block. Similarly, add the “getDownloadURL” in the import of “firebase-storage”. So your firebase storage import line will become:

import { getStorage, ref as stRef, uploadBytes, getDownloadURL } from "https://www.gstatic.com/firebasejs/9.1.0/firebase-storage.js";

Note the “getDownloadURL” in the import block. Then write the following code in your Javascript:

onValue(databaseReference, function (snapshot) {
	snapshot.forEach(function (childSnapshot) {
		const value = childSnapshot.val();

		const storageRefDownload = stRef(storage, "files/" + value.name);
		getDownloadURL(storageRefDownload).then(function (url) {
			vueApp.files.push({
				"id": childSnapshot.key,
				"name": value.name,
				"url": url
			});
		});
	});
});

You can apply the following CSS styles to make the table look a little nice:

<style>
	table, th, td {
		border: 1px solid black;
		border-collapse: collapse;
	}
	th, td {
		padding: 25px;
	}
</style>

Download File from Firebase Storage

Now you need to download the file. First, you need to replace the [download link here] section with the following:

<a v-bind:href="file.url" v-bind:download="file.name" onclick="downloadFile();">Download</a>

Create the following function in your Javascript:

function downloadFile() {
	// prevent default href action
	event.preventDefault();

	// get URL from href
	var anchor = event.target;
	var url = anchor.getAttribute("href");

	// get blob data
	const xhr = new XMLHttpRequest();
	xhr.responseType = "blob";
	xhr.onload = function (event) {
		const blob = xhr.response;

		// get clickable URL of blob data
		const blogUrl = window.URL.createObjectURL(blob);

		// replace href with new blob value
		anchor.setAttribute("href", blogUrl);

		// remove the onclick listener
		anchor.removeAttribute("onclick");

		// download the file
		anchor.click();

		// free up the memory
		window.URL.revokeObjectURL(blogUrl);
	};
	xhr.open("GET", url);
	xhr.send();
}

// make the function global so it can be accessible from anchor tag onclick
window.downloadFile = downloadFile;

Run the code now, and you will be able to download the file.

Note: If you get a CORS error while working in localhost, then do the following steps:

  1. Download gsutil from here.
  2. Extract the ZIP and paste the folder to a permanent location in your system.
  3. Open the terminal or command prompt in that extracted folder and run the following command in it:
./install.sh
  1. Once installed, run the following command to initialize it:
gcloud init
  1. It will ask for your project name too.
  2. After the project is set and gcloud is initialized, create a file named “cors.json” at the root of your project.
  3. Your “cors.json” must have the following content:
[
	{
		"origin": ["*"],
		"method": ["GET"],
		"maxAgeSeconds": 3600
	}
]
  1. Then run the following command in your terminal:
gsutil cors set cors.json gs://<your-cloud-storage-bucket>
  1. Now try to download the file again, your CORS error should be fixed.

Delete File from Firebase Storage

First, replace the [delete button here] section with the following:

<form class="delete-form">
	<input type="hidden" name="id" v-bind:value="file.id" />
	<input type="hidden" name="name" v-bind:value="file.name" />
	<input type="submit" value="Delete" />
</form>

Then add “deleteObject” in your import of “firebase-storage”. So your import line will look like this:

import { getStorage, ref as stRef, uploadBytes, getDownloadURL, deleteObject } from "https://www.gstatic.com/firebasejs/9.1.0/firebase-storage.js";

Then add the following event in your [updated event goes here] section:

updated: function () {
	var forms = document.querySelectorAll(".delete-form");
    for (var a = 0; a < forms.length; a++) {
    	forms[a].setAttribute("onsubmit", "onDeleteFormSubmit();");
    }
}

This will attach an event listener to every delete form, which will be called when the form is submitted. Now you need to create the following function in your Javascript:

function onDeleteFormSubmit() {
	event.preventDefault();
	var form = event.target;

	const tempDbRef = dbRef(database, "files/" + form.id.value);
	set(tempDbRef, null);

	const deleteStorageReference = stRef(storage, "files/" + form.name.value);
	deleteObject(deleteStorageReference);

	for (var a = 0; a < vueApp.files.length; a++) {
		if (vueApp.files[a].id == form.id.value) {
			vueApp.files.splice(a, 1);
			break;
		}
	}
}
window.onDeleteFormSubmit = onDeleteFormSubmit;

This will first delete the data from the real-time database. Then it will delete the file from Firebase Storage. Finally, it will remove it from the Vue JS array, so it will automatically be removed from the HTML table too.

You can also create a realtime chat in Firebase. Learn how to do it from here.

Now you will be able to upload files to the Firebase Storage and save its path in Realtime Database. And also to download and delete the files from it. If you face any problems facing this tutorial, kindly let us know in the comments section below.

class file for com.google.android.gms.internal.zzbfm not found Firebase and Google maps – Solution

Today, we will share with you the solution for class “zzbfm” not found error while working on Firebase and Google maps.

If you are ever facing an error in android that says:

class file for com.google.android.gms.internal.zzbfm not found

It is most likely when you just integrated Firebase library in your project that is already using maps, location or places.

The solution to this is:

  1. Version number of google maps and Firebase should be same.
  2. Change “implementation fileTree” to “compileOnly fileTree”
  3. Enable multiDex and add multiDex library

app > build.gradle

android {
    compileSdkVersion 28
//    buildToolsVersion "29.0.2"
    defaultConfig {
        applicationId "your.application.id"
        minSdkVersion 22
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        multiDexEnabled true // important
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
//    implementation fileTree(include: ['*.jar'], dir: 'libs')
    compileOnly fileTree(dir: 'libs', include: ['*.jar'])

    implementation 'com.google.android.gms:play-services-maps:11.8.0'
    implementation 'com.google.android.gms:play-services-location:11.8.0'
    implementation 'com.google.android.gms:play-services-places:11.8.0'

    implementation 'com.google.firebase:firebase-core:11.8.0'
    implementation 'com.google.firebase:firebase-messaging:11.8.0'
    implementation 'com.google.firebase:firebase-database:11.8.0'

    implementation 'com.android.support:multidex:1.0.3'
}
apply plugin: 'com.google.gms.google-services'

build.gradle

buildscript {
    repositories {
        jcenter()
        google()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.2'
        classpath 'com.google.gms:google-services:4.0.1'
    }
}

If your problem persists, please mention it in the comments section below. Check Google cloud platform for more services.

Learn how to embed maps without API key.

Maps without API Key – By Coordinates & By Address

Realtime Web-based Chat in Firebase

Following this tutorial, you will be able to do realtime chat in Firebase in your website. We will be using Javascript in this tutorial.

Firebase

Firebase provides the ability to use non-relational database which means data will be stored in objects and arrays. It uses JSON format to save data in database. The structure of data is similar to as stored in Mongo DB where each table is known as collection. Each row is resembled with documents inside collection.

By default, Firebase provides 15 GB of data where you can store text, images and documents too. One of the biggest advantage of using Firebase is that it has built-in support for realtime notifications. That means that when a new item is added or removed it will automatically be notified in respective events. So Firebase ease the process of setting up Socket, Node JS and Mongo DB.

Firebase console

Goto Firebase Console and create a new project. Set the name of project and click “Continue”, you can disable the analytics for now. After that, click on “web” icon to integrate Javascript API in your project. Set the name of your project again and DO NOT set Firebase hosting. Because you will be running on your own localhost or hosting server. Then you will be displayed a code which you can copy and paste at the top of your project.

In your firebase console, goto “Database” tab from left menu and click on “Realtime Database”. For now, you can setup the database in test mode. The code you copied from Firebase console will only have firebase-app.js. But you will also need firebase-database.js in order to use database. We will be using file named “index.php” where all chat operations are performed. Below code has some empty values but when you copy from Firebase console then they will be filled automatically as per your project.

index.php

<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/6.6.1/firebase-app.js"></script>

<!-- include firebase database -->
<script src="https://www.gstatic.com/firebasejs/6.6.1/firebase-database.js"></script>

<script>
	// Your web app's Firebase configuration
	var firebaseConfig = {
		apiKey: "",
		authDomain: "",
		databaseURL: "",
		projectId: "",
		storageBucket: "",
		messagingSenderId: "",
		appId: ""
	};
	// Initialize Firebase
	firebase.initializeApp(firebaseConfig);
</script>

Sending chat message in Firebase

Open your browser console by right click on empty area of page and select “Inspect Element” from context menu and open the “Console” tab. Make sure your internet is connected and check if there is any error. When it comes to sending message, for the sake of simplicity we will be saving username and message in the database. First we need to get the name of user when page opens. We can use Javascript built-in prompt() function to do that. Then we need to create a simple form with an input field to send message. When that form submits then we will call a function that will save the data in Firebase database.

The autocomplete field will make sure not to display old entered values in this field. At the end of sendMessage() make sure to return false. Otherwise the form will submits to itself and the page refreshed. firebase.database() will return an instance of Firebase database. You can either store it in a separate variable to re-use it or you can call it every-time you need to perform some action in database. When the data is saved then Firebase will fire an event that will be called when a new child is added. We will discuss that in details in next step.

The code below will create a new node named “messages”. It will give it a unique ID and save username and message in it. The nodes in database are called as “Childs” in Firebase database.

<script>
	// Your web app's Firebase configuration
	var firebaseConfig = {
		apiKey: "",
		authDomain: "",
		databaseURL: "",
		projectId: "",
		storageBucket: "",
		messagingSenderId: "",
		appId: ""
	};
	// Initialize Firebase
	firebase.initializeApp(firebaseConfig);

	var myName = prompt("Enter your name");
</script>
	
<!-- create a form to send message -->
<form onsubmit="return sendMessage();">
	<input id="message" placeholder="Enter message" autocomplete="off">

	<input type="submit">
</form>
	
<script>
	function sendMessage() {
		// get message
		var message = document.getElementById("message").value;

		// save in database
		firebase.database().ref("messages").push().set({
			"sender": myName,
			"message": message
		});

		// prevent form from submitting
		return false;
	}
</script>

Display messages from chat in Firebase

To display database data from Firebase we need to attach a listener called “child_added”. The main benefit of using this listener is that it simplifies the process of fetching the old messages from database. It is also used to listen to new incoming messages. So this event will be called in 2 situations.

  1. When the page loads it will get all messages saved in database.
  2. And second, when a new child is added in Firebase database.

Code below will create a list where all messages needs to be displayed and attach a listener which will be called automatically when the page loads. On each iteration it will return a snapshot object which is a Firebase database object and it contains child’s key and value.

On each iteration we are creating a new list and appending at the end of UL tag. As the child’s key is unique throughout the database, so we will be adding this as an ID to list item. This will be used when we try to delete message, it will be covered in detail in next step. We will be displaying delete button only if the message is sent from current user which means you can delete only those messages which are sent by you.

Button tag will have an attribute data-id which will be used to get the unique ID of message and an onclick event is attached which will be called when that button is pressed. At the end we are displaying sender’s name and message content.

<!-- create a list -->
<ul id="messages"></ul>
	
<script>
	// listen for incoming messages
	firebase.database().ref("messages").on("child_added", function (snapshot) {
		var html = "";
		// give each message a unique ID
		html += "<li id='message-" + snapshot.key + "'>";
		// show delete button if message is sent by me
		if (snapshot.val().sender == myName) {
			html += "<button data-id='" + snapshot.key + "' onclick='deleteMessage(this);'>";
				html += "Delete";
			html += "</button>";
		}
		html += snapshot.val().sender + ": " + snapshot.val().message;
		html += "</li>";

		document.getElementById("messages").innerHTML += html;
	});
</script>

Delete messages from chat in Firebase

Since we have already created a button to delete message, now we need to create a function that will be called when that button is pressed. We are going to delete the message from database and notify all other users that, that message has been removed. You can either remove that message node from list item or you can simply change the text of message to “This message has been removed” like WhatsApp does.

Next time you refresh the page, that message will be removed altogether. First we are getting the unique ID of message from the data-id attribute of button. Then we call the remove() function of Firebase instance and it will remove that message child from database. As soon as the child is removed, Firebase has an event named “child_removed” which will be called with deleted child key and value. So we are attaching that listener which will return the deleted child key and value which can be used to remove or alter that node from list item.

Now a delete button will be displayed along with your sent messages, when clicked it will be removed from Firebase database and a message will be displayed saying “This message has been removed”.

function deleteMessage(self) {
	// get message ID
	var messageId = self.getAttribute("data-id");

	// delete message
	firebase.database().ref("messages").child(messageId).remove();
}

// attach listener for delete message
firebase.database().ref("messages").on("child_removed", function (snapshot) {
	// remove message node
	document.getElementById("message-" + snapshot.key).innerHTML = "This message has been removed";
});

That’s how you can do real-time chat in Firebase using Javascript. If you do not want to use Firebase and do chat with your own server. You need to follow this tutorial.

Firebase Storage – Upload, Download, and Delete