iOS Swift5:APP终止时无法接收带图片的远程推送通知
iOS 终止状态下无法接收带图片的APNs通知问题解决
问题现象
APP处于前台时可正常接收带图片的通知,但完全终止(退出后台)后无法收到通知,已实现Notification Service Extension和Notification Content Extension。
核心原因
- 推送Payload缺少
mutable-content:1:终止状态下系统不会唤醒主APP,必须依赖Notification Service Extension处理富媒体,而只有当payload包含mutable-content:1时,系统才会触发Extension。 - 主APP冗余逻辑无效:AppDelegate中的
didReceiveRemoteNotification和processNotificationContent在终止状态下不会触发,这些创建本地通知的逻辑仅在前台有效,无法覆盖终止场景。 - Extension代码存在疏漏:未处理系统超时场景、未明确指定文件类型,可能导致Extension处理失败后无通知展示。
解决方案
1. 修正推送Payload结构
确保推送的aps字典中包含mutable-content:1,示例如下:
{ "aps": { "alert": { "title": "通知标题", "body": "通知内容" }, "sound": "default", "mutable-content": 1 }, "notificationMediaUrl": "https://your-image-url.com/image.jpg" }
2. 检查Notification Service Extension配置
- 确保Extension的Bundle ID为主APP Bundle ID + 后缀(如
com.yourapp.NotificationService) - 确认Extension的签名配置与主APP一致(使用相同Team ID)
- 检查Extension的
Info.plist配置是否正确:
<key>NSExtension</key> <dict> <key>NSExtensionAttributes</key> <dict> <key>UNNotificationExtensionCategory</key> <string>notificationWithMedia</string> <key>UNNotificationExtensionInitialContentSizeRatio</key> <real>0.5</real> </dict> <key>NSExtensionPointIdentifier</key> <string>com.apple.usernotifications.service</string> <key>NSExtensionPrincipalClass</key> <string>$(PRODUCT_MODULE_NAME).NotificationService</string> </dict>
3. 修正Extension代码(处理超时与文件类型)
import UserNotifications import MobileCoreServices import UniformTypeIdentifiers class NotificationService: UNNotificationServiceExtension { var contentHandler: ((UNNotificationContent) -> Void)? var bestAttemptContent: UNMutableNotificationContent? override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler guard let bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) else { contentHandler(request.content) return } guard let mediaUrlString = bestAttemptContent.userInfo["notificationMediaUrl"] as? String, let mediaUrl = URL(string: mediaUrlString) else { contentHandler(bestAttemptContent) return } downloadImage(from: mediaUrl) { [weak self] attachment in guard let self = self else { return } if let attachment = attachment { bestAttemptContent.attachments = [attachment] } contentHandler(bestAttemptContent) } } // 处理系统超时场景,避免无通知展示 override func serviceExtensionTimeWillExpire() { if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { contentHandler(bestAttemptContent) } } private func downloadImage(from url: URL, completion: @escaping (UNNotificationAttachment?) -> Void) { URLSession.shared.dataTask(with: url) { data, response, error in guard let data = data, error == nil else { completion(nil) return } let temporaryDirectory = FileManager.default.temporaryDirectory let fileURL = temporaryDirectory.appendingPathComponent("\(UUID().uuidString).jpg") do { try data.write(to: fileURL) // 明确指定文件类型,避免系统识别错误 let options: [UNNotificationAttachmentOptionsKey: Any] = [ .typeHint: UTType.jpeg.identifier ] let attachment = try UNNotificationAttachment(identifier: UUID().uuidString, url: fileURL, options: options) completion(attachment) } catch { print("创建图片附件失败: \(error)") completion(nil) } }.resume() } }
4. 精简主APP逻辑(移除冗余代码)
删除AppDelegate中didReceiveRemoteNotification里创建本地通知的逻辑,同时简化willPresent方法:
@available(iOS 10.0, *) func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { // 直接展示通知,Extension已处理图片 completionHandler([.alert, .sound, .badge]) }
测试步骤
- 完全退出APP(从后台滑动删除)
- 发送包含
mutable-content:1的推送 - 查看Xcode控制台,确认Extension的
didReceive方法被调用
内容的提问来源于stack exchange,提问作者Sarbjyot Singh
相关产品推荐
相关产品推荐

