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 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ị.
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ị.
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.
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ị.
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)
}
}
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ờ.
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ự.
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.
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.
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.
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.
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()
}
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.
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.
Để 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.
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ủ 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.
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.
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.
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.
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
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.
Đọc thêm