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

iOS:后台/已杀死状态下接收推送打开ViewController失败,求解决方案

推送通知唤醒App后跳转ViewController在后台/杀死状态失效的解决办法

我尝试在收到推送通知时打开指定的ViewController,当前代码在App处于前台时可以正常工作,但在后台或被杀死的状态下无法生效。以下是我的实现代码:

func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) { 
    guard let aps = payload.dictionaryPayload[AnyHashable("aps")] as? NSDictionary else { return } 
    startCustomCallController(apnData: aps) 
} 
func startCustomCallController(apnData: NSDictionary) { 
    let storyBord = UIStoryboard(name: "PrivateUi", bundle: nil) 
    guard let controller = storyBord.instantiateViewController(withIdentifier: "NewIncomingCallViewController") as? NewIncomingCallViewController else{ 
        print("Error instantiating view controller") 
        return 
    } 
    controller.apnData = apnData 
    guard let appdelegate = UIApplication.shared.delegate as? AppDelegate else { 
        print("appdelegate is missing") 
        return 
    } 
    UIView.transition(with: appdelegate.window!, duration: 0.5, options: .transitionCrossDissolve, animations: { 
        appdelegate.window!.rootViewController = controller 
    }, completion: nil) 
}

请问这是不可实现的功能,还是需要进行相关修改?

这当然是可以实现的!你的代码在前台正常工作,但后台/杀死状态下失效,主要是因为这两种状态下App的启动和回调逻辑有差异,加上一些配置和线程处理的问题。下面是具体的修改方案:

1. 确保推送Payload配置正确

从你用PKPushRegistry来看,这应该是VoIP推送(如果是普通远程推送逻辑会不一样)。VoIP推送要唤醒后台/杀死状态的App,Payload必须满足:

  • 包含aps字典,且里面要有content-available: 1
  • 如果你需要弹出通知提醒,还要加上alert字段(比如alert: { "title": "来电", "body": "新的通话请求" })

示例Payload结构:

{
  "aps": {
    "content-available": 1,
    "alert": {
      "title": "来电提醒",
      "body": "有新的通话请求"
    },
    "sound": "default"
  },
  // 你的自定义业务字段
}

2. 处理App被杀死后的启动场景

当App处于杀死状态时,收到VoIP推送会触发App启动,此时pushRegistry:didReceiveIncomingPushWith不会立即调用,而是需要在AppDelegate的application(_:didFinishLaunchingWithOptions:)里先处理启动参数中的推送数据:

在AppDelegate中添加这段逻辑:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // 初始化你的VoIP推送注册(如果还没做的话)
    setupVoIPPushRegistry()
    
    // 检查是否是通过推送启动App
    if let remoteNotification = launchOptions?[.remoteNotification] as? [String: Any],
       let aps = remoteNotification["aps"] as? NSDictionary {
        // 延迟一点执行,确保window已经初始化完成
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            self.startCustomCallController(apnData: aps)
        }
    }
    
    return true
}

3. 后台状态下确保UI操作在主线程

pushRegistry:didReceiveIncomingPushWith的回调可能在后台线程执行,直接操作UI会导致异常或不生效。修改你的startCustomCallController方法,把UI操作包裹在主线程里:

func startCustomCallController(apnData: NSDictionary) {
    DispatchQueue.main.async { // 新增主线程包裹
        let storyBord = UIStoryboard(name: "PrivateUi", bundle: nil)
        guard let controller = storyBord.instantiateViewController(withIdentifier: "NewIncomingCallViewController") as? NewIncomingCallViewController else{
            print("Error instantiating view controller")
            return
        }
        controller.apnData = apnData
        guard let appdelegate = UIApplication.shared.delegate as? AppDelegate else {
            print("appdelegate is missing")
            return
        }
        // 确保window存在
        guard let window = appdelegate.window else {
            print("Window not initialized")
            return
        }
        UIView.transition(with: window, duration: 0.5, options: .transitionCrossDissolve, animations: {
            window.rootViewController = controller
        }, completion: nil)
    }
}

4. 检查后台权限配置

确保你的项目已经开启了VoIP后台模式:

  1. 打开Xcode项目的Signing & Capabilities标签
  2. 添加Background Modes权限
  3. 勾选Voice over IP选项

另外,还要确保用户已经授权了通知权限(包括VoIP推送权限),在App启动时请求对应的权限:

func requestNotificationPermissions() {
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
        if granted {
            DispatchQueue.main.async {
                UIApplication.shared.registerForRemoteNotifications()
            }
        }
    }
}

最后补充

VoIP推送有严格的系统规则:如果你的App处理了VoIP推送但没有展示来电界面(比如CXCallController相关的系统来电UI,或者自定义的来电界面),可能会被系统限制推送权限。所以如果是通话场景,建议结合CallKit框架来实现,这样系统会更稳定地唤醒App并展示来电界面。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 09:04:08