ios pushkit end last call before reportnewIncommingCall

1.3k Views Asked by At

im stuck in how to ending all the last call before report a new incoming call, my function work for 2 call, it mean i can end the first call before report new call

But the problem is after next report, the end call function throw error

Here is my code:

// Handle incoming pushes
    func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
//        print("Handle incoming pushes: \(payload.dictionaryPayload)")
        
        endCall(thenReportNewCallForUuid: UUID.init())
    }
    
    func reportNewCall(uuid : UUID){
        
        let config = CXProviderConfiguration(localizedName: "CallKitExample")
        config.includesCallsInRecents = true
        config.supportsVideo = true
        config.supportedHandleTypes = [.generic]
        config.iconTemplateImageData = UIImage(named: "logo_square")!.pngData()
        config.maximumCallGroups = 1
        config.maximumCallsPerCallGroup = 1
        
        
        let provider = CXProvider(configuration: config)
        provider.setDelegate(self, queue: nil)
        
        let update = CXCallUpdate()
        update.supportsHolding = false
        update.supportsGrouping = false
        update.supportsUngrouping = false
        update.remoteHandle = CXHandle(type: .generic, value: uuid.uuidString)
        update.hasVideo = true
        provider.reportNewIncomingCall(with: uuid, update: update, completion: { error in
            print("reportNewIncomingCall \(uuid) error: \(error)")
            
            UserDefaults.standard.set(uuid.uuidString, forKey: "CallUUID")
            UserDefaults.standard.synchronize()
        })
    }

func endCall(thenReportNewCallForUuid : UUID) {
        
        guard let lastCallUUIDString = UserDefaults.standard.string(forKey: "CallUUID"), !lastCallUUIDString.isEmpty  else{
            return
        }
        print("end uuid: \(lastCallUUIDString)")
        let call = UUID.init(uuidString: lastCallUUIDString)!
        
        let controller = CXCallController()
        let endTransaction = CXEndCallAction(call: call)
        let transaction = CXTransaction(action: endTransaction)
        controller.request(transaction, completion: { error in
            if let error = error {
                print("endcall Error: \(error)")
                self.reportNewCall(uuid: thenReportNewCallForUuid)
            } else {
                print("endcall Success")
                self.reportNewCall(uuid: thenReportNewCallForUuid)
            }
        })
    }

Here is log + error i got

end uuid: CB91CCC6-7FCD-49D3-BE93-7A6581295B57
endcall Error: Error Domain=com.apple.CallKit.error.requesttransaction Code=2 "(null)" 
-> OK first time endcall error because no call 
reportNewIncomingCall 202DB031-23AE-46B6-91E9-3FBA708E07A7 error: nil


end uuid: 202DB031-23AE-46B6-91E9-3FBA708E07A7
endcall Success -> Matched call to end -> success
reportNewIncomingCall C45FEC0B-1320-4357-ADEF-7B7CA28D96C8 error: nil


end uuid: C45FEC0B-1320-4357-ADEF-7B7CA28D96C8
endcall Error: Error Domain=com.apple.CallKit.error.requesttransaction Code=4 "(null)"
-> Matched call to end -> FAILED
reportNewIncomingCall CBDBA75A-B263-49E5-9138-8D5CCA28ED9E error: nil

Some one who mark duplicate please show the right answer? Thanks

Does someone facing same problem? Please help

2

There are 2 best solutions below

0
Marco On BEST ANSWER

I see at least a couple of issues in your code. But, before that, why are you ending the ongoing call whenever you receive a new incoming call? I'm just curious, because it doesn't seem to be a great user experience.

Anyway, the issues I've found are the following:

  • At every new incoming call you instantiate a new CXProvider. As stated in the documentation:

A VoIP app should create only one instance of CXProvider and store it for use globally.

  • You don't invoke the completion handler of the pushRegistry(_:didReceiveIncomingPushWith:type:completion) method. You should invoke it inside the completion handler of the reportNewIncomingCall(with:update:completion:) method.

I think that the errors you're facing are caused by the CXProvider issue. But if you don't fix also the second issue you could incur in another problem: the system will suppose that you haven't reported a new incoming call and so, after a few calls, it will stop to send you new VoIP pushes (this limitation was first introduced in iOS 13).

0
Radu On

i've had the same issue and fixed it by moving the instantiation of CXProvider, CXProfiderConfiguration and CXCallController outside of pushRegistry function and inside didFinishLaunchingWithOptions part of AppDelegate, something like this:

private func voipRegistration() {
    // Create a push registry object
    let mainQueue = DispatchQueue.main
    let voipRegistry: PKPushRegistry = PKPushRegistry(queue: mainQueue)
    voipRegistry.delegate = self
    voipRegistry.desiredPushTypes = [PKPushType.voIP]
    config.iconTemplateImageData = #imageLiteral(resourceName: "cometchat_white").pngData()
    config.includesCallsInRecents = false
    config.ringtoneSound = "ringtone.caf"
    config.supportsVideo = false
    provider = CXProvider.init(configuration: config)
    provider!.setDelegate(self, queue: nil)
   
}

You have to have a single instance of CXProvider.