iOS NotificationServiceExtension与Flutter应用交互:推送通知解密问题
一、在NSE中读取Hive存储的解密信息
Hive是Flutter端的本地存储库,NSE作为iOS原生扩展无法直接调用Flutter的Hive API,你需要通过共享App Group容器来让NSE访问Hive相关的存储内容:
给主App和NSE配置相同的App Group
- 在Xcode中分别选中主App target和NSE target,进入
Signing & Capabilities,添加App Groups能力,创建并勾选同一个分组(比如group.com.yourapp.hive)
- 在Xcode中分别选中主App target和NSE target,进入
修改Hive的存储路径到共享容器
在Flutter主App初始化Hive时,将存储目录设置为App Group的共享路径:import 'package:path_provider/path_provider.dart'; import 'package:hive/hive.dart'; Future<void> initHive() async { // 获取App Group共享容器路径 final appGroupDir = await getApplicationSupportDirectory(); final sharedPath = "${appGroupDir.path}/../Group Containers/group.com.yourapp.hive/hive"; Hive.init(sharedPath); // 后续打开Hive box的逻辑保持不变 }在NSE中读取解密所需信息
如果你觉得直接解析Hive的二进制文件太麻烦,可以在Flutter端把解密密钥/关键信息单独存入共享容器的UserDefaults中,NSE直接读取即可:// NSE中的didReceive方法代码 override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) // 访问App Group的UserDefaults let userDefaults = UserDefaults(suiteName: "group.com.yourapp.hive") if let decryptionKey = userDefaults?.string(forKey: "notification_decryption_key") { // 用密钥解密推送数据 if let encryptedData = bestAttemptContent?.userInfo["encrypted_content"] as? String { let decryptedContent = decrypt(encryptedData, key: decryptionKey) // 替换为你的解密逻辑 bestAttemptContent?.body = decryptedContent } } contentHandler(bestAttemptContent ?? request.content) }对应的Flutter端写入逻辑可以通过MethodChannel调用原生代码完成(shared_preferences默认不支持App Group,直接用原生写入更可靠)。
二、替代NSE调用Flutter方法通道的方案
NSE运行在独立后台进程,无法连接主App的Flutter引擎,MethodChannel在这里完全不可用,推荐两种替代思路:
直接在NSE中完成解密和通知修改
也就是上面的方案,把解密依赖的信息放到共享容器,NSE直接读取处理,这是最可靠的方式——NSE生命周期短,依赖主App进程容易导致通知处理延迟或失败。通过主App后台任务处理
如果解密逻辑必须在Flutter端完成,可以给推送通知添加content-available: 1配置(iOS端),触发主App后台唤醒,然后在Flutter的FirebaseMessaging.onBackgroundMessage(若使用Firebase推送)中完成解密,再调用本地通知插件展示处理后的内容。但这种方式依赖系统允许主App后台唤醒,可靠性不如NSE直接处理。
内容的提问来源于stack exchange,提问作者PaulHuang

