Nhận tin nhắn trong ứng dụng Flutter

Chọn nền tảng: iOS+ Android Web Flutter Unity C++


Tuỳ thuộc vào trạng thái của thiết bị, tin nhắn đến sẽ được xử lý theo cách khác nhau. Để hiểu rõ các tình huống này và cách tích hợp FCM vào ứng dụng của riêng bạn, trước tiên, bạn cần xác định các trạng thái mà thiết bị có thể ở:

Tiểu bang Mô tả
Nền trước Khi ứng dụng đang mở, đang hiển thị và đang được sử dụng.
Thông tin cơ bản Khi ứng dụng đang mở nhưng ở chế độ nền (đã thu nhỏ). Trường hợp này thường xảy ra khi người dùng nhấn nút "màn hình chính" trên thiết bị, chuyển sang một ứng dụng khác bằng trình chuyển đổi ứng dụng hoặc mở ứng dụng trong một thẻ khác (trên web).
Chấm dứt vào ngày Khi thiết bị bị khoá hoặc ứng dụng không chạy.

Có một số điều kiện tiên quyết phải đáp ứng trước khi ứng dụng có thể nhận tải trọng tin nhắn bằng FCM:

  • Ứng dụng phải được mở ít nhất một lần (để cho phép đăng ký với FCM).
  • Trên iOS, nếu người dùng vuốt để đóng ứng dụng khỏi trình chuyển đổi ứng dụng, thì ứng dụng phải được mở lại theo cách thủ công để tin nhắn trong nền bắt đầu hoạt động trở lại.
  • Trên Android, nếu người dùng buộc thoát ứng dụng khỏi phần cài đặt thiết bị, thì ứng dụng phải được mở lại theo cách thủ công để tin nhắn bắt đầu hoạt động.
  • Trên web, bạn phải yêu cầu mã thông báo (bằng cách sử dụng getToken()) bằng chứng chỉ thông báo đẩy trên web.

Yêu cầu quyền nhận tin nhắn

Trên iOS, macOS, web và Android 13 (hoặc mới hơn), trước khi có thể nhận tải trọng FCM trên thiết bị, trước tiên, bạn phải yêu cầu người dùng cấp quyền.

Gói firebase_messaging cung cấp một API để yêu cầu quyền bằng phương thức requestPermission. API này chấp nhận một số đối số được đặt tên để xác định loại quyền mà bạn muốn yêu cầu, chẳng hạn như liệu tin nhắn có chứa tải trọng thông báo có thể kích hoạt âm thanh hay đọc tin nhắn bằng Siri hay không. Theo mặc định, phương thức này yêu cầu các quyền mặc định hợp lý. API tham chiếu cung cấp tài liệu đầy đủ về mục đích của từng quyền.

Để bắt đầu, hãy gọi phương thức từ ứng dụng của bạn (trên iOS, một phương thức thức có sẵn sẽ hiển thị, trên web, luồng API của trình duyệt sẽ được kích hoạt):

FirebaseMessaging messaging = FirebaseMessaging.instance;

NotificationSettings settings = await messaging.requestPermission(
  alert: true,
  announcement: false,
  badge: true,
  carPlay: false,
  criticalAlert: false,
  provisional: false,
  sound: true,
);

print('User granted permission: ${settings.authorizationStatus}');

Bạn có thể sử dụng thuộc tính authorizationStatus của đối tượng NotificationSettings được trả về từ yêu cầu để xác định quyết định chung của người dùng:

  • authorized: Người dùng đã cấp quyền.
  • denied: Người dùng đã từ chối quyền.
  • notDetermined: Người dùng chưa chọn cấp quyền hay không.
  • provisional: Người dùng đã cấp quyền tạm thời

Các thuộc tính khác trên NotificationSettings trả về thông tin về việc một quyền cụ thể có được bật, tắt hay không được hỗ trợ trên thiết bị hiện tại hay không.

Sau khi được cấp quyền và hiểu rõ các loại trạng thái thiết bị, ứng dụng của bạn có thể bắt đầu xử lý tải trọng FCM đến.

Xử lý tin nhắn

Dựa trên trạng thái hiện tại của ứng dụng, tải trọng đến của các loại tin nhắn khác nhau yêu cầu các cách triển khai khác nhau để xử lý:

Tin nhắn trên nền trước

Để xử lý tin nhắn trong khi ứng dụng của bạn đang ở nền trước, hãy theo dõi luồng onMessage.

FirebaseMessaging.onMessage.listen((RemoteMessage message) {
  print('Got a message whilst in the foreground!');
  print('Message data: ${message.data}');

  if (message.notification != null) {
    print('Message also contained a notification: ${message.notification}');
  }
});

Luồng này chứa một RemoteMessage, nêu chi tiết nhiều thông tin về tải trọng, chẳng hạn như nguồn gốc, mã nhận dạng duy nhất, thời gian gửi, liệu tải trọng có chứa thông báo hay không, v.v. Vì tin nhắn được truy xuất trong khi ứng dụng của bạn đang ở nền trước, nên bạn có thể truy cập trực tiếp vào trạng thái và ngữ cảnh của ứng dụng Flutter.

Tin nhắn trên nền trước và thông báo

Theo mặc định, thông báo đến trong khi ứng dụng đang ở nền trước sẽ không hiển thị thông báo có thể nhìn thấy trên cả Android và iOS. Tuy nhiên, bạn có thể ghi đè hành vi này:

  • Trên Android, bạn phải tạo kênh thông báo "Mức độ ưu tiên cao".
  • Trên iOS, bạn có thể cập nhật các tuỳ chọn trình bày cho ứng dụng.

Tin nhắn trong nền

Quy trình xử lý tin nhắn trong nền khác nhau trên các nền tảng dựa trên Android, Apple và web.

Nền tảng Apple và Android

Xử lý tin nhắn trong nền bằng cách đăng ký trình xử lý onBackgroundMessage. Khi nhận được tin nhắn, một isolate sẽ được tạo (chỉ dành cho Android, iOS/macOS không yêu cầu một isolate riêng biệt) cho phép bạn xử lý tin nhắn ngay cả khi ứng dụng không chạy.

Có một số điều cần lưu ý về trình xử lý tin nhắn trong nền:

  1. Đây không được là một hàm ẩn danh.
  2. Đây phải là một hàm cấp cao nhất (ví dụ: không phải là phương thức lớp yêu cầu khởi chạy).
  3. Khi sử dụng Flutter phiên bản 3.3.0 trở lên, trình xử lý tin nhắn phải được chú thích bằng @pragma('vm:entry-point') ngay phía trên khai báo hàm (nếu không, trình xử lý này có thể bị xoá trong quá trình loại bỏ mã chết cho chế độ phát hành ).
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  // If you're going to use other Firebase services in the background, such as Firestore,
  // make sure you call `initializeApp` before using other Firebase services.
  await Firebase.initializeApp();

  print("Handling a background message: ${message.messageId}");
}

void main() {
  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
  runApp(MyApp());
}

Vì trình xử lý chạy trong isolate riêng bên ngoài ngữ cảnh ứng dụng, nên bạn không thể cập nhật trạng thái ứng dụng hoặc thực thi bất kỳ logic nào ảnh hưởng đến giao diện người dùng. Tuy nhiên, bạn có thể thực hiện logic như yêu cầu HTTP, thực hiện các thao tác IO (ví dụ: cập nhật bộ nhớ cục bộ), giao tiếp với các trình bổ trợ khác, v.v.

Bạn cũng nên hoàn tất logic càng sớm càng tốt. Việc chạy các tác vụ dài và chuyên sâu sẽ ảnh hưởng đến hiệu suất của thiết bị và có thể khiến hệ điều hành chấm dứt quy trình. Nếu các tác vụ chạy lâu hơn 30 giây, thiết bị có thể tự động huỷ quy trình.

Web

Trên web, hãy viết một Trình chạy dịch vụ JavaScript Service Worker chạy trong nền. Sử dụng trình chạy dịch vụ để xử lý tin nhắn trong nền.

Để bắt đầu, hãy tạo một tệp mới trong thư mục web và gọi tệp đó là firebase-messaging-sw.js:

// See this file for the latest firebase-js-sdk version:
// https://github.com/firebase/flutterfire/blob/main/packages/firebase_core/firebase_core_web/lib/src/firebase_sdk_version.dart
importScripts("https://www.gstatic.com/firebasejs/10.7.0/firebase-app-compat.js");
importScripts("https://www.gstatic.com/firebasejs/10.7.0/firebase-messaging-compat.js");

firebase.initializeApp({
  apiKey: "...",
  authDomain: "...",
  databaseURL: "...",
  projectId: "...",
  storageBucket: "...",
  messagingSenderId: "...",
  appId: "...",
});

const messaging = firebase.messaging();

// Optional:
messaging.onBackgroundMessage((message) => {
  console.log("onBackgroundMessage", message);
});

Tệp này phải nhập cả SDK ứng dụng và SDK nhắn tin, khởi chạy Firebase và hiển thị biến messaging.

Tiếp theo, bạn phải đăng ký trình chạy. Trong tệp index.html, hãy đăng ký trình chạy bằng cách sửa đổi thẻ <script> khởi động Flutter:

<script src="flutter_bootstrap.js" async>
  if ('serviceWorker' in navigator) {
    window.addEventListener('load', function () {
      navigator.serviceWorker.register('firebase-messaging-sw.js', {
        scope: '/firebase-cloud-messaging-push-scope',
      });
    });
  }
</script>

Nếu vẫn đang sử dụng hệ thống tạo mẫu cũ, bạn có thể đăng ký trình chạy bằng cách sửa đổi thẻ <script> khởi động Flutter như sau:

<html>
<body>
  <script>
      var serviceWorkerVersion = null;
      var scriptLoaded = false;
      function loadMainDartJs() {
        if (scriptLoaded) {
          return;
        }
        scriptLoaded = true;
        var scriptTag = document.createElement('script');
        scriptTag.src = 'main.dart.js';
        scriptTag.type = 'application/javascript';
        document.body.append(scriptTag);
      }

      if ('serviceWorker' in navigator) {
        // Service workers are supported. Use them.
        window.addEventListener('load', function () {
          // Register Firebase Messaging service worker.
          navigator.serviceWorker.register('firebase-messaging-sw.js', {
            scope: '/firebase-cloud-messaging-push-scope',
          });

          // Wait for registration to finish before dropping the <script> tag.
          // Otherwise, the browser will load the script multiple times,
          // potentially different versions.
          var serviceWorkerUrl =
            'flutter_service_worker.js?v=' + serviceWorkerVersion;

          navigator.serviceWorker.register(serviceWorkerUrl).then((reg) => {
            function waitForActivation(serviceWorker) {
              serviceWorker.addEventListener('statechange', () => {
                if (serviceWorker.state == 'activated') {
                  console.log('Installed new service worker.');
                  loadMainDartJs();
                }
              });
            }
            if (!reg.active && (reg.installing || reg.waiting)) {
              // No active web worker and we have installed or are installing
              // one for the first time. Simply wait for it to activate.
              waitForActivation(reg.installing ?? reg.waiting);
            } else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
              // When the app updates the serviceWorkerVersion changes, so we
              // need to ask the service worker to update.
              console.log('New service worker available.');
              reg.update();
              waitForActivation(reg.installing);
            } else {
              // Existing service worker is still good.
              console.log('Loading app from service worker.');
              loadMainDartJs();
            }
          });

          // If service worker doesn't succeed in a reasonable amount of time,
          // fallback to plaint <script> tag.
          setTimeout(() => {
            if (!scriptLoaded) {
              console.warn(
                'Failed to load app from service worker. Falling back to plain <script> tag.'
              );
              loadMainDartJs();
            }
          }, 4000);
        });
      } else {
        // Service workers not supported. Just drop the <script> tag.
        loadMainDartJs();
      }
  </script>
</body>

Tiếp theo, hãy khởi động lại ứng dụng Flutter. Trình chạy sẽ được đăng ký và mọi tin nhắn trong nền sẽ được xử lý bằng tệp này.

Xử lý hoạt động tương tác

Vì thông báo là một dấu hiệu có thể nhìn thấy, nên người dùng thường tương tác với thông báo (bằng cách nhấn). Hành vi mặc định trên cả Android và iOS là mở ứng dụng. Nếu ứng dụng bị chấm dứt, ứng dụng sẽ được khởi động; nếu ở chế độ nền, ứng dụng sẽ được đưa lên nền trước.

Tuỳ thuộc vào nội dung của thông báo, bạn có thể muốn xử lý hoạt động tương tác của người dùng khi ứng dụng mở. Ví dụ: nếu một tin nhắn trò chuyện mới được gửi bằng thông báo và người dùng nhấn vào thông báo đó, thì bạn có thể muốn mở cuộc trò chuyện cụ thể khi ứng dụng mở.

Gói firebase-messaging cung cấp 2 cách để xử lý hoạt động tương tác này:

  • getInitialMessage(): Nếu ứng dụng được mở từ trạng thái chấm dứt, thì một Future chứa RemoteMessage sẽ được trả về. Sau khi sử dụng, RemoteMessage sẽ bị xoá.
  • onMessageOpenedApp: Một Stream đăng RemoteMessage khi ứng dụng được mở từ trạng thái nền.

Bạn nên xử lý cả hai trường hợp để đảm bảo trải nghiệm người dùng mượt mà. Ví dụ về mã sau đây trình bày cách đạt được điều này:

class Application extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _Application();
}

class _Application extends State<Application> {
  // It is assumed that all messages contain a data field with the key 'type'
  Future<void> setupInteractedMessage() async {
    // Get any messages which caused the application to open from
    // a terminated state.
    RemoteMessage? initialMessage =
        await FirebaseMessaging.instance.getInitialMessage();

    // If the message also contains a data property with a "type" of "chat",
    // navigate to a chat screen
    if (initialMessage != null) {
      _handleMessage(initialMessage);
    }

    // Also handle any interaction when the app is in the background using a
    // Stream listener
    FirebaseMessaging.onMessageOpenedApp.listen(_handleMessage);
  }

  void _handleMessage(RemoteMessage message) {
    if (message.data['type'] == 'chat') {
      Navigator.pushNamed(context, '/chat',
        arguments: ChatArguments(message),
      );
    }
  }

  @override
  void initState() {
    super.initState();

    // Run code required to handle interacted messages in an async function
    // as initState() must not be async
    setupInteractedMessage();
  }

  @override
  Widget build(BuildContext context) {
    return Text("...");
  }
}

Cách bạn xử lý hoạt động tương tác phụ thuộc vào cách thiết lập ứng dụng. Ví dụ trước cho thấy một hình minh hoạ cơ bản bằng StatefulWidget.

Nội địa hoá tin nhắn

Bạn có thể gửi chuỗi đã nội địa hoá theo 2 cách:

  • Lưu trữ ngôn ngữ ưu tiên của từng người dùng trong máy chủ và gửi thông báo tuỳ chỉnh cho từng ngôn ngữ
  • Nhúng chuỗi đã nội địa hoá vào ứng dụng và sử dụng chế độ cài đặt ngôn ngữ tích hợp sẵn của hệ điều hành

Sau đây là cách sử dụng phương thức thứ hai:

Android

  1. Chỉ định tin nhắn bằng ngôn ngữ mặc định trong resources/values/strings.xml:

    <string name="notification_title">Hello world</string>
    <string name="notification_message">This is a message</string>
    
  2. Chỉ định tin nhắn đã dịch trong thư mục values-language. Ví dụ: chỉ định tin nhắn bằng tiếng Pháp trong resources/values-fr/strings.xml:

    <string name="notification_title">Bonjour le monde</string>
    <string name="notification_message">C'est un message</string>
    
  3. Trong tải trọng máy chủ, thay vì sử dụng các khoá title, messagebody, hãy sử dụng title_loc_keybody_loc_key cho tin nhắn đã nội địa hoá, đồng thời đặt các khoá này thành thuộc tính name của tin nhắn mà bạn muốn hiển thị.

    Tải trọng tin nhắn sẽ có dạng như sau:

    {
      "android": {
         "notification": {
           "title_loc_key": "notification_title",
           "body_loc_key": "notification_message"
         }
      }
    }
    

iOS

  1. Chỉ định tin nhắn bằng ngôn ngữ mặc định trong Base.lproj/Localizable.strings:

    "NOTIFICATION_TITLE" = "Hello World";
    "NOTIFICATION_MESSAGE" = "This is a message";
    
  2. Chỉ định tin nhắn đã dịch trong thư mục language.lproj. Ví dụ: chỉ định tin nhắn bằng tiếng Pháp trong fr.lproj/Localizable.strings:

    "NOTIFICATION_TITLE" = "Bonjour le monde";
    "NOTIFICATION_MESSAGE" = "C'est un message";
    

    Tải trọng tin nhắn sẽ có dạng như sau:

    {
      "apns": {
         "payload": {
           "alert": {
             "title-loc-key": "NOTIFICATION_TITLE",
             "loc-key": "NOTIFICATION_MESSAGE"
           }
         }
      }
    }
    

Bật tính năng xuất dữ liệu phân phối tin nhắn

Bạn có thể xuất dữ liệu tin nhắn vào BigQuery để phân tích thêm. BigQuery cho phép bạn phân tích dữ liệu bằng BigQuery SQL, xuất dữ liệu sang một nhà cung cấp dịch vụ đám mây khác hoặc sử dụng dữ liệu cho các mô hình ML tuỳ chỉnh. Hoạt động xuất sang BigQuery bao gồm tất cả dữ liệu có sẵn cho tin nhắn, bất kể loại tin nhắn hoặc liệu tin nhắn có được gửi bằng API hay Trình soạn thông báo hay không.

Để bật tính năng xuất, trước tiên, hãy làm theo các bước trong tài liệu về tính năng xuất dữ liệu BigQuery. Việc bật tính năng này theo phương thức lập trình ở cấp thực thể ứng dụng cho phép bạn yêu cầu người dùng cuối cấp quyền phân tích dữ liệu phân phối tin nhắn của họ (nên dùng). Hãy làm theo các hướng dẫn sau để bật tính năng xuất theo phương thức lập trình:

Android

Bạn có thể sử dụng mã sau:

await FirebaseMessaging.instance.setDeliveryMetricsExportToBigQuery(true);

iOS

Đối với iOS, bạn cần thay đổi AppDelegate.m bằng nội dung sau.

#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"
#import <Firebase/Firebase.h>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [GeneratedPluginRegistrant registerWithRegistry:self];
  // Override point for customization after application launch.
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

- (void)application:(UIApplication *)application
    didReceiveRemoteNotification:(NSDictionary *)userInfo
          fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
  [[FIRMessaging extensionHelper] exportDeliveryMetricsToBigQueryWithMessageInfo:userInfo];
}

@end

Web

Đối với Web, bạn cần thay đổi trình chạy dịch vụ để sử dụng phiên bản 9 của SDK. Bạn cần gói phiên bản 9, vì vậy, bạn cần sử dụng một trình gói như esbuild để trình chạy dịch vụ hoạt động. Hãy xem ứng dụng mẫu để biết cách thực hiện việc này.

Sau khi di chuyển sang SDK phiên bản 9, bạn có thể sử dụng mã sau:

import {
  experimentalSetDeliveryMetricsExportedToBigQueryEnabled,
  getMessaging,
} from 'firebase/messaging/sw';
...

const messaging = getMessaging(app);
experimentalSetDeliveryMetricsExportedToBigQueryEnabled(messaging, true);

Đừng quên chạy yarn build để xuất phiên bản mới của trình chạy dịch vụ sang thư mục web.

Hiển thị hình ảnh trong thông báo trên iOS

Trên các thiết bị Apple, để Thông báo FCM đến hiển thị hình ảnh từ tải trọng FCM, bạn phải thêm một tiện ích dịch vụ thông báo bổ sung và định cấu hình ứng dụng để sử dụng tiện ích đó.

Nếu đang sử dụng tính năng xác thực bằng số điện thoại của Firebase, bạn phải thêm pod Firebase Auth vào Podfile.

Bước 1 – Thêm tiện ích dịch vụ thông báo

  1. Trong Xcode, hãy nhấp vào File > New > Target... (Tệp > Mới > Mục tiêu...)
  2. Một phương thức thức sẽ hiển thị danh sách các mục tiêu có thể; hãy cuộn đến hoặc sử dụng bộ lọc để chọn Notification Service Extension (Tiện ích dịch vụ thông báo). Nhấp vào Next (Tiếp theo).
  3. Thêm tên sản phẩm (sử dụng "ImageNotification" để làm theo hướng dẫn này), chọn Swift hoặc Objective-C, rồi nhấp vào Finish (Hoàn tất).
  4. Bật lược đồ bằng cách nhấp vào Activate (Kích hoạt).

Bước 2 – Thêm mục tiêu vào Podfile

Swift

Đảm bảo tiện ích mới của bạn có quyền truy cập vào gói swift FirebaseMessaging bằng cách thêm tiện ích đó vào mục tiêu Runner:

  1. Từ Trình điều hướng, hãy thêm SDK nền tảng Apple của Firebase: File > Add Package Dependencies...

  2. Tìm kiếm hoặc nhập URL gói: none https://github.com/firebase/firebase-ios-sdk

  3. Thêm vào Dự án Runner: Add Package (Thêm gói)

  4. Chọn FirebaseMessaging và thêm vào mục tiêu ImageNotification: Add Package (Thêm gói)

Objective-C

Đảm bảo tiện ích mới của bạn có quyền truy cập vào pod Firebase/Messaging bằng cách thêm tiện ích đó vào Podfile:

  1. Từ Trình điều hướng, hãy mở Podfile: Pods > Podfile

  2. Chuyển đến cuối tệp và thêm:

    target 'ImageNotification' do
      use_frameworks!
      pod 'Firebase/Auth' # Add this line if you are using FirebaseAuth phone authentication
      pod 'Firebase/Messaging'
    end
    
  3. Cài đặt hoặc cập nhật các pod bằng pod install từ thư mục ios hoặc macos.

Bước 3 – Sử dụng trình trợ giúp tiện ích

Tại thời điểm này, mọi thứ vẫn sẽ chạy bình thường. Bước cuối cùng là gọi trình trợ giúp tiện ích.

Swift

  1. Từ trình điều hướng, hãy chọn tiện ích ImageNotification

  2. Mở tệp NotificationService.swift.

  3. Thay thế nội dung của NotificationService.swift bằng:

    import UserNotifications
    import FirebaseMessaging
    
    class NotificationService: UNNotificationServiceExtension {
    
        var contentHandler: ((UNNotificationContent) -> Void)?
        var bestAttemptContent: UNMutableNotificationContent?
    
        override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
            self.contentHandler = contentHandler
            bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
    
            Messaging.serviceExtension().populateNotificationContent(bestAttemptContent!, withContentHandler: contentHandler)
        }
    
        override func serviceExtensionTimeWillExpire() {
            if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
                contentHandler(bestAttemptContent)
            }
        }
    }
    

Objective-C

  1. Từ trình điều hướng, hãy chọn tiện ích ImageNotification

  2. Mở tệp NotificationService.m.

  3. Ở đầu tệp, hãy nhập FirebaseMessaging.h ngay sau NotificationService.h.

    Thay thế nội dung của NotificationService.m bằng:

    #import "NotificationService.h"
    #import "FirebaseMessaging.h"
    #import <FirebaseAuth/FirebaseAuth-Swift.h> // Add this line if you are using FirebaseAuth phone authentication
    #import <UIKit/UIKit.h> // Add this line if you are using FirebaseAuth phone authentication
    
    @interface NotificationService () <NSURLSessionDelegate>
    
    @property(nonatomic) void (^contentHandler)(UNNotificationContent *contentToDeliver);
    @property(nonatomic) UNMutableNotificationContent *bestAttemptContent;
    
    @end
    
    @implementation NotificationService
    
    /* Uncomment this if you are using Firebase Auth
    - (BOOL)application:(UIApplication *)app
                openURL:(NSURL *)url
                options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
      if ([[FIRAuth auth] canHandleURL:url]) {
        return YES;
      }
      return NO;
    }
    
    - (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts {
      for (UIOpenURLContext *urlContext in URLContexts) {
        [FIRAuth.auth canHandleURL:urlContext.URL];
      }
    }
    */
    
    - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
        self.contentHandler = contentHandler;
        self.bestAttemptContent = [request.content mutableCopy];
    
        // Modify the notification content here...
        [[FIRMessaging extensionHelper] populateNotificationContent:self.bestAttemptContent withContentHandler:contentHandler];
    }
    
    - (void)serviceExtensionTimeWillExpire {
        // Called just before the extension will be terminated by the system.
        // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
        self.contentHandler(self.bestAttemptContent);
    }
    
    @end
    

Bước 4 – Thêm hình ảnh vào tải trọng

Trong tải trọng thông báo, giờ đây, bạn có thể thêm hình ảnh. Để tìm hiểu thêm, hãy xem hướng dẫn cách tạo yêu cầu gửi.