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

Swift 5中Agora视频通话集成CallKit与PushKit推送问题排查

问题诊断与解决方案

核心问题梳理

你的代码存在几个关键问题,导致实际呼叫时无法触发通知:

  • PKPushRegistry是viewDidLoad内的局部变量,函数执行完后会被ARC释放,无法持续监听voIP推送。
  • CXProvider在每次收到推送时重新实例化,没有全局持有,可能导致CallKit通知无法正常显示。
  • 缺少服务器端推送触发逻辑:Agora仅负责音视频通话建立,设备间的呼叫通知需要通过APNs(voIP推送)由你的服务器主动发送给目标设备。
  • 未正确处理推送的completion回调,可能被系统判定为推送处理失败,影响后续推送。

分步修复方案

1. 全局持有关键实例

将PKPushRegistry和CXProvider改为类的全局属性,确保生命周期与ViewController一致:

class VideoCallViewController: UIViewController, CXProviderDelegate, PKPushRegistryDelegate {
    // 新增全局属性
    private var pushRegistry: PKPushRegistry!
    private var callProvider: CXProvider!
    private var currentCallUUID: UUID? // 保存当前呼叫的UUID

    // ... 其他原有代码
}

2. 初始化Push Registry和Call Provider

在viewDidLoad中初始化这两个实例,而非局部创建:

override func viewDidLoad() {
    super.viewDidLoad()
    initializeAndJoinChannel()
    setupPushRegistry()
    setupCallProvider()
}

private func setupPushRegistry() {
    pushRegistry = PKPushRegistry(queue: DispatchQueue.main)
    pushRegistry.delegate = self
    pushRegistry.desiredPushTypes = [PKPushType.voIP]
}

private func setupCallProvider() {
    let config = CXProviderConfiguration()
    config.iconTemplateImageData = UIImage(named: "xxx")!.pngData()
    config.ringtoneSound = "ringtone.caf"
    config.includesCallsInRecents = false
    config.supportsVideo = true
    
    callProvider = CXProvider(configuration: config)
    callProvider.setDelegate(self, queue: DispatchQueue.main)
}

3. 修复推送接收方法

使用全局的callProvider,并正确调用completion回调:

func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
    // 从payload中解析呼叫信息(比如对方ID、频道名等)
    let callerName = payload.dictionaryPayload["caller_name"] as? String ?? "Unknown"
    let channelId = payload.dictionaryPayload["channel_id"] as? String ?? ""
    
    let update = CXCallUpdate()
    update.remoteHandle = CXHandle(type: .generic, value: callerName)
    update.hasVideo = true
    
    currentCallUUID = UUID()
    callProvider.reportNewIncomingCall(with: currentCallUUID!, update: update) { [weak self] error in
        if let error = error {
            print("Failed to report incoming call: \(error.localizedDescription)")
        }
        // 必须调用completion,告诉系统推送已处理
        completion()
    }
}

4. 补充服务器端推送逻辑

当发起呼叫时,你的服务器需要:

  • 收集并保存目标设备的voIP token(你已在didUpdate pushCredentials中打印token,需将其上传至服务器)
  • 向苹果voIP推送服务器发送APNs请求,携带呼叫相关payload(比如频道ID、呼叫者信息)

示例APNs请求payload结构:

{
    "aps": {
        "alert": "有人呼叫你",
        "sound": "ringtone.caf",
        "content-available": 1
    },
    "channel_id": "你的Agora频道名",
    "caller_name": "呼叫者名称",
    "caller_uid": "呼叫者UID"
}

5. 修复CallKit呼出逻辑

保存呼出的UUID,避免后续调用出错:

func initializeAndJoinChannel() {
    apiCaller.videoCallToken(videoCall: videoCall) { [self] rslt in
        switch rslt {
        case .success(let response):
            if response.result.success {
                // ... 原有Agora初始化代码
                
                // 修复呼出逻辑
                currentCallUUID = UUID()
                let startCallAction = CXStartCallAction(call: currentCallUUID!, handle: CXHandle(type: .generic, value: "Person"))
                let transaction = CXTransaction(action: startCallAction)
                let controller = CXCallController()
                controller.request(transaction) { error in
                    if let error = error {
                        print("Failed to start call: \(error.localizedDescription)")
                    }
                }
                
                DispatchQueue.main.asyncAfter(wallDeadline: DispatchWallTime.now() + 5) {
                    if let uuid = self.currentCallUUID {
                        self.callProvider.reportOutgoingCall(with: uuid, connectedAt: nil)
                    }
                }
                
                // ... 原有加入频道代码
            }
            // ... 错误处理代码
        case .failure(_):
            self.makeAlert(title: "Hata", message: "Veriler alınamadı!")
        }
    }
}

6. 验证后台配置

再次确认以下配置:

  • 开启Background Modes中的Voice over IP和Remote notifications
  • 确保voIP推送证书已正确配置,服务器使用对应环境的证书(开发/生产)
  • 测试必须使用真实设备,模拟器无法接收voIP推送

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 05:55:18