iOS端Linphone集成后,如何在APP关闭时接收SIP来电?
解决iOS后台/锁屏下Linphone来电提醒问题
问题分析
你的代码存在几个关键问题,导致后台/锁屏状态下无法正常触发来电提醒:
- 组件生命周期错误:
PKPushRegistry是局部变量,控制器销毁后会被释放,无法持续监听VoIP推送;且在推送回调中才创建CXProvider,来不及触发系统来电界面。 - 推送Payload格式错误:测试用的curl命令发送的是普通通知格式,不符合VoIP推送要求,VoIP推送不需要
aps.alert包裹,应直接携带自定义来电数据。 - Linphone与CallKit联动缺失:仅处理了CallKit的基础回调,未关联Linphone的实际通话逻辑,无法建立真实通话连接。
修复步骤
1. 全局管理PushRegistry与CallKit Provider
将PKPushRegistry和CXProvider改为全局变量,确保APP在后台时组件依然存活:
import UIKit import CallKit import PushKit class SplashScreen : UIViewController, CXProviderDelegate, PKPushRegistryDelegate { // 改为全局变量,保证生命周期 private var pushRegistry: PKPushRegistry! private var callKitProvider: CXProvider! // ... 原有代码 override func viewDidLoad() { super.viewDidLoad() MyApp.currentUIViewController = self // 初始化VoIP推送监听 pushRegistry = PKPushRegistry(queue: DispatchQueue.main) pushRegistry.delegate = self pushRegistry.desiredPushTypes = [PKPushType.voIP] // 提前初始化CallKit Provider let config = CXProviderConfiguration(localizedName: "My App") config.includesCallsInRecents = true config.supportsVideo = true config.iconTemplateImageData = UIImage(named: "callkit-icon")?.pngData() // 设置来电界面图标 callKitProvider = CXProvider(configuration: config) callKitProvider.setDelegate(self, queue: DispatchQueue.main) } // ... 原有代码 }
2. 修正VoIP推送测试命令
使用符合VoIP标准的Payload格式,去掉aps.alert,直接传递来电信息:
curl -v -d '{"callerId":"1001", "callerName":"张三", "callType":"video"}' --http2 --cert YeniCallKit.pem:202148 https://api.development.push.apple.com/3/device/177e7861396ad0256886673d1efb3c82d5adb54e86dc5cfd781356c1672cfa07
3. 完善推送回调逻辑
解析推送数据,初始化Linphone核心(APP被杀死时需重新初始化),并正确调用CallKit来电上报,最后必须执行completion回调:
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) { MyApp.writeLog(TAG, "收到VoIP推送") // 解析来电信息 let callerId = payload.dictionaryPayload["callerId"] as? String ?? "Unknown" let callerName = payload.dictionaryPayload["callerName"] as? String ?? "Someone" let isVideo = (payload.dictionaryPayload["callType"] as? String ?? "audio") == "video" // 初始化Linphone核心(APP被杀死后重启需要) if !LinphoneManager.shared.isCoreInitialized() { LinphoneManager.shared.initializeCore() } // 上报来电给CallKit let callUUID = UUID() let update = CXCallUpdate() update.remoteHandle = CXHandle(type: .phoneNumber, value: callerId) update.localizedCallerName = callerName update.hasVideo = isVideo callKitProvider.reportNewIncomingCall(with: callUUID, update: update) { error in if let error = error { MyApp.writeLog(TAG, "来电上报失败: \(error.localizedDescription)") } else { // 通知Linphone处理来电逻辑 LinphoneManager.shared.handleIncomingCall(uuid: callUUID, callerId: callerId) } // 必须调用completion,否则系统会限制后续推送 completion() } }
4. 关联CallKit与Linphone通话操作
在CallKit的接听/挂断回调中,调用Linphone的对应方法:
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) { // 通知Linphone接听来电 LinphoneManager.shared.answerCall(uuid: action.callUUID) action.fulfill() MyApp.writeLog(TAG, "接听来电") } func provider(_ provider: CXProvider, perform action: CXEndCallAction) { // 通知Linphone挂断来电 LinphoneManager.shared.endCall(uuid: action.callUUID) action.fulfill() MyApp.writeLog(TAG, "挂断来电") }
5. 确认APP权限配置
- 在
Info.plist中添加UIBackgroundModes,包含voip和audio两项 - 确保APP已申请并获取Push Notifications权限
- 确认签名文件包含Push Notification和VoIP服务权限
内容的提问来源于stack exchange,提问作者SmartPerson
相关产品推荐
相关产品推荐

