Tại Sao Push Notification Quan Trọng Với Ứng Dụng Di Động?
Nói thật nhé — push notification không phải là thứ "có thì tốt". Nó là yếu tố sống còn quyết định tỷ lệ giữ chân người dùng của ứng dụng. Theo thống kê, app có push notification đúng cách tăng tỷ lệ mở lại lên đến 88% so với app không có. Và với .NET MAUI, tích hợp push notification qua Firebase Cloud Messaging (FCM) là lựa chọn phổ biến nhất — hỗ trợ cả Android lẫn iOS từ một codebase duy nhất.
Nhưng mà, thực tế triển khai thì không hề đơn giản đâu. Bạn phải đối mặt với sự khác biệt giữa Android và iOS, xử lý notification khi app ở foreground/background/bị tắt, quản lý FCM token, cấu hình deep link sao cho mượt mà. Phần lớn tài liệu hiện tại bằng tiếng Anh thì chỉ hướng dẫn một nửa — hoặc chỉ Android, hoặc chỉ iOS, hoặc bỏ qua luôn phần deep link.
Bài viết này sẽ đi qua toàn bộ quy trình từ tạo project Firebase, cấu hình cho cả hai nền tảng, xử lý mọi trạng thái ứng dụng, cho đến deep link — tất cả với code mẫu chạy được trên .NET MAUI 10. Mình đã trải qua kha khá lần "đập đầu vào bàn" khi tích hợp push notification nên hy vọng bài này sẽ giúp bạn tránh được những lỗi tương tự.
Tổng Quan Kiến Trúc Push Notification
Trước khi bắt tay vào code, hãy hiểu rõ luồng hoạt động của push notification đã:
- Ứng dụng đăng ký với Firebase Cloud Messaging và nhận về một FCM token duy nhất cho thiết bị đó
- FCM token được gửi về backend server của bạn và lưu trữ
- Khi cần gửi notification, backend gọi Firebase API với token của thiết bị đích
- Firebase chuyển tiếp message đến APNs (Apple) hoặc trực tiếp đến thiết bị Android
- Ứng dụng nhận message và hiển thị notification
Điểm quan trọng cần nhớ: trên iOS, Firebase không gửi trực tiếp mà phải đi qua Apple Push Notification service (APNs). Đây là lý do iOS luôn cần thêm bước cấu hình APNs key — và cũng là bước mà nhiều bạn hay quên.
Bước 1: Tạo Project Firebase Và Đăng Ký Ứng Dụng
Tạo project Firebase
- Truy cập Firebase Console tại console.firebase.google.com
- Click "Add project", đặt tên (ví dụ:
MauiPushDemo) - Bật hoặc tắt Google Analytics tùy nhu cầu, rồi nhấn "Create project"
Đăng ký ứng dụng Android
- Trong Firebase Console, vào Project Settings → General
- Click "Add app" → chọn biểu tượng Android
- Nhập Android package name — đây chính là
ApplicationIdtrong file.csprojcủa bạn (ví dụ:com.yourcompany.mauipushdemo) - Tải file
google-services.jsonvề máy
Đăng ký ứng dụng iOS
- Quay lại Project Settings, click "Add app" → chọn biểu tượng iOS
- Nhập Bundle ID — cũng chính là
ApplicationIdtrong.csproj - Tải file
GoogleService-Info.plistvề máy
Lưu ý quan trọng: Package name (Android) và Bundle ID (iOS) phải khớp chính xác với ApplicationId trong project .NET MAUI. Mình không đùa đâu — đây là nguyên nhân số 1 khiến push notification không hoạt động, và mình đã thấy lỗi này rất nhiều lần rồi.
Bước 2: Cấu Hình APNs Cho iOS
iOS yêu cầu thêm một bước cấu hình nữa vì Firebase phải giao tiếp qua Apple Push Notification service:
- Đăng nhập Apple Developer Portal → vào phần Keys
- Tạo key mới, tick chọn "Apple Push Notifications service (APNs)"
- Tải file
.p8về và ghi nhớ Key ID - Quay lại Firebase Console → Project Settings → Cloud Messaging
- Trong phần "Apple app configuration", upload file
.p8, nhập Key ID và Team ID
Ngoài ra, bạn cần tạo file Entitlements.plist trong thư mục Platforms/iOS với nội dung:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
</dict>
</plist>
Khi phát hành lên App Store, nhớ đổi giá trị development thành production nhé.
Bước 3: Cài Đặt Và Cấu Hình Project .NET MAUI
Cài đặt NuGet package
Có hai lựa chọn phổ biến:
- Plugin.FirebasePushNotifications (phiên bản mới nhất: 3.2.x) — plugin chuyên biệt, nhẹ, chỉ tập trung vào push notification
- Plugin.Firebase — bộ plugin Firebase đầy đủ hơn, bao gồm cả Analytics, Auth, v.v.
Trong bài viết này, mình chọn Plugin.FirebasePushNotifications vì nó nhẹ hơn và đủ dùng cho mục đích push notification. Không cần phải kéo cả "vũ trụ Firebase" vào project khi chỉ cần gửi notification thôi:
dotnet add package Plugin.FirebasePushNotifications
Thêm file cấu hình Firebase vào project
Sửa file .csproj để include đúng file cho từng nền tảng:
<ItemGroup Condition="$(TargetFramework.Contains('-android'))">
<GoogleServicesJson Include="Platforms\Android\google-services.json" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.Contains('-ios'))">
<BundleResource Include="Platforms\iOS\GoogleService-Info.plist" />
</ItemGroup>
Copy file google-services.json vào Platforms/Android/ và GoogleService-Info.plist vào Platforms/iOS/.
Cấu hình trong MauiProgram.cs
using Plugin.FirebasePushNotifications;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UseFirebasePushNotifications(options =>
{
options.AutoRegistration = true;
})
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
// Đăng ký IFirebasePushNotification qua DI
builder.Services.AddSingleton(
FirebasePushNotification.Current);
return builder.Build();
}
}
Bước 4: Cấu Hình Nền Tảng Android
MainActivity.cs
Điều quan trọng nhất trên Android là thiết lập LaunchMode đúng cách. Nếu bạn dùng SingleTop hoặc SingleTask, khi user bấm vào notification mà app đang chạy, hệ thống sẽ gọi OnNewIntent thay vì tạo Activity mới. Nghe có vẻ nhỏ nhưng nếu quên cái này thì deep link sẽ không bao giờ hoạt động.
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Plugin.FirebasePushNotifications.Platforms;
namespace MauiPushDemo;
[Activity(
Theme = "@style/Maui.SplashTheme",
MainLauncher = true,
LaunchMode = LaunchMode.SingleTask,
ConfigurationChanges = ConfigChanges.ScreenSize
| ConfigChanges.Orientation
| ConfigChanges.UiMode)]
public class MainActivity : MauiAppCompatActivity
{
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
FirebasePushNotificationManager.ProcessIntent(this, Intent);
CreateNotificationChannel();
}
protected override void OnNewIntent(Intent? intent)
{
base.OnNewIntent(intent);
if (intent != null)
{
FirebasePushNotificationManager.ProcessIntent(this, intent);
}
}
private void CreateNotificationChannel()
{
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
var channel = new NotificationChannel(
"default_channel",
"Thông báo chung",
NotificationImportance.High)
{
Description = "Kênh thông báo mặc định"
};
var notificationManager =
(NotificationManager)GetSystemService(
NotificationService)!;
notificationManager.CreateNotificationChannel(channel);
}
}
}
AndroidManifest.xml
Thêm các permission cần thiết vào manifest:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission
android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
Lưu ý: Từ Android 13 (API 33) trở lên, permission POST_NOTIFICATIONS là bắt buộc và phải được request tại runtime. Nếu quên request runtime permission, notification sẽ hoàn toàn im lặng — không lỗi, không log, chỉ đơn giản là không hiển thị gì cả.
Bước 5: Cấu Hình Nền Tảng iOS
AppDelegate.cs
using Foundation;
using UIKit;
using UserNotifications;
using Plugin.FirebasePushNotifications.Platforms;
namespace MauiPushDemo;
[Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate
{
protected override MauiApp CreateMauiApp()
=> MauiProgram.CreateMauiApp();
public override bool FinishedLaunching(
UIApplication application,
NSDictionary launchOptions)
{
var result = base.FinishedLaunching(
application, launchOptions);
// Yêu cầu quyền notification
UNUserNotificationCenter.Current
.RequestAuthorization(
UNAuthorizationOptions.Alert
| UNAuthorizationOptions.Badge
| UNAuthorizationOptions.Sound,
(granted, error) =>
{
if (granted)
{
InvokeOnMainThread(
UIApplication.SharedApplication
.RegisterForRemoteNotifications);
}
});
return result;
}
}
Cái này cực kỳ quan trọng: Trên iOS, push notification qua Firebase chỉ hoạt động ở chế độ Release. Trong Debug mode, phương thức Configure của Firebase có thể fail với lỗi native loading. Mình từng mất cả buổi chiều debug tại sao iOS không nhận notification, cuối cùng hóa ra chỉ cần chuyển sang Release build. Để test push notification iOS, bạn phải build qua TestFlight hoặc AdHoc distribution.
Bước 6: Đăng Ký Và Quản Lý FCM Token
FCM token — nói nôm na — là "địa chỉ nhà" mà Firebase dùng để gửi notification đến đúng thiết bị. Mỗi thiết bị có token riêng, và cái phiền phức là token có thể thay đổi bất kỳ lúc nào. Bạn phải theo dõi sự thay đổi token và cập nhật lên backend ngay.
public partial class MainPage : ContentPage
{
private readonly IFirebasePushNotification _pushNotification;
public MainPage(IFirebasePushNotification pushNotification)
{
InitializeComponent();
_pushNotification = pushNotification;
}
protected override async void OnAppearing()
{
base.OnAppearing();
// Đăng ký nhận push notification
await _pushNotification
.RegisterForPushNotificationsAsync();
// Lắng nghe sự kiện token thay đổi
_pushNotification.TokenRefreshed += OnTokenRefreshed;
// Lấy token hiện tại
var token = _pushNotification.Token;
if (!string.IsNullOrEmpty(token))
{
await SendTokenToBackend(token);
System.Diagnostics.Debug.WriteLine(
$"FCM Token: {token}");
}
}
private async void OnTokenRefreshed(
object? sender,
FirebasePushNotificationTokenEventArgs e)
{
// Token mới — cập nhật lên backend
await SendTokenToBackend(e.Token);
}
private async Task SendTokenToBackend(string token)
{
using var httpClient = new HttpClient();
var payload = new { DeviceToken = token };
await httpClient.PostAsJsonAsync(
"https://your-api.com/api/devices/register",
payload);
}
}
Mẹo nhỏ: Mỗi lần bắt đầu debug session mới, Firebase có thể tạo token mới. Đừng bao giờ cache token cứng trong quá trình development — bạn sẽ tự làm khó mình thôi.
Bước 7: Xử Lý Notification Ở Mọi Trạng Thái Ứng Dụng
Đây là phần phức tạp nhất và cũng là nơi nhiều developer (kể cả mình hồi mới bắt đầu) hay mắc lỗi. Ứng dụng có thể ở ba trạng thái khi nhận notification, và mỗi trạng thái xử lý khác nhau hoàn toàn.
Foreground — App đang mở
Khi app ở foreground, Firebase SDK không tự động hiển thị notification trên thanh thông báo. Bạn phải tự xử lý việc hiển thị:
// Trong App.xaml.cs hoặc service riêng
public partial class App : Application
{
public App(IFirebasePushNotification pushNotification)
{
InitializeComponent();
// Xử lý notification khi app đang mở
pushNotification.NotificationReceived +=
(sender, e) =>
{
// Hiển thị in-app notification
// hoặc cập nhật UI
MainThread.BeginInvokeOnMainThread(() =>
{
Shell.Current.DisplayAlert(
e.Data["title"]?.ToString()
?? "Thông báo",
e.Data["body"]?.ToString()
?? "",
"OK");
});
};
// Xử lý khi user bấm vào notification
pushNotification.NotificationAction +=
OnNotificationAction;
}
private async void OnNotificationAction(
object? sender,
FirebasePushNotificationResponseEventArgs e)
{
// Xử lý navigation dựa trên data
if (e.Data.TryGetValue("route", out var route)
&& route != null)
{
await Shell.Current.GoToAsync(
route.ToString()!);
}
}
}
Background — App bị minimize
Khi app ở background, hệ điều hành tự hiển thị notification cho bạn. Khi user bấm vào, Android gọi OnNewIntent trên Activity hiện tại (nhờ LaunchMode.SingleTask), còn iOS truyền data qua UNUserNotificationCenter. Tin vui là plugin Plugin.FirebasePushNotifications đã xử lý hết phần này rồi — bạn chỉ cần lắng nghe event NotificationAction là đủ.
Terminated — App đã bị tắt hoàn toàn
Đây mới là case khó. Khi user bấm notification lúc app đã tắt hoàn toàn, app được khởi động lại từ đầu. Trên Android, data được truyền qua Intent ban đầu trong OnCreate. Đó chính là lý do dòng FirebasePushNotificationManager.ProcessIntent(this, Intent) trong OnCreate rất quan trọng — nó đảm bảo notification data được xử lý ngay cả khi app vừa cold-start.
Phân biệt Data Message và Notification Message
Firebase có hai loại message, và việc hiểu rõ sự khác biệt sẽ giúp bạn tránh được nhiều bug khó chịu:
- Notification message: Firebase SDK tự hiển thị khi app ở background. Khi app ở foreground,
OnMessageReceivedđược gọi. - Data message: Luôn kích hoạt
OnMessageReceivedở cả foreground lẫn background, cho bạn toàn quyền kiểm soát.
Khuyến nghị: Sử dụng data message nếu bạn cần kiểm soát hoàn toàn cách notification hiển thị. Thật sự, sau nhiều dự án thì mình gần như luôn chọn data message vì nó nhất quán hơn. Gửi từ backend với payload dạng:
{
"message": {
"token": "FCM_TOKEN_CUA_THIET_BI",
"data": {
"title": "Đơn hàng mới",
"body": "Bạn có đơn hàng #1234 cần xử lý",
"route": "//orders/1234",
"image": "https://example.com/order-icon.png"
}
}
}
Bước 8: Tích Hợp Deep Link Từ Notification
Deep link cho phép notification đưa user đến đúng trang cần thiết trong app — thay vì mở trang chủ rồi phải tự tìm đường. Nói thật, thiếu deep link thì push notification mất đi một nửa giá trị.
Đăng ký route trong Shell
// AppShell.xaml.cs
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
// Đăng ký route cho deep link
Routing.RegisterRoute("orders/detail",
typeof(OrderDetailPage));
Routing.RegisterRoute("promotions/detail",
typeof(PromotionDetailPage));
Routing.RegisterRoute("chat",
typeof(ChatPage));
}
}
Xử lý navigation từ notification
private async void OnNotificationAction(
object? sender,
FirebasePushNotificationResponseEventArgs e)
{
if (!e.Data.TryGetValue("route", out var routeObj)
|| routeObj is not string route)
return;
// Đợi app khởi tạo xong (quan trọng khi cold start)
await Task.Delay(500);
await MainThread.InvokeOnMainThreadAsync(async () =>
{
// Truyền parameter nếu có
var queryParams = new Dictionary<string, object>();
if (e.Data.TryGetValue("id", out var id))
queryParams["id"] = id!;
await Shell.Current.GoToAsync(
route,
queryParams);
});
}
Cái Task.Delay(500) ở trên nhìn hơi "hack" nhưng thực sự cần thiết. Khi app cold-start từ notification tap, Shell chưa kịp khởi tạo xong mà bạn đã navigate thì sẽ gặp exception. 500ms thường là đủ, nhưng nếu app của bạn khởi tạo chậm hơn thì có thể cần tăng lên.
Nhận parameter trên trang đích
[QueryProperty(nameof(OrderId), "id")]
public partial class OrderDetailPage : ContentPage
{
private string? _orderId;
public string? OrderId
{
get => _orderId;
set
{
_orderId = value;
LoadOrderDetail(value);
}
}
private async void LoadOrderDetail(string? orderId)
{
if (string.IsNullOrEmpty(orderId)) return;
// Gọi API lấy chi tiết đơn hàng
}
}
Bước 9: Gửi Notification Từ Backend
OK, phần client đã xong. Giờ đến phần backend. Để gửi push notification, backend của bạn cần gọi Firebase Cloud Messaging API v1. Dưới đây là ví dụ sử dụng C# với FirebaseAdmin SDK:
using FirebaseAdmin;
using FirebaseAdmin.Messaging;
using Google.Apis.Auth.OAuth2;
// Khởi tạo Firebase Admin (chỉ gọi 1 lần)
FirebaseApp.Create(new AppOptions
{
Credential = GoogleCredential
.FromFile("firebase-service-account.json")
});
// Gửi notification đến một thiết bị cụ thể
var message = new Message
{
Token = "FCM_TOKEN_CUA_THIET_BI",
Data = new Dictionary<string, string>
{
{ "title", "Khuyến mãi hot!" },
{ "body", "Giảm giá 50% cho đơn hàng đầu tiên" },
{ "route", "promotions/detail" },
{ "id", "promo-2026-spring" }
},
Android = new AndroidConfig
{
Priority = Priority.High
},
Apns = new ApnsConfig
{
Headers = new Dictionary<string, string>
{
{ "apns-priority", "10" }
},
Aps = new Aps
{
Sound = "default",
Badge = 1
}
}
};
var response = await FirebaseMessaging
.DefaultInstance.SendAsync(message);
Console.WriteLine($"Message sent: {response}");
Gợi ý thêm: Nếu bạn dùng Azure Notification Hub làm middleware, hãy chắc chắn cấu hình dùng FCM v1 (không phải legacy) trong Azure Portal → Notification Hub → Settings → Google (FCM v1). Legacy API sắp bị deprecated rồi nên đừng dùng nữa.
Bước 10: Xử Lý Lỗi Thường Gặp Và Troubleshooting
Phần này mình tổng hợp từ kinh nghiệm thực tế khi hỗ trợ nhiều dự án tích hợp push notification. Đây là các lỗi phổ biến nhất và cách fix:
1. Notification không nhận được trên Android
- Kiểm tra package name: Phải khớp chính xác giữa Firebase Console và
ApplicationIdtrong.csproj - Kiểm tra Build Action: File
google-services.jsonphải có Build Action làGoogleServicesJson - Kiểm tra NotificationChannel: Từ Android 8+ (API 26), bạn bắt buộc phải tạo NotificationChannel
- POST_NOTIFICATIONS permission: Từ Android 13, phải request tại runtime
2. Notification không nhận được trên iOS
- APNs key chưa upload: Kiểm tra lại phần Cloud Messaging trong Firebase Console
- Entitlements.plist thiếu: Push Notifications entitlement phải được enable
- Debug mode: Firebase push trên iOS không hoạt động trong Debug — phải test qua TestFlight
- Provisioning profile: Phải dùng profile có bật Push Notifications capability
3. Notification hiển thị nhưng bấm vào không có phản hồi
- LaunchMode sai: Đảm bảo dùng
SingleTaskhoặcSingleToptrên Android - ProcessIntent thiếu: Kiểm tra đã gọi
ProcessIntenttrong cảOnCreatevàOnNewIntent - Event handler chưa đăng ký: Đảm bảo
NotificationActionevent được đăng ký sớm (trongAppconstructor, không phải trong page)
4. FCM Token rỗng hoặc null
- Đảm bảo đã gọi
RegisterForPushNotificationsAsync()trước khi lấy token - Kiểm tra kết nối internet của thiết bị
- Trên iOS simulator, push notification chỉ hỗ trợ từ iOS 16+ trên macOS 13+ với chip Apple Silicon hoặc T2
5. Xung đột plugin
Cái này hơi khó phát hiện. Nếu bạn dùng đồng thời plugin local notification (như Plugin.LocalNotification), có thể xảy ra xung đột khiến push notification bị "nuốt" mất. Hãy kiểm tra xem có plugin nào đang intercept Firebase message không — đặc biệt là các plugin đăng ký cùng broadcast receiver hoặc notification delegate.
Gửi Notification Đến Nhóm Người Dùng Với Topics
Ngoài gửi đến từng thiết bị, Firebase còn hỗ trợ Topic messaging — cho phép gửi notification đến tất cả thiết bị đã subscribe một topic nhất định. Cực kỳ tiện cho các tính năng như thông báo khuyến mãi hoặc tin tức theo chuyên mục:
// Subscribe topic từ client
await _pushNotification.SubscribeTopicAsync("promotions");
await _pushNotification.SubscribeTopicAsync("news_vi");
// Unsubscribe
await _pushNotification.UnsubscribeTopicAsync("promotions");
// Gửi từ backend đến topic
var topicMessage = new Message
{
Topic = "promotions",
Data = new Dictionary<string, string>
{
{ "title", "Flash sale!" },
{ "body", "Chỉ còn 2 giờ để mua hàng giảm giá" },
{ "route", "promotions" }
}
};
await FirebaseMessaging.DefaultInstance
.SendAsync(topicMessage);
Topics rất hữu ích cho các tính năng như: thông báo khuyến mãi, tin tức theo chuyên mục, cập nhật theo vùng địa lý, hoặc phân quyền thông báo theo vai trò người dùng.
Checklist Trước Khi Phát Hành
Trước khi submit app lên store, chạy qua danh sách này một lượt (mình đã bỏ lỡ ít nhất 2 trong số này trong dự án đầu tiên):
- ☑ FCM v1 API đã được cấu hình (không dùng legacy API)
- ☑ APNs key đã upload lên Firebase Console
- ☑ Entitlements.plist trên iOS đã chuyển sang
production - ☑ Backend lưu trữ FCM token và xử lý token refresh
- ☑ Notification hoạt động ở cả 3 trạng thái: foreground, background, terminated
- ☑ Deep link hoạt động đúng từ mọi trạng thái
- ☑ Android 13+ runtime permission đã được xử lý
- ☑ NotificationChannel đã được tạo (Android 8+)
- ☑ File
google-services.jsonđã thêm vào.gitignore - ☑ Test trên thiết bị thật (không chỉ emulator/simulator)
Câu Hỏi Thường Gặp (FAQ)
Push notification trong .NET MAUI có hoạt động trên Windows và Mac Catalyst không?
Hiện tại, Plugin.FirebasePushNotifications chỉ hỗ trợ Android và iOS. Nếu bạn cần push notification trên Windows, hãy xem xét sử dụng Windows Push Notification Services (WNS) riêng hoặc thư viện Shiny.NET hỗ trợ đa nền tảng hơn.
Tôi có thể test push notification trên emulator hoặc simulator không?
Trên Android emulator: có, miễn là emulator có Google Play Services. Trên iOS simulator: có nhưng chỉ từ iOS 16+ trên macOS 13+ với máy Mac chip Apple Silicon hoặc T2. Tuy nhiên, Firebase push trên iOS thường chỉ hoạt động ổn định ở chế độ Release, nên lời khuyên chân thành là hãy test trên thiết bị thật qua TestFlight.
Sự khác nhau giữa Firebase Cloud Messaging và Azure Notification Hubs là gì?
Firebase Cloud Messaging (FCM) là dịch vụ gửi push notification của Google, giao tiếp trực tiếp với thiết bị Android và qua APNs đến iOS. Azure Notification Hubs là middleware đứng giữa backend và các push service (FCM, APNs, WNS) — giúp quản lý đăng ký thiết bị và gửi notification đến nhiều nền tảng từ một API duy nhất. Nếu app của bạn chỉ cần Android và iOS, dùng FCM trực tiếp là đủ và đơn giản hơn. Nếu cần hỗ trợ thêm Windows hoặc quản lý quy mô lớn, Azure Notification Hubs sẽ phù hợp hơn.
Làm sao để gửi notification với hình ảnh trên cả Android và iOS?
Trên Android, bạn gửi URL hình ảnh trong trường image của notification payload — khá đơn giản. Trên iOS thì phức tạp hơn một chút, cần sử dụng Notification Service Extension để tải và hiển thị hình ảnh. May mắn là plugin Plugin.FirebasePushNotifications đã hỗ trợ sẵn rich notification — bạn chỉ cần thêm URL ảnh vào data payload.
FCM token có thời hạn bao lâu? Khi nào cần refresh?
FCM token không có thời hạn cố định, nhưng sẽ thay đổi trong các trường hợp: app được cài lại, user xóa data, app được restore trên thiết bị mới, hoặc Firebase tự quyết định rotate token. Do đó, bạn nên luôn lắng nghe sự kiện TokenRefreshed và cập nhật token mới lên backend ngay lập tức. Đừng bao giờ giả định rằng token cũ vẫn còn hợp lệ.