It’s time for the exciting conclusion of our Firebase chat app tutorial! In Part 3, we equipped our app to handle push notifications. We’re still restricted to sending them out manually from the server, though. Ideally, we would like to send users a push notification every time they receive a new message.
Lucky for us, Google introduced Firebase Cloud Functions earlier this year. Cloud functions reside on the server instead of users’ devices, and they can react to data changes on the server. We’ll write a cloud function that looks for changes in our “Messages” node. This happens whenever a user writes a new message. When our cloud function detects the change, we’ll send out push notifications to our users.
If you thought setting-up push notifications was wonky, wait until you set-up cloud functions!
Set-Up Firebase CLI
The first step toward using cloud functions is to install Firebase CLI (Command Line Interface). In order to install Firebase CLI, you need Node.js. This already sounds like we’re going down a pretty deep rabbit hole, but we’ll get through it! You can download Node.js here.
Now that you have Node.js installed, you have access to the Node Package Manager. Enter the following command in Terminal to install Firebase CLI.
npm install -g firebase-tools
Nice! Now you can login with a follow-up command.
firebase login
You’ve made it in! You now have a variety of commands at your disposal. You can enter firebase list to see all of your projects. If you need to update Firebase CLI, simply enter npm install -g firebase-tools again.
Create a Project Root
The reason we installed Firebase CLI is to upload cloud functions to the server. First, we need a place to store our project’s cloud functions on our local machine. In Terminal, change directories to your project’s root directory. That’s the top-level Chat folder that has your projects and Podfile inside. Once you’re there, type in the following.
firebase init
You’ll see a fancy graphic (at least by Terminal standards) that spells out Firebase with little flames. Just below the graphic, you’ll get a question about which CLI features you would like to set-up in this folder. Use the arrow keys to move down to Functions, then hit space to select it, and enter to confirm. Next, select Chat as the project for this folder. Finally, Terminal will ask you if you want to install dependencies. Do it! This will take a moment, giving you a chance to admire your progress. Once it’s complete, we’re ready to write some code!
Write our Cloud Function
If you look in your Finder window, you’ll notice there’s a new folder inside your Chat folder called functions. Inside the folder, there’s a file called index.js. This is where we’ll write our cloud functions. Open up the file in the editor of your choice (I’m using VS Code today).
At the top of the file, you’ll see the following code.
const functions = require('firebase-functions');
Just below that, add the following.
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
This gives us access to the admin messaging functions. Now we want to write our own function. At the bottom of the document, add a new function.
exports.sendChatNotification = functions.database.ref('/messages/{message}').onWrite(event => {
var messageObject = event.data.val();
const payload = {
notification: {
title: 'You have a new message!',
body: messageObject,
sound: "default"
}
};
const options = {
priority: "high",
timeToLive: 60 * 60 * 24
};
return admin.messaging().sendToTopic("pushNotifications", payload, options);
});
Let me break down what’s happening. Our function is listening for any write events in the “messages” node of our database. Whenever a user posts a new message, this event will trigger and execute the contents of the function.
Inside the function, we grab the value from the event. This is just the message that we save to the database, which includes the username in front of the message. Then we create a payload, which includes a title for the push notification, some content, and a sound specification. We use a static title, with a dynamic body that shows the content of the new message. After setting some standard options, we use the sendToTopic messaging function. In addition to the payload and options, we specify a topic—“pushNotifications”. Every user who is subscribed to the “pushNotifications” category will get the push notification. We’ll subscribe every user to that category in a minute.
Deploy our Cloud Function
Back in Terminal, make sure you’re still inside your project’s directory. If not, navigate back there. Once you’re ready, run the following command to upload your cloud function.
firebase deploy --only functions
This takes longer than you would think, but eventually you’ll see a confirmation that the deployment is complete.
Subscribe Users to Category in Xcode
We actually need to make a minor change in Xcode to subscribe users to a category. Our server code specified that we will only send a push notification to users who are subscribed to the category “pushNotifications”. The best time to subscribe users to this category is right after they sign up. In LoginViewController.swift, import the Firebase Messaging library.
import FirebaseMessaging
Add a function at the bottom of the class to subscribe all users to this category. Remember, they will still only receive the notification if they agree to the prompt when they first open the app.
func subscribeToPushNotifications() {
FIRMessaging.messaging().subscribe(toTopic: "pushNotifications")
}
Finally, call this function at the bottom of saveUsername.
func saveUsername() {
let userDefaults = UserDefaults.standard
userDefaults.set(usernameField.text, forKey: "username")
userDefaults.synchronize()
subscribeToPushNotifications()
}
If you’ve already run the app and chosen a username, delete the app from your iPhone, and reinstall it. You can choose the same username. This time when you choose your name, the Firebase code will subscribe you to the “pushNotifications” category.
Test our Code
Let’s see how well our notification code works. If you have two devices, run the Chat app on both of them. If not, run the app on a device and a simulator. Make sure you enable push notifications when you receive the prompt. Close the app on one of your devices. If you only have one device, close it and keep the simulator open. Now post a message on the open device. Hopefully, you’ll see a push notification show-up on the closed one, letting you know to respond!
Wrap-Up
We’ve done it! You’ll be ready to pounce every time someone contributes a new message to the conversation. We’ve used a lot of Firebase features in this project, including the realtime database, cloud messaging, and cloud functions. Good luck integrating these concepts into your own app ideas!