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

实现类WhatsApp视频通话通知逻辑,解决PushKit未上报CallKit崩溃问题

iOS VoIP推送+CallKit 未上报来电崩溃问题解决方案

首先明确崩溃的根本原因:iOS 13及以上系统对VoIP类型的PushKit推送做了强制约束,收到推送回调后必须在回调执行结束前向CallKit上报新的来电事件,如果未上报、上报超时、或者上报次数和收到的VoIP推送次数不匹配,系统会直接触发崩溃,这是苹果为了防止开发者滥用VoIP推送做的限制,和你要实现的WhatsApp类来电效果本身不冲突。

核心适配方案

  • 明确收到VoIP推送后的强制处理逻辑:只要触发PKPushRegistry的didReceiveIncomingPushWithPayload回调,不管应用当前是什么状态、这个推送是不是有效来电,都要先向CallKit发起新来电上报请求,再做后续的业务判断
  • 针对无效推送(比如重复来电、用户已经在其他端接听/拒接的来电)的处理逻辑:上报来电到CallKit之后,再立即调用结束通话的接口,系统会自动弹出来电后立即挂断的通知,不会影响用户体验,也满足苹果的上报要求
  • 如果你的应用需要支持免打扰、黑名单、或者用户手动关闭了来电通知的场景:不能直接不上报CallKit,还是要先上报,再根据配置立即结束通话,同时可以配置结束原因为CXCallEndedReasonAnsweredElsewhere或者CXCallEndedReasonDeclinedElsewhere,符合业务逻辑也满足系统要求

类WhatsApp来电效果的实现细节

  • WhatsApp的实现逻辑也是严格遵守了苹果的PushKit+CallKit约束,没有绕过上报的特殊方法:收到VoIP推送后第一时间上报CallKit,系统会弹出原生的来电锁屏/横幅通知,和你要的效果完全一致
  • 视频通话的来电配置:上报CXCallUpdate的时候,把hasVideo属性设为true,系统的CallKit通知会自动显示视频通话的标识,和WhatsApp的样式一致
  • 通话后续状态同步:上报来电成功后再异步和自己的业务服务器校验来电有效性,如果已经失效就立即调结束通话接口,如果有效就等用户操作后再同步状态到服务端

代码示例参考

// 收到VoIP推送后的回调
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion {
    if (![type isEqualToString:PKPushTypeVoIP]) {
        if (completion) completion();
        return;
    }
    // 第一步:生成唯一的通话UUID,先上报CallKit
    NSUUID *callUUID = [NSUUID UUID];
    CXCallUpdate *update = [[CXCallUpdate alloc] init];
    update.remoteHandle = [[CXHandle alloc] initWithType:CXHandleTypePhoneNumber value:payload.dictionaryPayload[@"callerId"]];
    update.hasVideo = [payload.dictionaryPayload[@"isVideo"] boolValue];
    update.supportsDTMF = NO;
    update.supportsHolding = NO;
    update.supportsGrouping = NO;
    update.supportsUngrouping = NO;
    
    [self.callProvider reportNewIncomingCallWithUUID:callUUID update:update completion:^(NSError * _Nullable error) {
        if (error) {
            // 上报失败直接结束通话,避免崩溃
            [self.callController reportCallWithUUID:callUUID endedAtDate:[NSDate date] reason:CXCallEndedReasonFailed];
            if (completion) completion();
            return;
        }
        // 第二步:异步校验来电有效性
        [self checkCallValidWithCallId:payload.dictionaryPayload[@"callId"] completion:^(BOOL isValid) {
            if (!isValid) {
                // 无效来电直接结束
                [self.callController reportCallWithUUID:callUUID endedAtDate:[NSDate date] reason:CXCallEndedReasonDeclinedElsewhere];
            }
            if (completion) completion();
        }];
    }];
}

注意事项

  • 不要尝试用普通推送替代VoIP推送实现来电通知,iOS后台情况下普通推送无法拉起应用唤起CallKit,达不到锁屏下直接弹出来电通知的效果
  • 所有的VoIP推送回调必须调用completion,也必须对应一次CallKit的来电上报,哪怕后续立即结束也不能跳过上报步骤
  • 测试阶段可以用Xcode的Push Notifications调试工具模拟VoIP推送,验证不同场景下的上报逻辑是否正常,避免线上崩溃

内容的提问来源于stack exchange,提问作者Abd Alrahman Alhayek

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 05:39:02