Expo托管工作流下iOS通知显示图片问题求助
解决方案:Expo SDK 49 EAS托管工作流实现iOS通知图片展示
1. 使用社区封装插件(推荐)
无需手动操作Xcode,借助社区插件即可快速添加Notification Service Extension:
- 安装依赖:
npx expo install expo-notifications @bam.tech/expo-notification-service-extension
- 配置
app.json/app.config.js:
{ "expo": { "plugins": [ [ "@bam.tech/expo-notification-service-extension", { "name": "NotificationService", "appleTeamId": "你的Apple Team ID" } ], "expo-notifications" ] } }
- 调整FCM推送Payload:
必须在apns字段中开启mutable-content,并指定图片URL:
{ "to": "目标设备Token", "notification": { "title": "测试通知", "body": "带图片的通知内容" }, "apns": { "payload": { "aps": { "mutable-content": 1 }, "attachment": "https://example.com/your-image.jpg" } } }
- 构建测试:
eas build --platform ios
构建完成后安装到测试设备,发送符合格式的推送即可看到图片展示。
2. 自定义Config Plugin(按需使用)
如果社区插件不符合需求,可自行编写插件添加Extension:
- 在项目中创建
plugins/notification-service-extension.js,内容如下:
const { withXcodeProject, withDangerousMod } = require('@expo/config-plugins'); const fs = require('fs'); const path = require('path'); module.exports = function withNotificationServiceExtension(config) { config = withXcodeProject(config, async (config) => { // 自动修改Xcode项目添加Extension Target,逻辑可参考Expo官方文档扩展 return config; }); config = withDangerousMod(config, [ 'ios', async (config) => { const extensionPath = path.join(config.modRequest.platformProjectRoot, 'NotificationService'); if (!fs.existsSync(extensionPath)) { fs.mkdirSync(extensionPath); // 写入NotificationService核心逻辑 fs.writeFileSync(path.join(extensionPath, 'NotificationService.swift'), ` import UserNotifications 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) guard let bestAttemptContent = bestAttemptContent else { contentHandler(request.content) return } // 解析并下载图片附件 if let attachmentUrlStr = bestAttemptContent.userInfo["attachment"] as? String, let url = URL(string: attachmentUrlStr) { downloadAttachment(url: url) { attachment in if let attachment = attachment { bestAttemptContent.attachments = [attachment] } contentHandler(bestAttemptContent) } } else { contentHandler(bestAttemptContent) } } override func serviceExtensionTimeWillExpire() { if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { contentHandler(bestAttemptContent) } } private func downloadAttachment(url: URL, completion: @escaping (UNNotificationAttachment?) -> Void) { let task = URLSession.shared.downloadTask(with: url) { tmpUrl, _, error in guard let tmpUrl = tmpUrl, error == nil else { completion(nil) return } let fileManager = FileManager.default let tmpDir = NSTemporaryDirectory() let destUrl = URL(fileURLWithPath: tmpDir).appendingPathComponent(url.lastPathComponent) do { if fileManager.fileExists(atPath: destUrl.path) { try fileManager.removeItem(at: destUrl) } try fileManager.moveItem(at: tmpUrl, to: destUrl) let attachment = try UNNotificationAttachment(identifier: "", url: destUrl) completion(attachment) } catch { completion(nil) } } task.resume() } } `); // 写入Extension的Info.plist fs.writeFileSync(path.join(extensionPath, 'Info.plist'), ` <?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>CFBundleDisplayName</key> <string>NotificationService</string> <key>CFBundleIdentifier</key> <string>$(PRODUCT_BUNDLE_IDENTIFIER).notification-service</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> <key>CFBundleName</key> <string>NotificationService</string> <key>CFBundlePackageType</key> <string>XPC!</string> <key>CFBundleShortVersionString</key> <string>1.0</string> <key>CFBundleVersion</key> <string>1</string> <key>NSExtension</key> <dict> <key>NSExtensionPointIdentifier</key> <string>com.apple.usernotifications.service</string> <key>NSExtensionPrincipalClass</key> <string>$(PRODUCT_MODULE_NAME).NotificationService</string> </dict> </dict> </plist> `); } return config; }, ]); return config; };
- 在
app.json中引入自定义插件:
{ "expo": { "plugins": [ "./plugins/notification-service-extension.js", "expo-notifications" ] } }
- 运行
eas build --platform ios完成构建测试。
内容的提问来源于stack exchange,提问作者bikkeltio
相关产品推荐
相关产品推荐

