Getting issue on accepting video call from VOIP when app is in terminated state

Getting issue on accepting video call from VOIP notification when app is in terminated state/ Force Quit.
When i accept the call from VOIP notification, the video call is getting failed for the first time,
when i call again its getting accepted.

Hi @Android_MR_Firebase , thanks for the report.
I’m guessing that there might be an error with authenticating the user when the first push notification is received. Can you make sure that the user is authenticated before call.accept() is called? It would also help if you could provide the code snippet where you handle the received push notification.

Thanks :slight_smile:

Hi @mininny

i followed this link

i am calling the call.accept() here :

func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
        guard let call = SendBirdCall.getCall(forUUID: action.callUUID),
              SendBirdCall.currentUser != nil else {
            action.fail()
            return
        }
        
        let callOptions = CallOptions(isAudioEnabled: true, isVideoEnabled: call.isVideoCall, useFrontCamera: true)
        let acceptParams = AcceptParams(callOptions: callOptions)
        call.accept(with: acceptParams)
        UIApplication.shared.showCallController(with: call)
        action.fulfill()
    }

Here is the code for handling received VOIP push notification:

import UIKit
import CallKit
import PushKit
import SendBirdCalls


extension AppDelegate: PKPushRegistryDelegate {
    func voipRegistration() {
        self.voipRegistry = PKPushRegistry(queue: DispatchQueue.main)
        self.voipRegistry?.delegate = self
        self.voipRegistry?.desiredPushTypes = [.voIP]
    }
    
    func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
        
        tokenVoip = pushCredentials.token
        UserDefaults.standard.voipPushToken = pushCredentials.token

    }
    
    func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) {
        
        SendBirdCall.pushRegistry(registry, didReceiveIncomingPushWith: payload, for: type, completionHandler: nil)
    }
    
   
    func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
        SendBirdCall.pushRegistry(registry, didReceiveIncomingPushWith: payload, for: type) { uuid in
            guard uuid != nil else {
                let update = CXCallUpdate()
                update.remoteHandle = CXHandle(type: .generic, value: "invalid")
                let randomUUID = UUID()
                
                CXCallManager.shared.reportIncomingCall(with: randomUUID, update: update) { _ in
                    CXCallManager.shared.endCall(for: randomUUID, endedAt: Date(), reason: .acceptFailed)
                }
                completion()
                return
            }
            completion()
        }
    }
}

Here is the didStartRinging function :

func didStartRinging(_ call: DirectCall) {

        call.delegate = self
        
        guard let uuid = call.callUUID else { return }
        guard CXCallManager.shared.shouldProcessCall(for: uuid) else { return }    
      
        print(call.caller)

        let name = call.caller?.nickname ?? "Unknown"

        let update = CXCallUpdate()
        update.remoteHandle = CXHandle(type: .generic, value: name)
        update.hasVideo = call.isVideoCall
        update.localizedCallerName = name
        
        if SendBirdCall.currentUser == nil {

            CXCallManager.shared.reportIncomingCall(with: uuid, update: update) { _ in
 //               CXCallManager.shared.endCall(for: uuid, endedAt: Date(), reason: .acceptFailed)
            }
  //          call.end()
        } 
		else if SendBirdCall.getOngoingCallCount() > 1 {

            CXCallManager.shared.reportIncomingCall(with: uuid, update: update) { _ in

               	CXCallManager.shared.endCall(for: uuid, endedAt: Date(), reason: .declined)

            }
            call.end()
        } 
		else {
            
            CXCallManager.shared.reportIncomingCall(with: uuid, update: update)
        }
    }

Hi @mininny Sharing another video of issue, when app is forced quit or terminated, and device is locked.

Hi @mininny
Any update on this will be appreciated.
The project is held up and we cant go live with this issue coming up.

Hi @Android_MR_Firebase, sorry for the delayed response. It seems like you are not authenticating the user before calling DirectCall.accept(). Can you please refer to the following code? quickstart-calls-directcall-ios/CXCallManager.swift at 650d93bd6e5cdf73c63ea7e3c2412c2734d4635d · sendbird/quickstart-calls-directcall-ios · GitHub

What you should do is call SendBirdCall.authenticate in provider(_ provider: CXProvider, perform action: CXAnswerCallAction) before calling call.accept(with: AcceptParams)

Please let me know if this resolves your issue after trying this. :slight_smile:

Hi @mininny

i tried the above, but still its not working,

the authenticateIfneeded is not available in the version of SendBird i am using (installed via Cocoapods),
now, i am authenticating the user as shared in code snippet, Please let me know if i am doing it right, or i have to use the SendBirdCall.currentUser!.userId at the place of userId in AuthenticateParams as

let authenticateParams = AuthenticateParams(userId: SendBirdCall.currentUser!.userId)

func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
        guard let call = SendBirdCall.getCall(forUUID: action.callUUID),
              SendBirdCall.currentUser != nil else {
            action.fail()
            return
        }

        let userId = self.appDelegate.dictInfo["idToAuth"]
        
        let authenticateParams = AuthenticateParams(userId: userId)
        SendBirdCall.authenticate(with: authenticateParams) { (user, error) in
            guard let user = user, error == nil else { return }

            print("Successfully Authenticated: \(user.userId)")
        }
        
        let callOptions = CallOptions(isAudioEnabled: true, isVideoEnabled: call.isVideoCall, useFrontCamera: true)
        let acceptParams = AcceptParams(callOptions: callOptions)
        call.accept(with: acceptParams)
        UIApplication.shared.showCallController(with: call)
        action.fulfill()
    }
1 Like

Hi @mininny @Miyoung_Han @Miyoung_Han

i have same issue in group call. Direct Call perfect working with Voip.
I wanted to bring to your attention an issue I’ve been experiencing with group calls in SendBird. While direct calls are working perfectly with VoIP, I’ve encountered a problem on the receiver side during group audio and video calls. When all users are invited and the call is received, the didReceiveIncomingPushWith method returns a nil UUID when passed in SendBirdCall.pushRegistry. I’ve also checked the “quickstart-calls-groupcall-ios” GitHub demo, and it seems that the receiver side handling of VOIP calls is not addressed. Any assistance in resolving this issue would be greatly appreciated.

SendBirdCall.pushRegistry(registry, didReceiveIncomingPushWith: payload, for: type)
This function direct_call return uuid perfect but group call return nil

Hello @Dev_iOS, I’m Elliot.
The Invitation feature for GroupCall is different from DirectCall.dial() as it simply push notifies the invitee. When the invitee receives an invitation (push), they should launch the application and use RoomInvitation.accept()/decline() and then call functions like room.enter() for the corresponding RoomInvitation.Room

Hi @Elliot_Choi
Thanks for the replay.

You say it notifies invitees only but if the user has a VOIP token in the call token store how will the invited user get the push notification.

We use direct call and group call in our app.

Using DirectCall working perfet working with VOIP.
i also use invite to another user in group room call use code is
let inviteeId: String = “”
room.sendInvitation(invitee: inviteeId) { invitation, error in
guard error == nil else { return } // An error occurred while sending an invitation.
// Successfully sent an invitation to the invitee.
}

that time not recived notificatio on this method
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) → Void) {
SendBirdCall.application(application, didReceiveRemoteNotification: userInfo)
}

but call this method
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () → Void) { }

because stored VOIP token in call Users → Call → Push Token → iOS in SendBird Dashboard so.

Hi @Dev_iOS

I’m Young, an Engineering Manager of Calls.

I want you to check if what I understand is correct below:
Do you want to use the VoIP service with PushKit right now, and you want to use the group call and get an invitation through the iOS VoIP Push notification?
If I understand correctly, the receiving event of PushKit’s VoIP Push notification should use the event below:

func pushRegistry(_ registry: PKPushRegistry, 
          didReceiveIncomingPushWith payload: PKPushPayload, 
          for type: PKPushType, 
          completion: @escaping () -> Void) { 
}

not this of remote push notification:

func application(
    _ application: UIApplication,
    didReceiveRemoteNotification userInfo: [AnyHashable : Any],
    fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
)

You can find out more by referring to Apple’s iOS PushKit — PushKit | Apple Developer Documentation
And you also can find out more info about Responding to VoIP Notifications from PushKit —
Responding to VoIP Notifications from PushKit | Apple Developer Documentation

Hi @Young_Hwang

Please Can you create one project in Github with manage 1-1 user call & Room Group call handle both side Sender and receiver side so easy to understand all.

Ex:
User List : User A, User B & User C

Flow 1:
User A call to User B (it means 1-1 call) using Push Kit or Call Kit.
its working fine in GitHub - sendbird/quickstart-calls-directcall-ios: iOS sample for Direct Call of Sendbird Calls, guiding you to build a real-time voice and video calls quickly and easily. demo.
we already implemented and it’s perfect working.

Flow 2:
User A, User B & User C in one Group like whatsapp group.

User A make group call and send room invitation to User B & C.
i not understand how to handle group call using Push Kit or Call Kit so i say create one demo for both example in single iOS project.

Thank You

@Young_Hwang @Elliot_Choi
Any update ?
If you make a demo project, share the link here.

Application is waiting to go live due to this issue.

@Dev_iOS

Hello, at the moment we don’t have any plans to release any additional demo applications.

Hi @Tyler
Thank you for your replay.

Then How to manage Direct Call & Group call in single application?
Direct Call → using Push Kit or Call Kit (it’s perfect working)
Group Call → Use Notification

but store VOIP token in call Users → Call → Push Token → iOS so not send notification to other invitee user. i send invitation to other group member that side received didReceiveIncomingPushWith PKPushRegistry
Please help regarding this issue.

Hi Sendbird Team

Please inform when fix group call issue.

Thanks

Hi @Dev_iOS The fix was released in Release Release 1.10.16 · sendbird/sendbird-calls-ios · GitHub. Please try updating the SDK and see if the issue resolves. Thank you :slight_smile:

Hi mininny

Thank you for resolve this issue.