You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在iOS UNNotificationServiceExtension中调用Flutter代码

在UNNotificationServiceExtension中触发Flutter代码的可行方案

方案1:App Groups共享状态 + URL Scheme后台唤醒主App

由于UNNotificationServiceExtension无法直接初始化Flutter引擎,我们可以通过共享数据+唤醒主App的方式间接触发Flutter代码:

  1. 配置App Groups
    在Xcode中为主App和通知扩展开启同一个App Group(项目配置 -> Signing & Capabilities -> 添加App Groups,填写统一的group ID,比如group.com.your.app)。

  2. 扩展侧写入任务数据并唤醒主App
    在UNNotificationServiceExtension的didReceive(_:withContentHandler:)方法中,将需要Flutter执行的任务信息写入App Group的UserDefaults,然后通过自定义URL Scheme唤醒主App:

    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        self.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
        
        // 写入任务数据到App Group
        let groupDefaults = UserDefaults(suiteName: "group.com.your.app")
        groupDefaults?.set("需要执行的任务标识/数据", forKey: "PendingFlutterTask")
        groupDefaults?.synchronize()
        
        // 通过URL Scheme唤醒主App
        if let url = URL(string: "your-app-scheme://trigger-flutter-task") {
            UIApplication.shared.open(url, options: [:], completionHandler: nil)
        }
        
        contentHandler(bestAttemptContent ?? request.content)
    }
    
  3. 主App侧接收唤醒并触发Flutter代码
    在主App的AppDelegate(或SceneDelegate)中处理URL Scheme跳转,读取App Group的数据后通过MethodChannel传递给Flutter:

    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
        guard url.scheme == "your-app-scheme", url.host == "trigger-flutter-task" else {
            return false
        }
        
        let groupDefaults = UserDefaults(suiteName: "group.com.your.app")
        guard let taskData = groupDefaults?.string(forKey: "PendingFlutterTask") else {
            return true
        }
        
        // 传递任务给Flutter
        let channel = FlutterMethodChannel(name: "com.your.app/task-channel", binaryMessenger: window?.rootViewController as! FlutterBinaryMessenger)
        channel.invokeMethod("executeTask", arguments: ["data": taskData])
        
        // 清空任务标记
        groupDefaults?.removeObject(forKey: "PendingFlutterTask")
        groupDefaults?.synchronize()
        
        return true
    }
    
  4. Flutter侧接收并执行代码
    在Dart中监听MethodChannel,收到调用后执行对应逻辑:

    final MethodChannel _taskChannel = MethodChannel('com.your.app/task-channel');
    
    @override
    void initState() {
      super.initState();
      _taskChannel.setMethodCallHandler((MethodCall call) async {
        if (call.method == 'executeTask') {
          String taskData = call.arguments['data'];
          // 执行你的Flutter业务逻辑
          _processTask(taskData);
        }
      });
    }
    
    void _processTask(String taskData) {
      // 处理任务的具体代码
    }
    

方案2:iOS 15+ 用BGTaskScheduler调度后台任务

如果你的App只需要支持iOS 15及以上,可以利用BGTaskScheduler让主App在后台执行任务,无需强制唤醒到前台:

  1. 配置后台任务权限
    在主App的Info.plist中添加BGTaskSchedulerPermittedIdentifiers数组,包含你的任务ID(比如com.your.app.flutter-bg-task)。

  2. 扩展侧调度后台任务
    在通知扩展中写入任务数据后,提交后台任务请求:

    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        self.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
        
        let groupDefaults = UserDefaults(suiteName: "group.com.your.app")
        groupDefaults?.set("后台任务数据", forKey: "FlutterBGTaskData")
        groupDefaults?.synchronize()
        
        // 提交后台刷新任务
        let taskRequest = BGAppRefreshTaskRequest(identifier: "com.your.app.flutter-bg-task")
        taskRequest.earliestBeginDate = Date(timeIntervalSinceNow: 1) // 立即执行
        do {
            try BGTaskScheduler.shared.submit(taskRequest)
        } catch {
            print("提交后台任务失败: \(error)")
        }
        
        contentHandler(bestAttemptContent ?? request.content)
    }
    
  3. 主App侧注册并处理后台任务
    在主App的AppDelegate启动时注册任务处理器,收到任务后触发Flutter代码:

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // 注册后台任务
        BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.your.app.flutter-bg-task", using: nil) { task in
            self.handleFlutterBackgroundTask(task: task as! BGAppRefreshTask)
        }
        return true
    }
    
    func handleFlutterBackgroundTask(task: BGAppRefreshTask) {
        let groupDefaults = UserDefaults(suiteName: "group.com.your.app")
        guard let taskData = groupDefaults?.string(forKey: "FlutterBGTaskData") else {
            task.setTaskCompleted(success: false)
            return
        }
        
        let channel = FlutterMethodChannel(name: "com.your.app/bg-task-channel", binaryMessenger: window?.rootViewController as! FlutterBinaryMessenger)
        channel.invokeMethod("runBackgroundTask", arguments: taskData)
        
        groupDefaults?.removeObject(forKey: "FlutterBGTaskData")
        groupDefaults?.synchronize()
        
        task.setTaskCompleted(success: true)
    }
    
  4. Flutter侧处理任务
    同样通过MethodChannel接收任务并执行逻辑,和方案1的Dart代码类似,只需修改对应的Channel名称和方法名。

关键注意事项

  • 两种方案都依赖主App未被用户完全杀死:如果App被强制关闭,URL Scheme会直接打开App到前台,而BGTaskScheduler的任务会无法触发。
  • BGTaskScheduler的任务执行有严格的时间限制(通常30秒内),确保Flutter代码逻辑简洁高效。
  • URL Scheme需要在主App的Info.plist中配置CFBundleURLTypes,并在扩展的Info.plist中添加LSApplicationQueriesSchemes允许调用该Scheme。

内容的提问来源于stack exchange,提问作者DixieFlatline

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 21:12:49