Giới thiệu

Trong môi trường di động ngày nay, thông báo push là một trong những kênh giao tiếp quan trọng nhất giữa ứng dụng và người dùng. Khi một tính năng được triển khai đúng cách, nó không chỉ tăng tỷ lệ giữ chân người dùng mà còn cải thiện trải nghiệm người dùng một cách đáng kể. Bài viết này sẽ hướng dẫn chi tiết cách xây dựng một hệ thống thông báo push đa nền tảng (iOS và Android) bằng React Native, Firebase Cloud Messaging (FCM)TypeScript. Tất cả các bước sẽ được minh họa bằng mã nguồn thực tế, kèm theo các lưu ý, best practices và cách kiểm thử.

Kiến trúc tổng quan

Hệ thống thông báo push bao gồm ba thành phần chính:

  1. Firebase Cloud Messaging: Dịch vụ đám mây của Google chịu trách nhiệm gửi tin nhắn từ server tới thiết bị.
  2. Server backend (Node.js/Express): Nơi chứa logic quyết định khi nào, cho ai và nội dung gì sẽ được gửi.
  3. Ứng dụng React Native: Nhận và hiển thị thông báo, đồng thời quản lý token đăng ký.

Trong phần này, chúng ta sẽ tập trung vào việc tích hợp FCM vào phía client (React Native) và cung cấp một ví dụ backend đơn giản để gửi thông báo.

Cài đặt môi trường

Bước 1: Tạo dự án React Native với TypeScript

Đảm bảo máy tính đã cài Node.js, npm hoặc yarn, và React Native CLI. Thực hiện lệnh sau:

npx react-native init MyPushApp --template react-native-template-typescript

Sau khi dự án được tạo, chuyển vào thư mục dự án:

cd MyPushApp

Bước 2: Cài đặt các package cần thiết

Chúng ta sẽ sử dụng @react-native-firebase/app@react-native-firebase/messaging để tương tác với FCM.

yarn add @react-native-firebase/app @react-native-firebase/messaging

Tiếp theo, chạy lệnh liên kết native modules (đối với React Native < 0.60 không cần, nhưng không gây hại):

npx pod-install

Thiết lập Firebase

1. Tạo dự án Firebase

Truy cập Firebase Console, tạo một dự án mới và thêm hai ứng dụng: Android và iOS. Đối với Android, tải file google-services.json và đặt vào android/app/. Đối với iOS, tải file GoogleService-Info.plist và kéo vào Xcode project.

2. Cấu hình Android

Thêm plugin Google Services vào android/build.gradle:

buildscript {
    dependencies {
        classpath 'com.google.gms:google-services:4.3.15'
    }
}

Và áp dụng plugin trong android/app/build.gradle:

apply plugin: 'com.google.gms.google-services'

3. Cấu hình iOS

Mở ios/YourProject.xcworkspace trong Xcode, kéo file GoogleService-Info.plist vào project. Đảm bảo FirebaseAppDelegateProxyEnabled được bật (mặc định). Thêm quyền push trong Info.plist:

<key>UIBackgroundModes</key>
<array>
    <string>remote-notification</string>
</array>

Tích hợp FCM trong React Native

1. Yêu cầu quyền (Permission)

Trên iOS, cần yêu cầu người dùng cho phép nhận thông báo. Tạo một hook usePushNotification để xử lý toàn bộ luồng.

import { useEffect } from 'react';
import messaging from '@react-native-firebase/messaging';
import { Alert, Platform } from 'react-native';

export const usePushNotification = () => {
    // Yêu cầu quyền (iOS) hoặc kiểm tra trạng thái (Android)
    const requestPermission = async () => {
        const authStatus = await messaging().requestPermission();
        const enabled =
            authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
            authStatus === messaging.AuthorizationStatus.PROVISIONAL;
        if (enabled) {
            console.log('Permission granted.');
        } else {
            console.log('Permission denied.');
        }
    };

    // Lấy token thiết bị
    const getFcmToken = async () => {
        const token = await messaging().getToken();
        console.log('FCM Token:', token);
        // Lưu token vào backend nếu cần
    };

    // Xử lý khi nhận thông báo trong foreground
    const foregroundListener = () => {
        const unsubscribe = messaging().onMessage(async remoteMessage => {
            Alert.alert('Thông báo mới', remoteMessage.notification?.body ?? '');
        });
        return unsubscribe;
    };

    // Xử lý khi người dùng nhấn vào thông báo (background hoặc quit state)
    const backgroundListener = () => {
        messaging().onNotificationOpenedApp(remoteMessage => {
            console.log('Notification caused app to open from background state:', remoteMessage.notification);
            // Điều hướng tới màn hình chi tiết nếu cần
        });
        // Khi app được mở từ trạng thái quit
        messaging()
            .getInitialNotification()
            .then(remoteMessage => {
                if (remoteMessage) {
                    console.log('App opened from quit state by notification:', remoteMessage.notification);
                }
            });
    };

    useEffect(() => {
        requestPermission();
        getFcmToken();
        const unsubForeground = foregroundListener();
        backgroundListener();
        return () => {
            unsubForeground();
        };
    }, []);
};

Trong component gốc (App.tsx) chỉ cần gọi hook này:

import React from 'react';
import { SafeAreaView, Text } from 'react-native';
import { usePushNotification } from './src/hooks/usePushNotification';

const App = () => {
    usePushNotification();
    return (
        <SafeAreaView>
            <Text>React Native Push Notification Demo</Text>
        </SafeAreaView>
    );
};

export default App;

2. Đăng ký token lên server

Thông thường, token sẽ được gửi lên backend để lưu trữ và sử dụng khi cần gửi tin nhắn. Dưới đây là một hàm đơn giản dùng fetch:

const registerToken = async (token: string) => {
    try {
        const response = await fetch('https://your-backend.com/api/register-token', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({ token, platform: Platform.OS }),
        });
        const data = await response.json();
        console.log('Token registered:', data);
    } catch (error) {
        console.error('Error registering token:', error);
    }
};

Gọi hàm này ngay sau khi nhận được token trong getFcmToken.

Ví dụ backend gửi thông báo

Để minh họa, chúng ta sẽ xây dựng một server Node.js/Express đơn giản chỉ có một endpoint nhận danh sách token và nội dung tin nhắn, sau đó dùng Firebase Admin SDK để gửi.

// server.js
const express = require('express');
const admin = require('firebase-admin');
const bodyParser = require('body-parser');

// Khởi tạo Firebase Admin với service account key
const serviceAccount = require('./serviceAccountKey.json');
admin.initializeApp({
    credential: admin.credential.cert(serviceAccount),
});

const app = express();
app.use(bodyParser.json());

// Endpoint nhận token và nội dung
app.post('/api/send-notification', async (req, res) => {
    const { tokens, title, body } = req.body;
    if (!tokens || !title || !body) {
        return res.status(400).json({ error: 'Missing fields' });
    }
    const message = {
        notification: { title, body },
        tokens,
    };
    try {
        const response = await admin.messaging().sendMulticast(message);
        res.json({ successCount: response.successCount, failureCount: response.failureCount });
    } catch (err) {
        console.error('Error sending message:', err);
        res.status(500).json({ error: 'Internal server error' });
    }
});

app.listen(3000, () => console.log('Server running on port 3000'));

Để chạy server:

node server.js

Kiểm thử và debug

Kiểm thử trên thiết bị thực

Đối với Android, cần bật Google Play services và đảm bảo thiết bị có kết nối internet. Đối với iOS, cần cấu hình APNs trong Apple Developer portal và tải chứng chỉ vào Firebase.

Sau khi chạy ứng dụng, mở Logcat (Android) hoặc Console (iOS) để xem token và trạng thái đăng ký.

Sử dụng Firebase Console để gửi thử nghiệm

Firebase Console cung cấp giao diện “Cloud Messaging” để gửi tin nhắn tới một token cụ thể. Dán token đã nhận được và gửi một tin nhắn test để xác nhận luồng hoạt động.

Best practices và lưu ý bảo mật

  • Quản lý token: Token có thể thay đổi (khi người dùng gỡ cài đặt hoặc cập nhật app). Nên cập nhật token mỗi khi onTokenRefresh được kích hoạt.
  • Giới hạn payload: FCM giới hạn 4KB cho dữ liệu thông báo. Nếu cần truyền dữ liệu lớn, hãy sử dụng data payload và tải dữ liệu từ server khi người dùng mở app.
  • Độ ưu tiên: Sử dụng priority: 'high' cho tin nhắn thời gian thực, nhưng cần cân nhắc tiêu thụ pin.
  • Bảo mật endpoint: API /api/send-notification nên được bảo vệ bằng token JWT hoặc API key để tránh lạm dụng.
  • Kiểm soát tần suất: Đừng gửi quá nhiều thông báo trong một ngày, tránh gây “notification fatigue”.

Kết luận

Việc triển khai hệ thống thông báo push đa nền tảng với React Native, Firebase Cloud Messaging và TypeScript không chỉ giúp nâng cao trải nghiệm người dùng mà còn mở ra cơ hội tương tác sâu hơn với khách hàng. Bằng cách tuân thủ các best practices, quản lý token cẩn thận và bảo mật API, bạn sẽ có một giải pháp ổn định, dễ mở rộng và phù hợp cho mọi dự án di động.

Để nâng cao kỹ năng và có thêm các dự án thực tế, Tham khảo khóa học "Lập trình App Mobile với React Native + TypeScript" tại đây.