Engage users with Firebase Cloud Messaging

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[MUSIC PLAYING] ANDREA WU: Hello, welcome back to the fourth and final video of the series where we are building and managing an expense tracker web app using Firebase, React, and Next. If you didn't watch the other videos, don't worry. You can jump straight into this one without needing to go back to them. As a recap of the video series so far, we built a MVP for our expense tracker in the first video enabling our users to create an account and login as well as add, view, edit, and delete expenses. We did so using Firebase authentication, storage, and Firestore. And after building out all of that, we deployed our great web app to Firebase Hosting, so we can share it with our friends to use. In the second video, we improved upon the MVP and implemented optical character recognition, also known as OCR. This enabled users to just upload a receipt image without needing to type in every single field themselves for every single expense they want to add, since that can become really tedious. After uploading the receipt, they'll just need to confirm the contents of the receipt and edit if necessary. We did this using Cloud Functions and Google's Vision API, which automatically extract a text from the uploaded image. In the third video, we talked about how to hide this new OCR feature behind a feature flag during development then gradually roll out the feature to users after we're done developing it. We used the Remote Config to do feature flagging and Analytics to help us understand how users were experiencing the rollout. So what are we going to do today? Well, since we have this app out and about for a while now, we might want to think about how to retain the users we already have and continue to engage them. One idea that comes to mind is sending users a notification once a receipt is finished processing to remind them to confirm the receipt contents. And another idea is to send users a reminder once a week to upload any new expenses that they may have incurred in the last week. [MUSIC PLAYING] To implement both of these ideas, we're going to use Firebase Cloud Messaging. What is Firebase Cloud Messaging, or FCM for short? Well, it's a free service that enables developers to send messages between server and client apps. And these messages can be addressed to single devices, groups of devices, or topics. It can be used to deliver notifications and messages to your users on Android, iOS, and the web, and you don't need to add any logic to keep track of what kind of devices your customers are using or set up different service calls to different devices. Firebase will take care of all of that for you. FCM is powerful and scalable as it delivers hundreds of billions of messages per day for more than a quarter-million applications, with most of them being delivered in less than 200 milliseconds. Let's see a quick overview of how FCM works before we dive into coding. When a device first registers with the service, you get back a token, which can be used to uniquely identify this device to the service. Your device can also ask to subscribe to topics, which can really be any arbitrary string you specify. And the service will keep track of all of that for you. Behind the scenes, Firebase will also associate this user with a unique instance IDs from Analytics for Firebase. So you can send notifications with this user based on certain Analytics data while still doing so in a privacy-respectful way. To send notifications, you simply tell FCM, either using the Firebase Console, Cloud Functions, or your own servers, what message you want to send and who you want to send it to. You can target an individual device using its FCM token, a group of devices that have all subscribed to the same topic, or you can make use of Firebase Analytics to send messages to groups of users who have some exhibited behavior. All right, so now that we have a high-level overview of how FCM works, let's see how we're going to implement it for our app. [MUSIC PLAYING] Before we do that, we'll have to do some project and code setup also to make sure the app is working and running before we try to do anything. Let's clone the GitHub repo, which is linked in the description. Even if it came from any of the other videos, you should clone the repo because the starter code for this video is slightly different from the other videos code. We'll also need a Firebase project. If you don't already have a Firebase project from the video series so far, feel free to follow the link in the description on how to do so. If you do already have a project, please go to Firebase Console, copy our project configuration by going to Project Settings, and paste that into the Firebase Config variable in Firebase.js. Next, copy the storage bucket URL and open storage.js to set the BUCKET_URL variable as what you just copied. This is going to make sure we upload images to the correct storage bucket for your project. We'll also have to do one more step in Firebase Console, which is to upgrade to the Blaze plan if you haven't already, as this code uses Firebase Functions. And you can follow the link in the description to upgrade your plan. After doing all of that, let's do npm install on the command line to install all of the dependencies. Let's then go into our functions folder and NPM install everything there as well. While it's doing that, we can run Firebase Login, which will have us log in via the browser. Choose the appropriate email address, the one with which you created your Firebase project, and allow Firebase CLI to access your Google account. Upon success, the UI will reflect that and so will the command line. Next, let's deploy the function we have in our functions folder by doing Firebase deploy --only functions, and this might take a minute. Let's wait for it to finish. After it does, we can then run the app using npm run dev. And open the web app just to check it's working. Everything seems to be running just fine, so let's get started with FCM. [MUSIC PLAYING] We'll first open Firebase.js and create a constant to store an instance of messaging, which is created by calling getMessaging. The FCM web interface uses web credentials called Voluntary Application server identification or VAPID keys to otherwise send requests to supported web push services. To subscribe the app to push notifications, we'll need to associate a pair of keys with our Firebase project. Let's see how we can generate a new key pair in Firebase Console. We'll go to Project Settings, and go to Cloud Messaging. Down here in the Web Configuration section, we see a Web Push Certificates tab. Now, let's click Generate key pair. The Console displays a notice that the key pair was generated and displays the public key string and date added. What do we do with this? Well, let's copy it and open the messaging.js file. Let's create a constant VAPID_KEY paste it here. When notifications have been enabled on a browser, we'll be given a device token. This device token is what gets used to send a notification to a particular browser. Let's write a function called saveMessagingDeviceToken, in which we'll get a FCM device token from the browser and save it to Cloud Firestore. We'll create a constant to store the current token. And we'll use the method getToken, which takes the messaging instance as the first argument and takes the VAPID key as an option in the second argument. The function returns a promise containing the token string. So let's await it, and if it returns a token, let's print it out to the Console for debugging purposes for later. After, we'll save the device token to Cloud Firestore. To do so, we'll create a reference to where we want to store this device token in Firestore by using the doc function. A Firestore instance is the first argument, and the second argument is the path to the document, which is the collection name in this case. Let's create a constant to store the name of the FCM token collection fcmTokens. We'll use that in the doc function, and the third argument is the identifier for the document. Since we want to associate each FCM token with a user, we'll just use the user ID. Since we need the user ID here, let's take that as an argument to this saveMessagingDeviceToken function. All right, now, using setDoc, we can add the FCM token value for this user. And as a reminder, setDoc will overwrite the document if it already exists, meaning if the user already has a FCM token, the value in Firestore will get overwritten, which is what we want in this case. This is fine for one getToken successfully retrieves a token, but it's not going to work initially. For the app to be able to retrieve the device token, the user needs to grant the app permission to show notifications. So if getToken doesn't retrieve a token, we'll need to request permissions to show notifications. Let's write another function requestNotificationsPermissions, and we'll create a permission constant permission. We'll call the built-in notification web API to request permission and await the results. If permission is granted, we'll call saveMessagingDeviceToken. And since this function requires the UID, we'll take the UID as a parameter to requestNotificationsPermissions and pass it in when we call it in saveMessagingDeviceToken as well. If it fails, we'll just log for now that we cannot get permission. A better way to do this is to propagate it up to the UI and let the user know to check the page later, as opposed to being automatically notified of when to confirm the expense. Now that we have these functions written, where shall we call saveMessagingDeviceToken? Well, we want to request permissions to show notifications when the user submits an expense so that the app can notify the user that the receipt is ready to be confirmed on the app. We'll open expenseDialog.js, and call saveMessagingDeviceToken after we store the image into storage. In order to receive notifications, the app must define the Firebase Messaging Service worker in Firebase-messaging-sw.js. And this file goes in the public folder. Let's open that. We'll need to initialize the app here. So let's copy the Firebase Config from Firebase.js, and then we'll initialize the app and get an instance of Firebase Messaging. Just as I note, this instance of Firebase Messaging is an instance of one from the service worker. So it's a bit different than the instance we retrieved in Firebase.js. Great, let's try it on our app now. Whoops, when we initially open the app, there's an error saying that service messaging is not available. To fix the problem, there is a function in the FCM JavaScript SDK called isSupported, which returns a promise resolving to true if a FCM instance can be initialized in the current environment and false if not. Let's use that and await the results. If it's true, then we can get the FCM instance by calling getMessaging. We'll make all of this a function because otherwise, we're using a wait on global scope, and there is no guarantee on when the instance will be returned. As a function, we can wait on the return value before doing anything else. This means that every time we want to use a FCM instance, we'll need to call this messaging function. Let's go back to messaging.js where we need to use this messaging instance. And we'll change the code to have a constant, msg to store the return value of calling the messaging function we just wrote. Instead of the old messaging constant, we'll use msg. So here in getToken. With this fixed, let's see whether our app works now. It does. To remind ourselves of what we were doing before, we have just written saveMessagingDeviceToken and requestNotificationsPermissions, and we want to see whether that works. And we call saveMessagingDeviceToken after a user uploads an image to Cloud storage. Let's click on adding an expense and upload an image. When we click on Submit, we should be asked for permission to show notifications. And indeed, we are. Let's allow that. Let's take a look at the JavaScript Console. We printed out the FCM device token earlier. And so this confirms that we can get permission and after we do, we're able to get the token. [MUSIC PLAYING] Now, how are we going to actually see the notification? Well, first we need to write some code to send the actual notification. Since we want the user to receive a notification upon the Google Vision API finishing its processing and adding the new expense to the Firestore, we'll write the code to send a notification in our Cloud Function, which has all the expense processing code. Earlier in messaging.js, we stored the users FCM token into the Firestore. We're going to need to retrieve that token, so we know where to send this notification. So we'll create a documentSnapshot constant, and use the admin for Firestore to get the FCM token collection. And we specifically want to document what the user UID, which we already extracted, we'll get the document. And in another constant token, we'll get documentSnapshots.data, and get the FCM token. Now, we know where to send this notification, and we'll have to craft the notification message. Let's make the message, your receipt is ready for review and confirmation. And the payload will include the token, the notification with a title which we'll set as expense tracker your expense has been processed and the body with the message. We'll then use admins messaging to send a payload and upon receiving a response, we'll log it using functions.logger.log to note that we did indeed successfully send the message, just for debugging purposes. If we get an error, let's log that as well. With our updated function, we'll have to deploy it. So let's open the terminal and run firebase deploy --only functions. While that happens, let's figure out how to actually receive the notification. So with FCM, the behavior of messages differs depending on whether the page is in the foreground, meaning it has focus, or in the background hidden behind other tabs or completely closed. In all cases, the page must handle the onMessage callback. So let's implement that. [MUSIC PLAYING] With our service worker set up from before, we can handle messages when our app is in the foreground. We'll go back to messaging.js. And after we set the document in Firestore, we'll call onMessage to console.log that we're going to send a notification from the foreground, which is useful for debugging purposes. Then we're going to use the notification web API to send a notification with the messages notification title and body. Let's check whether this works. Our function is now deployed, and so if we open the web app and add an expense, choose an image to submit, and wait a bit while the app is in the foreground, we shall see the notification. And here it is. Yay. Now, what happens if the app is in the background? Well, we'll have to go back to. firebase -messaging-sw.js just to take care of that. We'll use messaging.onBackgroundMessage. And we'll console.log that we've received a background message, again, just for debugging purposes. We'll then extract the notification title from the payload by doing payload.notification.title. And we'll create notification options with the body of payload.notification.body. We'll use self.registratio n.showNotification of the notification title and notification options. Let's go back to the app. And this is a bit tricky. It took me a bit of time to figure out. Because sometimes, when I edited this firebase-messaging-sw.js file, the code didn't actually update. To mitigate this issue, and check that it does update, we can inspect the page, go to application, and click on Service Workers. There, we can see the currently running Service Workers, and if we click on it, we can see the code that's currently running. And we see that this background message function we just wrote is not there. To fix that, we can click on this unregister button here on the right, which unregisters the Service Worker. And if we refresh the page, we don't see any Service Workers. If we try again to add an expense, pick an image, and submit it, we can now see the Service Worker here and the associated code. And we see the onBackgroundMessage function now, great. TO make sure it's working, let's bring another page to the foreground, so our app is in the background. And let's wait a moment. Oh, here's the notification. And just to make sure, we can check the console.log for background message to make sure the notification is indeed being triggered from the onBackgroundMessage function and not the foreground one. Just for fun, let's also make sure we get a notification if the app is not open at all. We'll add an expense again, choose an image to upload, and submit. Upon getting the confirmation that the expense was uploaded, let's close the app. Wait a moment and voila, a notification appears. Yay. So with all of this, now we have successfully sent a notification to a user when an image gets uploaded regardless of whether the user has the app open in the foreground, in the background, or not at all. [MUSIC PLAYING] Now, FCM is useful for when we want to send notifications as a result of user actions like we just implemented, but we might also want to send notifications as reminders that are not based on user actions, such as a weekly reminder to upload expenses. Now, it might be annoying to users who are dedicated to uploading their expenses, to get a notification reminding them to upload expenses every single week, as they might be wondering why this app is sending them reminders when they're already doing the thing the notification is reminding them to do. Conveniently, as if we already foresaw this happening, FCM is integrated with capabilities to choose a user segment to receive our notification. In our case, we can only send notifications to users who haven't opened the app for a week. Let's see how we can set up a FCM messaging campaign in Firebase Console to do this. You'll see messaging. Let's go ahead and click on Create First Campaign. We're going to send notification messages, so let's choose that and click Create. We'll need to fill out some fields here, so for the notification title, let's put in Expense Tracker reminder to input expenses. In the notification text, we can say, Have any expenses from the last week that you didn't input yet? Go to the expense tracker and get those entered, so you have a complete view of your expenses. After we fill that out, we see that we can send a test message. Let's try that. If we click on the button, we see needing to add a FCM registration token. Now, this is the FCM token that we've retrieved in our code and added to Firestore in messaging.js. And that's the same FCM token we get from Firestore in our function's code. Conveniently, we printed out this FCM token, when we were writing our messaging.js code. So we can simply go back to our app and grab that from the JavaScript Console. Let's copy the printed token and then paste it into Firebase Console. After clicking on the plus button, we see that it's selected, and now we can click Test. Once we do, we can see how our notification looks, great. On Firebase Console, we'll hit Next. And this is where we'll pick the user segment that will receive this notification. We have to pick the app for which the user is using, and there's only one option here, our expense tracker web app. We can add another condition by clicking And. And then we can select Last app engagement. Since we want to send this notification to users who have not opened our app for more than a week, we'll pick more than, and put in seven days. We then click on Next, which will then bring us to scheduling. We'll probably want to run this every week. So we'll pick recurring notifications every one week. Which day do we want to run it? Well, up to you. Since the day I'm recording this is Tuesday, I'll just leave it at that. What time? I'll just leave it at the default time, and we can leave all the other fields as is. As a note, we can always come back and change the time and end date even after the campaign starts running. We don't need to do anything else here, so we'll click Review then publish. In order to show you this, I will need to have an account that I haven't logged into for exactly seven days at this moment, which unfortunately, I didn't time well enough to do. So in order to show you that these notification campaigns do indeed work, I'm going to go back and remove the condition for last app engagement. So this is how you edit a campaign. There's these three dots here, and we can click on Edit. We can leave the text as is, and click Next. Then hover over this last engagement row, and click on the X here to remove the condition. Let's review and publish. If we wait a few minutes, a notification should show up, soon, now, please. There we go, phew. So that's what should happen every week. And well, since I can't exactly show you this, you'll just have to trust me. So there you have it. Using Firebase Cloud Messaging, we were able to send users a notification that their expense was processed and ready to confirm. And we set up a weekly notification campaign through a Firebase Console to remind those who have not opened the app for a week to submit some expenses. I hope this was helpful, and as this is the final video in the series, it's been a great journey for me. And thanks for following the journey if you've gone through the whole series. Please let me know if you have any feedback. And in the meantime, happy Firebasing. [MUSIC PLAYING]
Info
Channel: Firebase
Views: 19,859
Rating: undefined out of 5
Keywords: Firebase remote config, what is firebase remote config, how to use firebase remote config, firebase remote config tutorial, google analytics, google analytics tutorial, launch features, how to launch features, firebase, react, web, app, authentication, firestore, cloud storage, hosting, developer, developers, firebase developer, firebase developers, google developers, google, Andrea Wu
Id: P51dI2y7QHA
Channel Id: undefined
Length: 25min 33sec (1533 seconds)
Published: Wed Dec 21 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.