Photo by Mark König on Unsplash

Photo by Mark König on Unsplash

Have you ever reached for your phone just to see a notification right away?

How many times have you heard the notification sound and not checked your phone?

Adding push notifications to mobile apps is a powerful feature that enhances the user experience. In this article, I’ll share the steps needed to implement notification integration using Vue.js and Quasar.

In my previous article, where I introduced sending and receiving notifications with Vue.js and Quasar, I described my feelings about the process in a very clear and enjoyable way. Please read it, give it a clap, and come back! 😊

Once I successfully obtained the device ID, it was time to move on to the actual mobile notification integration. It was a challenging task, and as usual, urgent. Let’s get started.

Mobile notification integration consists of the following five steps:

  • Preparing the project with Quasar and Capacitor

  • Creating and configuring a Firebase project

  • Managing Firebase Tokens

  • Setting up notification features

  • Sending notifications from backend

1) Preparing the Project with Quasar and Capacitor

1.1) Add Capacitor to the Project

code
quasar mode add capacitor

1.2) Add Capacitor’s Device API to the Project

code
npm install @capacitor/device

1.3) Platform Configurations

Configure your project to work on both Android and iOS devices with Capacitor.

  • Adding Android Platform: Run the following command to add Android support. This will add Android platform support to your project files and create necessary configurations in the src-capacitor/android directory:

code
quasar build -m capacitor -T androidA
  • Adding iOS Platform: If you want to add iOS support, use the following command. This will generate platform-specific configurations and project files for iOS. You can complete the device connection using Xcode for iOS:

code
quasar build -m capacitor -T ios

1.4) Synchronize Changes

code
npx cap sync

1.5) Open the src-capacitor/android Directory in Terminal

Navigate to the directory and re-run the last two commands:

code
npm install @capacitor/device
npx cap sync

Then, in the project root directory, run the following command:

code
npx cap sync android

2) Creating and Configuring a Firebase Project

Firebase provides a strong infrastructure for push notifications. We’ll use Firebase Cloud Messaging (FCM) to send notifications to devices, allowing us to communicate with each mobile device. Now, we need to create and configure a new project in Firebase.
Note: Firebase occasionally updates its interface, so I won’t include screenshots here. If the interface looks different, explore and find the options mentioned!

2.1) Creating a New Project in Firebase Console

  • Go to the Firebase Console and log in.

  • Click on “Add Project” and give your project a name.

  • Confirm the necessary permissions and project information, then create your project.

2.2) Enabling FCM

To activate push notifications, we need to enable FCM in the Firebase project.

  • Go to the Cloud Messaging or Messaging section in the project settings.

  • Check the project’s settings and permissions to ensure FCM is active; enable it if it’s not.

2.3) Adding iOS and Android Apps

To send notifications through Firebase, we need to set up configurations for both iOS and Android platforms.

  • Adding an Android App: Click on the “Android” icon and enter your App ID (Android Package Name), which can be found in your project’s android/app/build.gradle file. Complete the configurations, then add the generated google-services.json file to your project.

  • Place google-services.json in the src-capacitor/android/app folder.

  • Adding an iOS App: Click on the “iOS” icon and fill in the required details. Download the GoogleService-Info.plist file created by Firebase and place it in the appropriate folder within your iOS project.

2.4) Firebase SDK and API Keys

To send notifications successfully, we need to add API keys from the Firebase project to our project.

  • Getting the API Key: Go to Project Settings > General in the Firebase Console. Take note of the API keys listed here.

  • Add the necessary keys to the Firebase SDK configuration and your quasar.config.js file.

code
const firebaseConfig = {
  apiKey: "FIREBASE_API_KEY",
  authDomain: "YOUR_APP.firebaseapp.com",
  projectId: "YOUR_PROJECT_ID",
  storageBucket: "YOUR_APP.appspot.com",
  messagingSenderId: "YOUR_SENDER_ID",
  appId: "YOUR_APP_ID"
};

At this stage, the project you created in Firebase is now set up to receive notifications on both Android and iOS devices.

3) Firebase Token Management

The FCM tokenallows devices to authenticate with the Firebase Cloud Messaging server. Each device receives a unique token for the app to receive notifications. To send notifications specific to a user or device, this token is sent to the backend.

This token can change under the following conditions:

  • When the app is first installed on the device or reinstalled after being removed,

  • After its lifespan expires,

  • When the user clears the app data through device settings,

  • When the user removes or adds a Google account on the device,

  • In some cases, due to security updates or configuration changes made by Firebase,

  • Through manual interventions like invalidating the key from cbackend.

In these situations, your app should get a new key from FCM using onTokenRefresh or a similar handler and update this key on the server.

In this section, we will use the necessary libraries for FCM, register our device with Firebase, obtain the token, and learn how to use it. First, I’ll share the package.json file with you. You can install the required Capacitor and Firebase libraries by running the npm install command in the terminal.

code
"dependencies": {
    "@capacitor-community/fcm": "^6.0.0",
    "@capacitor/android": "^6.0.0",
    "@capacitor/app": "^6.0.1",
    "@capacitor/core": "^6.0.0",
    "@capacitor/device": "^6.0.0",
    "@capacitor/ios": "^6.0.0",
    "@capacitor/keyboard": "^6.0.0",
    "@capacitor/push-notifications": "^6.0.0",
    "firebase": "^10.8.0",
    "@quasar/extras": "^1.0.0",
    "axios": "^1.6.2",
    .
    .
    .
  },

3.1) Sending Device Information to the Backend for Notification Delivery

First, as I explained in my initial article, you’ll need the device token. You can obtain this and other device information from the code example below. Have you seen the import method used here? Usually, imports are placed at the top, but since this code only runs on mobile devices, the import is done dynamically under a platform check.

code
import { Platform } from 'quasar'
.
.
.
if (Platform.is.nativeMobileWrapper) {
  const { Device } = await import("@capacitor/device");
  const { FCM } = await import("@capacitor-community/fcm");
  const deviceId = await Device.getId()
  const deviceInfo = await Device.getInfo()
  const sdkVersion = deviceInfo.osVersion // (android) 31 veya (iOS) 17.2.1
  const deviceType = deviceInfo.platform // android veya iOS

  // Send these to backend.

3.2) Code Required to Obtain the Firebase Token

This section covers the most important part of our work. The critical code snippets that will be added to different files will handle a major portion of the task.

  • In the quasar.config.js file, add the following line under the boot array:

code
boot: ["PushNotificationListeners.js", ...],
  • Then, in the src/boot folder, create a file named PushNotificationListeners.js as specified in the boot section. This file will manage the entire process, including the preparation before receiving a push notification and the handling after it arrives.

code
import { boot } from "quasar/wrappers";
import { LocalStorage, Platform } from "quasar";
import { FCM } from "@capacitor-community/fcm";
import { PushNotifications } from "@capacitor/push-notifications";
import { Device } from "@capacitor/device";

export default boot(async ({ app, router }) => {

  const handleNotificationAction = async (notificationData) => {
    const data = notificationData.data;

    if (data && data.id) {
      await router.isReady();
      router.push({name: "ArticleDetail", query: { id: data.id }});
    }
  };

  const addListeners = async () => {
    try {
      await PushNotifications.addListener("registration", (token) => {
        console.info("Registration token: ", token.value);
      });

      await PushNotifications.addListener("registrationError", (err) => {
        console.error("Registration error: ", err.error);
      });

      await PushNotifications.addListener("pushNotificationReceived",
        (notification) => {
          if (process.env.NODE_ENV === "development") {
            console.log("Push notification received: ",
               JSON.stringify(notification, null, 2));
          }
        }
      );

      await PushNotifications.addListener("pushNotificationActionPerformed",
        (notification) => {
          handleNotificationAction(notification.notification);
        }
      );
    } catch (error) {
      console.error("Error adding listeners: ", error);
    }
  };

  const checkNotificationPermissions = async () => {
    try {
      let permStatus = await PushNotifications.checkPermissions();
      if (permStatus.receive === "prompt") {
        permStatus = await PushNotifications.requestPermissions();
      }
      return permStatus.receive === "granted";
    } catch (error) {
      console.error("Error checking permissions: ", error);
      return false;
    }
  };

  const registerNotifications = async () => {
    const hasPermission = await checkNotificationPermissions();
    if (!hasPermission) throw new Error("User denied permissions!");

    try {
      await PushNotifications.register();
    } catch (error) {
      console.error("Error registering notifications: ", error);
    }
  };

  const fcmRegister = async () => {
    try {
      const response = await FCM.getToken();
      LocalStorage.setItem("FIREBASE_TOKEN", response.token);
    } catch (error) {
      console.error("Error getting FCM token: ", error);
    }
  };

  if (Platform.is.nativeMobileWrapper) {
    try {
      await addListeners();
      await registerNotifications();
      await fcmRegister();
    } catch (error) {
      console.error("Error in notification setup: ", error);
    }
  }
});

This code block obtains the necessary permissions during the startup of our Quasar app, adds notification listeners, and prepares the device to retrieve its FCM token and send it to the backend. Let’s go through it step by step. Please don’t skip this explanation, as simply copying and pasting code without understanding it can lead to more time spent later when you try to figure it out. Also, if you mention “I integrated notifications” in a job interview, they’ll ask for details — don’t get caught off guard! 😂

3.2.1) Importing Modules and Dependencies:

code
import { boot } from "quasar/wrappers";
import { LocalStorage, Platform } from "quasar";
import { FCM } from "@capacitor-community/fcm";
import { PushNotifications } from "@capacitor/push-notifications";
import { Device } from "@capacitor/device";
  • boot: In Quasar applications, boot files are loaded once when the app starts, as you may know. This function allows various initial settings to be added to the project.

  • LocalStorage: Used to store persistent data in the browser. Here, we use it to save the Firebase token.

  • Platform: Quasar’s Platform module helps determine the type of device (e.g., mobile or desktop).

  • FCM: The FCM (Firebase Cloud Messaging) library from the @capacitor-community/fcm module, used to enable push notification functionality.

  • PushNotifications: The @capacitor/push-notifications module, which is used to manage push notification processes.

  • Device: The @capacitor/device module, used to retrieve device information.

3.2.2) Main Boot Function:

code
export default boot(async ({ app, router }) => { ... });

Purpose: Runs when the Quasar application starts. The app and router objects provide access to the app's Vue instance and router functions.

3.2.3) Navigation Function for Notifications:

code
const handleNotificationAction = async (notificationData) => {
  const data = notificationData.data;
  if (data && data.id) {
    await router.isReady();
    router.push({ name: "ArticleDetail", query: { id: data.id } });
  }
};
  • Purpose: Redirects the app to a specific page when the user clicks on a notification.

  • data.id: This is the data within the notification containing an ID for specific content (e.g., article details). If this ID exists, the app navigates to the ArticleDetail page with the id as a query parameter.

3.2.4) Adding Notification Event Listeners:

code
const addListeners = async () => { ... };
  • Purpose: Listens to various notification events and manages them.

  • registration: Triggered when the app successfully registers and receives a token, displaying this token in the console.

  • registrationError: Triggered if an error occurs during registration, logging the error in the console.

  • pushNotificationReceived: Triggered when a notification is received, logging the notification content in the console.

  • pushNotificationActionPerformed: Triggered when the user interacts with the notification (by calling handleNotificationAction), performing an action based on the data in the notification.

3.2.5) Checking Notification Permissions:

code
const checkNotificationPermissions = async () => { ... };
  • Purpose: Checks for the necessary permissions to allow the app to display push notifications.

  • checkPermissions(): Verifies the notification permission status. If permission isn’t granted, it prompts the user.

  • Result: Returns true if permission is granted, false otherwise.

3.2.6) Notification Registration:

code
const registerNotifications = async () => { ... };
  • Purpose: Registers the app to receive push notifications after the necessary permissions have been granted.

  • Calls the checkNotificationPermissions function and throws an error if permission is not granted. If permission is granted, it registers for notifications with PushNotifications.register().

3.2.7) Retrieving the FCM Token:

code
const fcmRegister = async () => { ... };
  • Purpose: Retrieves the FCM token and stores it in LocalStorage.

  • Uses FCM.getToken() to get the FCM token. Once obtained, the token is stored in LocalStorage under the key "FIREBASE_TOKEN". This token can then be sent to the backend to enable notification delivery.

3.2.8) Platform Check and Function Calls:

javascript
if (Platform.is.nativeMobileWrapper) {
  try {
    await addListeners();
    await registerNotifications();
    await fcmRegister();
  } catch (error) {
    console.error("Error in notification setup: ", error);
  }
}
  • Purpose: Ensures that the code runs only on mobile devices.

  • By checking Platform.is.nativeMobileWrapper, we confirm if the app is running on a mobile device. If it is, the addListeners, registerNotifications, and fcmRegister functions are called.

  • Error Handling: All function calls are wrapped in a try-catch block. If an error occurs, an error message is printed in the console.

W hy Can’t We Get the Token from the capacitor/push-notifications Package?
I tried this months ago. When I wrote the code using this library, I couldn’t manage to retrieve the token. As I recall, I got an error like “FCM is not implemented on web.” FCM thought I was trying to write the notification code as a web-based app for the browser. So, I’ll demonstrate how to retrieve the token using the capacitor-community/fcm library. Although it only took a paragraph to write, our journey to find this library could have made for a horror story.

3.3) Subscribing and Unsubscribing to Specific Notification Topics

FCM allows users to subscribe and unsubscribe to specific topics, enabling them to customize their notification experience. This feature ensures that notifications are sent only to relevant user groups, preventing unnecessary notifications. Thus, users can manage their notification preferences based on their interests or needs.

3.3.1) Use Cases

  • Sending Notifications to a Target Audience: You can segment users into specific groups and send notifications tailored to each group. For example, users subscribed to the “updates” topic receive only notifications about app updates, while those subscribed to the “special_offers” topic get notifications about promotions and discounts.

  • Managing Notifications Based on User Preferences: In an app’s settings screen, users can subscribe to or unsubscribe from notification types they’re interested in. This allows users to select notifications based on their interests, creating a personalized experience.

  • For Special Events and Campaigns: A temporary topic (e.g., “summer_sale”) can be created to be active only during a specific campaign or event. Users can subscribe to notifications related solely to this period.

Sample Code for Subscription Operations:
The following code shows how to subscribe and unsubscribe users to specific topics using FCM:

javascript
import { FCM } from "@capacitor-community/fcm";

// Subscribing the user to a specific topic
const subscribeToTopic = async (topic) => {
  try {
    await FCM.subscribeTo({ topic });
    console.log(`Successfully subscribed to topic: ${topic}`);
  } catch (error) {
    console.error(`Error subscribing to topic ${topic}:`, error);
  }
};

// Unsubscribes the user from a specific topic
const unsubscribeFromTopic = async (topic) => {
  try {
    await FCM.unsubscribeFrom({ topic });
    console.log(`Successfully unsubscribed from topic: ${topic}`);
    // A notification can be shown to the user upon successful unsubscription
  } catch (error) {
    console.error(`Error unsubscribing from topic ${topic}:`, error);
  }
};

// Example usage
await subscribeToTopic("updates"); // Subscribes to the "updates" topic
await unsubscribeFromTopic("updates"); // Unsubscribes from the "updates" topic
  • subscribeToTopic: Subscribes the user to the specified topic. Notifications sent to this topic will be delivered to all devices subscribed to it. After a successful subscription, you can provide feedback to the user confirming their subscription.

  • unsubscribeFromTopic: Unsubscribes the user from the specified topic. Once unsubscribed, the user will no longer receive notifications for that topic. It’s also helpful to show feedback confirming unsubscription once this process is successful.

3.3.2) Managing Subscriptions in the User Interface
On a settings screen, you can present a list or options for the notification types users can subscribe to. For example:

  • Using Checkboxes or Toggles: Add a checkbox or toggle button for each notification type. When a user turns a toggle on, the subscribeToTopic function is called; when turned off, the unsubscribeFromTopic function is called.

code
<template>
  <q-list bordered padding>
    <q-item v-for="topic in topics" :key="topic.name" clickable>
      <q-item-section>
        <q-checkbox
          v-model="topic.subscribed"
          @update:model-value="onToggleTopic(topic)"
          :label="topic.label"
        />
      </q-item-section>
    </q-item>
  </q-list>
</template>

<script>
import { ref } from "vue";
import { subscribeToTopic, unsubscribeFromTopic } from "./path/to/fcmUtils";

export default {
  setup() {
    // Define notification topics
    const topics = ref([
      { name: "updates", label: "Updates notifications", subscribed: false },
      { name: "special_offers", label: "Özel teklifler bildirimi", subscribed: false },
    ]);

    // Manage topic subscription
    const onToggleTopic = async (topic) => {
      try {
        if (topic.subscribed) {
          await subscribeToTopic(topic.name);
          console.log(`${topic.label} konusuna abone olundu.`);
        } else {
          await unsubscribeFromTopic(topic.name);
          console.log(`${topic.label} konusundan abonelik kaldırıldı.`);
        }
      } catch (error) {
        console.error(`Error toggling subscription for ${topic.name}:`, error);
      }
    };

    return { topics, onToggleTopic };
  },
};
</script>
  • topics Array: Defines an array of notification topics, each containing its name, label, and subscribed status. To add a new notification type, simply add an object to the topics array.

  • v-for and q-checkbox: A q-checkbox component is created for each notification topic using the v-for loop. This checkbox is bound to the subscribed status with v-model, automatically updating the subscription status based on the user’s selection.

  • onToggleTopic Function: This function runs when a checkbox is checked or unchecked. If subscribed is true, it calls subscribeToTopic to subscribe; otherwise, it calls unsubscribeFromTopic to unsubscribe.

This setup provides a more dynamic, organized, and scalable solution using Quasar’s q-checkbox and q-list components. You can also store this on the backend.

3.3.3) Key Points When Managing Notification Channels

  • Feedback: Showing a brief feedback message (e.g., “Subscribed to Updates channel”) on the screen when a user subscribes or unsubscribes enhances the user experience.

  • Permission and Consent: Asking the user’s consent to receive notifications helps create a user-friendly experience by preventing unwanted notifications.

  • Storing Subscription Status: Save the topics that the user is subscribed to in LocalStorage or a database so that these settings remain updated when the app is reopened. For example:

code
// Saving Subscription Status:
LocalStorage.setItem("isSubscribedToUpdates", true);

// Checking Subscription Status:
const isSubscribed = LocalStorage.getItem("isSubscribedToUpdates");

With this section, we’ve enabled users to subscribe to or unsubscribe from notification types relevant to their interests using Firebase Cloud Messaging. This approach helps users receive only the notifications that matter to them, reduces unnecessary notifications, and enhances the user experience of your app.

Photo by Brian J. Tromp on Unsplash

Photo by Brian J. Tromp on Unsplash

4) Configuring Notification Features

In this section, we’ll focus on additional settings and configurations that ensure effective notification management beyond user permissions. These steps help the app handle notifications correctly and provide a more impactful user experience:

4.1) Notification Categories and Priority Settings

  • Notification Types: Categorizing notifications personalizes the user experience. For example, separate categories can be created for different notification types like “updates,” “messages,” or “reminders.” This allows notifications to be processed distinctly.

  • Priority Levels: You can set priority levels for notifications on both Android and iOS platforms. For instance, important notifications can be given high priority, while less critical ones can be set to a lower priority.

4.2) Silent Notifications

Silent notifications are used for background data updates without disturbing the user. For example, when a new message arrives, this type of notification can be used to update data without sending an audible or visual notification to the user.
To enable silent notifications, the backend can add a parameter like "content-available": 1 to the notification payload.

4.3) Notification Sounds and Custom Ringtones

You can set different notification sounds or custom ringtones to capture the user’s attention. For example, assigning a standard sound for message notifications and a distinct sound for emergency notifications can be effective.
On both Android and iOS, custom sound files can be specified and placed within the app directory.

4.4) Notification Images and Icons

Using small icons or large images in notifications can make the message more attention-grabbing.

  • Large Images: Features like bigPicture on Android and attachments on iOS allow you to display large images, which is useful for product updates or news content.

  • Small Icons: Small icons are especially required in notifications on Android. You can configure app icons or custom icons in the appropriate sizes in the res directory in Quasar.

4.5) Timing and Delaying Notifications

Some notifications may need to be shown at a specific time or after an event. For example, you might use scheduled notifications to send a reminder if a user hasn’t opened the app for a certain number of days.
Scheduling notifications is particularly useful for reminders or event-based notifications. This feature can be programmed on the backend or in Firebase to trigger after a specified delay.

4.6) Managing Notification Frequency

To prevent users from receiving too many notifications, you can set a limit. For instance, you could add a restriction on the backend to prevent a user from receiving the same type of notifications consecutively, or keep a timestamp within the app.

  • Frequency Limiting: Controlling the frequency of notifications, such as setting daily or hourly limits, can improve the user experience.

4.7) Multi-Language Support (Localization)

You can add multi-language support to display notifications in the language set by the app. Notifications sent from Firebase or the backend can include “locale” information to adjust the language.
This feature provides a user-friendly experience, especially for apps targeting international audiences.

4.8) Dismissible and Recurring Notifications

  • Dismissible Notifications: Some notifications can be set to automatically close after a certain time, such as a notification that informs the user when a download is complete.

  • Recurring Notifications: You can schedule recurring notifications for reminders or events at specific intervals, which is useful for apps needing daily or weekly reminders.

These types of notifications aren’t managed through Firebase. They are developed, displayed, and managed natively on Android or iOS.

4.9) In-App Notification Handling

You can add in-app notification handling to manage notifications when the app is open. For example, when a notification arrives while the user is active in the app, it can be shown within the app rather than in the notification panel.
In-app notifications are ideal for grabbing the user’s attention directly through the app and are commonly used in chat or social media apps.

These configurations allow for more user-friendly notification management and customization. By following the steps in this section, you can improve the notification functionality of your app, making it more efficient and enhancing the user experience.

Now, here’s a sample code snippet written in Quasar and Vue 3 that covers these points:

javascript
const scheduleLocalNotification = async () => {
    const locale = LocalStorage.getItem("user_locale") || "en"; // Dil desteği için
    await PushNotifications.schedule({
      notifications: [
        {
          title: locale === "en" ? "Reminder" : "Hatırlatma",
          body: locale === "en" ? "Check the new updates!" : "Yeni güncellemeleri kontrol edin!",
          id: 1,
          schedule: { at: new Date(new Date().getTime() + 10000) }, // 10 saniye sonra
          sound: "default",
          attachments: [{ id: "bigImage", url: "https://example.com/image.jpg" }], // Büyük görsel
          actionTypeId: "OPEN_APP",
          extra: { screen: "Home" },
        },
      ],
    });
  };

During development, I’m sure you’ve experienced moments when notifications just won’t send, blank notification windows appear on your phone screen, you struggle with Firebase’s test notification interface, or you end up receiving the same notification repeatedly. If you’re reading this guide, you’re likely to experience these frustrations as well. Waiting impatiently for a notification while reviewing your code, trying to pinpoint mistakes, even catching and fixing bugs before the notification arrives, only to realize it won’t come after all…

These are truly patience-testing moments. But stay patient, keep reading through this long guide, and success will be yours. The thrill of finding the right solution and the satisfaction of seeing notifications working smoothly is well worth it. In this article, I’ve shared steps to minimize those frustrating moments and make the process more enjoyable. For readers only interested in developing push notifications with Quasar and Vue.js, feel free to skip to the conclusion.

5) Sending Notifications from the Backend
There are plenty of resources on this topic, so I’ll keep this section brief. Since I’ve been directly involved and personally coded every stage of this development, I’ll summarize this part in the article.

As a team lead now, I enjoy overseeing all development stages, ensuring my team writes clean code, and conducting code reviews to keep everything on track — even if I’m not directly coding every line.

To send notifications from the backend, we need to connect to FCM using the Firebase Admin SDK. This SDK allows secure communication with Firebase in a server environment. In this example, we’ll use Node.js to send notifications.

5.1) Installing Firebase Admin SDK
Use the following command to install the Firebase Admin SDK:

code

5.2) Firebase Service Account Settings

Create a service account JSON file through the Firebase Console. This file enables the backend to authenticate with Firebase. Save it in a secure location within your project.

5.3) Configuring the Firebase Admin SDK

Use the Firebase service account file to initialize the Firebase Admin SDK.

code
const admin = require("firebase-admin");
// Load the service account JSON file
const serviceAccount = require("./path/to/serviceAccountKey.json");
// Initialize the Firebase Admin SDK
admin.initializeApp({
    credential: admin.credential.cert(serviceAccount)
});

5.4) Sending Notifications

With the Firebase Admin SDK, you can send notifications to a specific user or a particular topic.

5.4.1) Sending a Notification to a Single Device
To send a notification to a single device, you need the device’s FCM token. For example, if a user’s FCM token is stored as registrationToken, you can send a notification with the following code:

code
const sendNotificationToDevice = async (registrationToken, title, body) => {
  const message = {
    notification: {
      title: title,
      body: body,
    },
    token: registrationToken,
  };

  try {
    const response = await admin.messaging().send(message);
    console.log("Successfully sent message:", response);
  } catch (error) {
    console.error("Error sending message:", error);
  }
};
code
// Example usage
sendNotificationToDevice(
  "device_fcm_token",
  "New Update!",
  "New features have been added. Check it out now!"
);

This code sends a notification to a specific device with a title and message body.

5.4.2) Sending a Notification to Users Subscribed to a Specific Topic
You can use the topic property to send a bulk notification to all users subscribed to a specific topic. This feature targets all users who have subscribed to that topic.

code
const sendNotificationToTopic = async (topic, title, body) => {
  const message = {
    notification: {
      title: title,
      body: body,
    },
    topic: topic,
  };
code
try {
    const response = await admin.messaging().send(message);
    console.log(`Successfully sent message to topic ${topic}:`, response);
  } catch (error) {
    console.error(`Error sending message to topic ${topic}:`, error);
  }
};
// Example usage
sendNotificationToTopic(
  "updates",
  "New Update!",
  "Check out all the new features now!"
);

5.4.3) Adding Data to Notifications
By adding extra data to notifications, you can enable more complex actions within the app. For example, you can send a notification with a specific article ID (id) to direct the user to that article.

code
const sendDataNotification = async (registrationToken, title, body, data) => {
  const message = {
    notification: {
      title: title,
      body: body,
    },
    data: data,
    token: registrationToken,
  };
code
try {
    const response = await admin.messaging().send(message);
    console.log("Successfully sent data message:", response);
  } catch (error) {
    console.error("Error sending data message:", error);
  }
};
// Example usage
sendDataNotification(
  "cihaz_fcm_tokeni",
  "Yeni Article published!",
  "Check out all the new features now!",
  { id: "12345" }
);

Code Explanations

  • sendNotificationToDevice: This function sends a notification to a specific device using registrationToken.

  • sendNotificationToTopic: This function sends a notification to all devices subscribed to a specific topic value, ideal for sending bulk notifications.

  • sendDataNotification: This function sends extra data along with the notification, allowing additional actions within the app based on notification clicks.

Usage Tips and Best Practices

  • Control Notification Frequency: Avoid sending notifications too frequently, as excessive notifications may reduce user interest.

  • Topic-Based Subscriptions: Allow users to subscribe only to topics they’re interested in, enabling more targeted notifications.

  • Data Security: Avoid adding sensitive data to notifications; any data sent in notifications is readable on the device.

  • Log Successful Deliveries: Keep logs to track whether notifications were successfully sent. This helps analyze the effectiveness of notifications.

Personally, when I come across an app that sends too many notifications, I hold down on the notification, mute it, and never see notifications from that app again.

With this section, we’ve covered the backend processes of sending notifications to users or topics in detail.

Conclusion

In this guide, we reviewed the steps for integrating Firebase Cloud Messaging (FCM) notifications into a mobile app using Vue.js and Quasar. We began by configuring Quasar and Capacitor, then created a Firebase project and managed FCM tokens. We demonstrated how to handle topic subscriptions and unsubscriptions to allow users to receive notifications based on their interests. Finally, we covered sending notifications on the backend, either to a specific user or a topic.

By completing these steps, you can offer personalized, topic-based, targeted notifications in your app, enhancing the user experience. Successful notification integration increases user engagement and activity, boosting the effectiveness of the app. Remember to respect notification frequency and user preferences to deliver a user-friendly notification experience. If you wish to expand your app’s notification infrastructure in the future, these foundational steps will help you build a more sophisticated notification management system.