Notification Service Extension — cách xử lý thông báo đẩy hoạt động

Tác giả: IT Sectr Đã đăng: 2026-06-16 Thời gian đọc: 9 phút

Notification Service Extension là một phần mở rộng iOS chặn thông báo đẩy ngay sau khi nhận được nhưng trước khi hiển thị cho người dùng. Phần mở rộng có thể giải mã tải trọng được mã hóa, tải xuống và đính kèm tệp phương tiện, đồng thời thay đổi văn bản hoặc tiêu đề thông báo trong thời gian thực. Theo tài liệu dành cho nhà phát triển của Apple (2025), để kích hoạt phần mở rộng, máy chủ phải gửi khóa mutable-content:1 trong các thuộc tính thông báo — đây là điều kiện duy nhất để khởi chạy UNNotificationServiceExtension.

Những điểm chính

  • Notification Service Extension sửa đổi thông báo đẩy trước khi hiển thị cho người dùng trên thiết bị iOS
  • Việc kích hoạt phần mở rộng yêu cầu tham số mutable-content:1 trong tải trọng thông báo từ máy chủ
  • Giao thức chính là UNNotificationServiceExtension với các phương thức didReceive(_:withContentHandler:) và serviceExtensionTimeWillExpire()
  • Phần mở rộng có thể tải xuống tệp đính kèm phương tiện từ mạng và đính kèm chúng vào thông báo qua UNNotificationAttachment
  • Thời gian thực thi có giới hạn — hệ thống phân bổ khoảng 30 giây để xử lý hoàn chỉnh một thông báo

Notification Service Extension là gì

Notification Service Extension là một phần mở rộng ứng dụng trong iOS chặn thông báo đẩy đến ở phía thiết bị và cho phép sửa đổi nội dung của nó trước khi người dùng nhìn thấy. Đây là loại phần mở rộng thông báo duy nhất hoạt động với nội dung thay vì hiển thị.

Sự khác biệt chính so với Notification Content Extension: Service Extension hoạt động trước khi thông báo được hiển thị và có thể thay đổi tiêu đề, nội dung, tệp âm thanh và tệp đính kèm. Content Extension hoạt động sau khi hiển thị và chỉ quản lý trình bày trực quan của thông báo đã hoàn chỉnh. Hai phần mở rộng này có thể hoạt động cùng nhau: Service Extension tải xuống hình ảnh và Content Extension hiển thị nó trong giao diện tùy chỉnh.

Phần mở rộng tự động kích hoạt khi nhận được thông báo đẩy có thuộc tính mutable-content:1 trong từ điển aps. iOS khởi chạy phần mở rộng ở chế độ nền, chuyển UNNotificationRequest gốc cho nó và chờ phiên bản đã sửa đổi để hiển thị.

Xử lý thông báo đẩy nhanh chóng

UNNotificationServiceExtension nhận UNNotificationRequest đầy đủ với nội dung gốc. Phần mở rộng có thể sửa đổi bất kỳ trường nào của UNNotificationContent: title, subtitle, body, userInfo, attachments và sound. Các thay đổi được áp dụng trước khi thông báo được hiển thị.

Các kịch bản sử dụng điển hình

Giải mã nội dung — nếu thông báo đẩy chứa tải trọng được mã hóa, phần mở rộng sẽ giải mã nó trước khi hiển thị. Tải xuống phương tiện — đính kèm hình ảnh hoặc video vào thông báo. Bản địa hóa — điều chỉnh văn bản thông báo theo cài đặt khu vực của thiết bị. Làm giàu dữ liệu — thêm thông tin bổ sung từ bộ nhớ cục bộ hoặc bộ đệm.

Theo Apple, kịch bản phổ biến nhất trong các ứng dụng là tải xuống hình ảnh cho thông báo phương tiện phong phú. Máy chủ gửi URL hình ảnh trong tải trọng, phần mở rộng tải xuống thư mục tạm thời và tạo UNNotificationAttachment, hệ thống sẽ hiển thị trong giao diện tiêu chuẩn hoặc tùy chỉnh.

Sửa đổi văn bản và tiêu đề

Phần mở rộng có thể viết lại hoàn toàn văn bản thông báo, thay thế tiêu đề hoặc thêm phụ đề. Ví dụ: ứng dụng nhắn tin có thể nhận thông báo được mã hóa, giải mã trong phần mở rộng và hiển thị văn bản có thể đọc được. Hoặc ứng dụng tin tức có thể thêm danh mục tin tức vào phụ đề trước khi hiển thị.

swift
override func didReceive(
    _ request: UNNotificationRequest,
    withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
) {
    let content = request.content.mutableCopy()
        as! UNMutableNotificationContent
    if let imageURL = content.userInfo["media-url"]
        as? String {
        downloadAndAttach(imageURL: imageURL,
            content: content,
            handler: contentHandler)
    }
}

Giao thức UNNotificationServiceExtension

UNNotificationServiceExtension là lớp cơ sở mà Service Extension kế thừa. Lớp này định nghĩa hai phương thức vòng đời: didReceive(_:withContentHandler:) — phương thức xử lý chính, và serviceExtensionTimeWillExpire() — trình xử lý thời gian chờ.

Phương thức didReceive:withContentHandler:

didReceive(_:withContentHandler:) được gọi khi nhận được thông báo. Phần mở rộng nhận được UNNotificationRequest và closure contentHandler, phải được gọi với UNMutableNotificationContent đã sửa đổi. Phần mở rộng có nghĩa vụ gọi contentHandler — nếu không, iOS sẽ hiển thị thông báo gốc sau khi hết thời gian chờ.

Quan trọng: phần mở rộng chỉ có thể xử lý một thông báo tại một thời điểm. Nếu nhiều thông báo đến cùng lúc, iOS tạo các phiên bản phần mở rộng riêng biệt cho mỗi thông báo. Không thể sử dụng trạng thái toàn cục để xử lý tuần tự.

Phương thức serviceExtensionTimeWillExpire:

serviceExtensionTimeWillExpire() được hệ thống gọi khi thời gian thực thi còn lại sắp hết. Trong phương thức này, bạn phải ngay lập tức gọi contentHandler với bất kỳ nội dung nào đã sẵn sàng tại thời điểm đó — ngay cả khi tệp phương tiện chưa tải xuống xong. Nếu bạn không gọi contentHandler trong phương thức này, iOS sẽ hiển thị thông báo gốc.

Nên lưu nội dung tối thiểu có thể chấp nhận được trong phương thức này — ví dụ: thông báo có văn bản và tiêu đề nhưng không có hình ảnh mà quá trình tải xuống không hoàn thành kịp thời gian.

Mã hóa và tệp đính kèm phương tiện

UNNotificationAttachment là một đối tượng được tạo bởi phần mở rộng để đính kèm tệp phương tiện vào thông báo. Phần mở rộng tải xuống tệp từ mạng, lưu vào thư mục tạm thời và tạo UNNotificationAttachment với loại nội dung được chỉ định.

Tạo tệp đính kèm từ tệp đã tải xuống

UNNotificationAttachment được tạo bằng trình khởi tạo init(identifier:url:options:). URL phải trỏ đến tệp cục bộ trong thư mục tạm thời mà phần mở rộng có thể truy cập. Sau khi tạo, tệp đính kèm được thêm vào mảng attachments của UNMutableNotificationContent.

Apple khuyến nghị sử dụng URLSession với cấu hình nền để tải xuống — khi sử dụng URLSession tiêu chuẩn, quá trình tải xuống chặn luồng và tiêu tốn thời gian từ giới hạn 30 giây. URLSession nền tiếp tục tải xuống ngay cả khi phần mở rộng kết thúc và kết quả có thể được sử dụng trong lần khởi chạy tiếp theo.

Giải mã tải trọng được mã hóa

Nếu máy chủ gửi thông báo được mã hóa, phần mở rộng phải giải mã tải trọng trước khi gọi contentHandler. Việc giải mã thường bao gồm yêu cầu khóa từ Keychain hoặc App Group, giải mã qua CommonCrypto và thay thế body hoặc userInfo của thông báo. Trong trường hợp lỗi giải mã, bạn nên gọi contentHandler với nội dung gốc — để người dùng ít nhất thấy rằng thông báo đã đến, dù không đọc được.

swift
func downloadAndAttach(
    imageURL: String,
    content: UNMutableNotificationContent,
    handler: @escaping (UNNotificationContent) -> Void
) {
    let task = URLSession.shared.dataTask(with:
        URL(string: imageURL)!) { data, _, _ in
        let url = FileManager.default
            .temporaryDirectory
            .appendingPathComponent("image.jpg")
        try? data?.write(to: url)
        let attachment = try? UNNotificationAttachment(
            identifier: "image", url: url)
        content.attachments = [attachment].compactMap { $0 }
        handler(content)
    }
    task.resume()
}

Thời gian chờ và cơ chế dự phòng

Notification Service Extension hoạt động trong các khung thời gian nghiêm ngặt. iOS phân bổ thời gian thực thi cố định — khoảng 30 giây kể từ khi kích hoạt. Nếu phần mở rộng chưa gọi contentHandler trong thời gian này, hệ thống sẽ buộc kết thúc tiến trình và hiển thị thông báo gốc không thay đổi.

Chiến lược suy giảm dần dần

Nên triển khai dự phòng đa cấp: đầu tiên thử tải xuống phương tiện, nếu thành công gọi contentHandler với nội dung đầy đủ; nếu thất bại gọi contentHandler với văn bản nhưng không có phương tiện; nếu lỗi nghiêm trọng chuyển nội dung gốc. Cách tiếp cận này đảm bảo người dùng luôn thấy thông báo chứ không phải màn hình trống.

Theo Apple, nguyên nhân phổ biến nhất của thời gian chờ là tải xuống tệp phương tiện lớn qua kết nối chậm. Để giảm rủi ro, nên tối ưu hóa kích thước hình ảnh trên máy chủ — gửi bản xem trước lên đến 300 KB thay vì độ phân giải đầy đủ. Hình ảnh kích thước đầy đủ nên được tải xuống khi mở ứng dụng.

Giám sát hiệu suất

Để theo dõi thời gian chờ và lỗi của phần mở rộng, bạn có thể sử dụng os_log để ghi lại thông báo chẩn đoán trong Hệ thống ghi nhật ký hợp nhất. Mặc dù việc ghi nhật ký tệp trực tiếp trong phần mở rộng rất khó, os_log cho phép phân tích hiệu suất qua Console.app trên thiết bị nhà phát triển. Apple khuyến nghị thêm số liệu vào mỗi lần gọi didReceive — thời gian tải xuống, kích thước tệp, kết quả hoạt động.

swift
override func serviceExtensionTimeWillExpire() {
    let fallback = bestEffortContent as?
        UNMutableNotificationContent
        ?? request.content.mutableCopy()
        as! UNMutableNotificationContent
    contentHandler(fallback)
}

Câu hỏi thường gặp

Máy chủ kích hoạt Notification Service Extension như thế nào?

Máy chủ thêm khóa mutable-content:1 vào từ điển aps của thông báo đẩy. Nếu không có tham số này, hệ thống bỏ qua phần mở rộng và hiển thị thông báo tiêu chuẩn.

Có thể sử dụng phần mở rộng mà không có mutable-content không?

Không. mutable-content:1 là điều kiện bắt buộc để kích hoạt Service Extension. Nếu khóa bị thiếu hoặc được đặt thành 0, thông báo sẽ được hiển thị mà không gọi phần mở rộng.

Điều gì xảy ra khi vượt quá giới hạn 30 giây?

iOS buộc kết thúc phần mở rộng và hiển thị thông báo gốc không thay đổi. Để tránh điều này, hãy triển khai serviceExtensionTimeWillExpire() với nội dung tối thiểu có thể chấp nhận được.

Làm cách nào để chuyển khóa mã hóa cho phần mở rộng?

Thông qua App Group (UserDefaults hoặc tệp dùng chung) hoặc Keychain với quyền truy cập chia sẻ giữa ứng dụng và phần mở rộng. Chuyển trực tiếp khóa trong tải trọng thông báo là không an toàn.

Có thể đính kèm bao nhiêu tệp phương tiện trong Service Extension?

Lên đến 4 tệp đính kèm cho mỗi thông báo, mỗi tệp tối đa 50 MB. Tổng kích thước của các tệp đính kèm ảnh hưởng đến thời gian tải xuống — càng nhiều tệp, nguy cơ hết thời gian chờ càng cao.

Tóm tắt

  • Notification Service Extension sửa đổi thông báo đẩy trên thiết bị trước khi hiển thị, thay đổi văn bản, tiêu đề và tệp đính kèm phương tiện
  • Việc kích hoạt yêu cầu khóa mutable-content:1 trong từ điển aps — nếu không có, phần mở rộng sẽ không khởi chạy
  • Giao thức chính UNNotificationServiceExtension định nghĩa các phương thức didReceive và serviceExtensionTimeWillExpire để xử lý
  • Phần mở rộng có thể tải xuống phương tiện từ mạng, giải mã tải trọng và thêm tối đa 4 tệp đính kèm vào thông báo
  • Thời gian thực thi bị giới hạn ở khoảng 30 giây — nếu vượt quá, iOS hiển thị thông báo gốc
  • Nên áp dụng chiến lược suy giảm dần dần với dự phòng đa cấp để tránh thông báo trống

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm