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

Flutter iOS终止状态下静默推送长时间失效问题求助

解决Flutter iOS应用终止30分钟后静默推送失效问题

问题背景

Flutter iOS应用在前台、后台或刚终止(短时间内)时,原生代码可正常处理静默推送;但应用终止超过30分钟后,静默推送完全失效。对比Viber、WhatsApp等应用,即使被划动关闭仍能接收消息和通话,需补充配置以实现类似效果。

用户当前配置与代码:

  • 已开启后台权限:remote notifications、background fetching、voip、background processing
  • 原生Swift代码实现了didReceiveRemoteNotification回调
  • 推送Payload配置见下文

核心原因与解决方案

1. 改用VoIP推送(PushKit)替代普通静默推送

Viber、WhatsApp这类应用依赖VoIP推送(PushKit),而非普通静默推送。VoIP推送优先级远高于普通静默推送,即使应用被彻底终止,系统也会强制唤醒应用处理推送,这是它们能持续接收消息/通话的关键。

普通静默推送受iOS节能机制限制,应用终止超过30分钟后,系统会停止唤醒应用处理这类推送,这是系统层面的限制,无法通过普通配置绕过。

实现步骤:

  • 在Xcode中确保Voice over IP后台模式已开启(你已配置)
  • 引入PushKit框架,实现PKPushRegistryDelegate:
import PushKit

class AppDelegate: UIResponder, UIApplicationDelegate, PKPushRegistryDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // 初始化VoIP推送注册
        let voipRegistry = PKPushRegistry(queue: DispatchQueue.main)
        voipRegistry.delegate = self
        voipRegistry.desiredPushTypes = [.voIP]
        return true
    }

    // 接收VoIP推送回调
    func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
        // 处理推送逻辑,比如本地通知、传递数据到Flutter
        let content = UNMutableNotificationContent()
        content.title = "新消息"
        content.body = payload.dictionaryPayload["data"]?["body"] as? String ?? "未知消息"
        content.sound = UNNotificationSound.default
        
        let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
        UNUserNotificationCenter.current().add(request)
        
        // 传递数据到Flutter
        if let controller = window?.rootViewController as? FlutterViewController {
            let channel = FlutterMethodChannel(name: "notificationHandler", binaryMessenger: controller.binaryMessenger)
            channel.invokeMethod("handleVoipNotification", arguments: payload.dictionaryPayload["data"])
        }
        
        completion() // 必须调用,告知系统处理完成
    }

    // 获取VoIP推送令牌,需上传到服务器
    func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
        let voipToken = pushCredentials.token.map { String(format: "%02x", $0) }.joined()
        print("VoIP Token: \(voipToken)")
        // 将token发送到你的后端服务器
    }
}

2. 修正普通静默推送Payload(若仍需使用)

如果坚持使用普通静默推送,需修正Payload中的错误配置,尽量降低系统限制的影响:

  • 移除空的notification字段,避免被系统识别为普通通知
  • 移除aps.sound配置,静默推送不应触发声音
  • 确保apns-push-type为background,apns-priority为5

修正后的Payload示例:

{
    "data": {
        "title": "You have new message",
        "body": "New message(s) recieved."
    },
    "content_available": true,
    "apns": {
        "payload": {
            "aps": {
                "contentAvailable": true,
                "mutableContent": true
            }
        },
        "headers": {
            "apns-push-type": "background",
            "apns-priority": "5"
        }
    },
    "to": "你的设备令牌"
}

3. 修复原生代码的回调缺陷

你的didReceiveRemoteNotification代码未调用completionHandler,这会导致系统判定后台任务超时,后续会限制应用的唤醒权限。必须在任务完成后调用该方法:

// 在传递数据到Dart的逻辑后添加
notificationChannel.invokeMethod("handleNotification", arguments: nsDataToSend) { result in
    // 根据实际处理结果选择.newData/.noData/.failed
    completionHandler(.newData)
}

4. 其他注意事项

  • 测试VoIP推送时,需使用APNs的VoIP专用通道,Firebase Messaging支持VoIP推送,但需确保配置正确
  • 避免在调试模式下测试长时间终止后的推送行为,调试环境的系统限制与正式环境不同,建议使用Ad Hoc或App Store版本测试
  • 确保用户已授予应用通知权限,包括“允许通知”和相关展示权限

内容的提问来源于stack exchange,提问作者Z.Ihab

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 21:40:59