quickblox-ios-sdk icon indicating copy to clipboard operation
quickblox-ios-sdk copied to clipboard

VoIP Push notification not work in background and terminate state.

Open ritesh5553 opened this issue 4 years ago • 23 comments

Hello,

We have performed chatting, audio, video call functionality in our project. we got text message related notification, but we haven't got VoIP notification for audio/video call once the app goes in background and terminate the state. we have used the latest framework for audio/video calls. and also sometimes not subscription not work. So can you guide us to complete this functionality?

Thanks.

ritesh5553 avatar May 11 '20 05:05 ritesh5553

need help with same issue

Navya-ios avatar Sep 23 '20 19:09 Navya-ios

Hello @Navya-ios ,

I have found some way to solve this issue. You can try as below,

-> Have you added PKPushRegistryDelegate method in appdelegate file? and PKPushRegistryDelegate is only in appdelegate file. otherwise you added more than 1 PKPushRegistryDelegate, it will not work.

-> If you will fail to report 2-3 notification, then Apple will block your notification. so you have to first delete your app then reinstall app.

-> If you have added “answerTimeInterval” in didReceiveIncomingPushWith method. then comment it. you must have to incoming call whenever receive VoIP notification.

riteshpatel0 avatar Sep 24 '20 04:09 riteshpatel0

Hello navya,

I have found some way to solve this issue. You can try as below,

-> Have you added PKPushRegistryDelegate method in appdelegate file? and PKPushRegistryDelegate is only in appdelegate file. otherwise you added more than 1 PKPushRegistryDelegate, it will not work.

-> If you will fail to report 2-3 notification, then Apple will block your notification. so you have to first delete your app then reinstall app.

-> If you have added “answerTimeInterval” in didReceiveIncomingPushWith method. then comment it. you must have to incoming call whenever receive VoIP notification.

Yes PKPushRegistryDelegate methods are in App delegate only, subscription is getting success but incoming call was coming.

Navya-ios avatar Sep 24 '20 04:09 Navya-ios

Hello @Navya-ios ,

I have found some way to solve this issue. You can try as below,

-> Have you added PKPushRegistryDelegate method in appdelegate file? and PKPushRegistryDelegate is only in appdelegate file. otherwise you added more than 1 PKPushRegistryDelegate, it will not work.

-> If you will fail to report 2-3 notification, then Apple will block your notification. so you have to first delete your app then reinstall app.

-> If you have added “answerTimeInterval” in didReceiveIncomingPushWith method. then comment it. you must have to incoming call whenever receive VoIP notification.

Yes PKPushRegistryDelegate methods are in App delegate only, subscription is getting success but incoming call was coming.

Navya-ios avatar Sep 24 '20 04:09 Navya-ios

What you are writing in didReceiveIncomingPushWith methods, Can you share here.

riteshpatel0 avatar Sep 24 '20 06:09 riteshpatel0

func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) { print("didReceiveIncomingPushwith payload") let application = UIApplication.shared

    //in case of bad internet we check how long the VOIP Push was delivered for call(1-1)
    //if time delivery is more than “answerTimeInterval” - return
    if type == .voIP,
        payload.dictionaryPayload[UsersConstant.voipEvent] != nil {
        if let timeStampString = payload.dictionaryPayload["timestamp"] as? String,
            let opponentsIDsString = payload.dictionaryPayload["opponentsIDs"] as? String {
            let opponentsIDsArray = opponentsIDsString.components(separatedBy: ",")
            if opponentsIDsArray.count == 2 {
                let formatter = DateFormatter()
                formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
                if let startCallDate = formatter.date(from: timeStampString) {
                    if Date().timeIntervalSince(startCallDate) > QBRTCConfig.answerTimeInterval() {
                        print("[WaitingRoomVC] timeIntervalSinceStartCall > QBRTCConfig.answerTimeInterval")
                        return
                    }
                }
            }
        }
    }
    
    if type == .voIP,
        payload.dictionaryPayload[UsersConstant.voipEvent] != nil,
        application.applicationState == .background {
        var opponentsIDs: [String]? = nil
        var opponentsNumberIDs: [NSNumber] = []
        var opponentsNamesString = "incoming call. Connecting..."
        var sessionID: String? = nil
        var callUUID = UUID()
        var sessionConferenceType = QBRTCConferenceType.audio
        self.isUpdatedPayload = false
        
        if let opponentsIDsString = payload.dictionaryPayload["opponentsIDs"] as? String,
            let allOpponentsNamesString = payload.dictionaryPayload["contactIdentifier"] as? String,
            let sessionIDString = payload.dictionaryPayload["sessionID"] as? String,
            let callUUIDPayload = UUID(uuidString: sessionIDString) {
            self.isUpdatedPayload = true
            self.sessionID = sessionIDString
            sessionID = sessionIDString
            callUUID = callUUIDPayload
            if let conferenceTypeString = payload.dictionaryPayload["conferenceType"] as? String {
                sessionConferenceType = conferenceTypeString == "1" ? QBRTCConferenceType.video : QBRTCConferenceType.audio
            }
            
            let profile = Profile()
            guard profile.isFull == true else {
                return
            }
            let opponentsIDsArray = opponentsIDsString.components(separatedBy: ",")
            
            var opponentsNumberIDsArray = opponentsIDsArray.compactMap({NSNumber(value: Int($0)!)})
            var allOpponentsNamesArray = allOpponentsNamesString.components(separatedBy: ",")
            for i in 0...opponentsNumberIDsArray.count - 1 {
                if opponentsNumberIDsArray[i].uintValue == profile.ID {
                    opponentsNumberIDsArray.remove(at: i)
                    allOpponentsNamesArray.remove(at: i)
                    break
                }
            }
            opponentsNumberIDs = opponentsNumberIDsArray
            opponentsIDs = opponentsNumberIDs.compactMap({ $0.stringValue })
            opponentsNamesString = allOpponentsNamesArray.joined(separator: ", ")
        }
        
        let fetchUsersCompletion = { [weak self] (usersIDs: [String]?) -> Void in
            if let opponentsIDs = usersIDs {
                QBRequest.users(withIDs: opponentsIDs, page: nil, successBlock: { [weak self] (respose, page, users) in
                    if users.isEmpty == false {
                        self?.dataSource.update(users: users)
                    }
                }) { (response) in
                    print("[WaitingRoomVC] error fetch usersWithIDs")
                }
            }
        }
        
        CallKitManager.instance.reportIncomingCall(withUserIDs: opponentsNumberIDs,
                                                   outCallerName: opponentsNamesString,
                                                   session: nil,
                                                   sessionID: sessionID,
                                                   sessionConferenceType: sessionConferenceType,
                                                   uuid: callUUID,
                                                   onAcceptAction: { [weak self] (isAccept) in
                                                    guard let self = self else {
                                                        return
                                                    }
                                                    
                                                    if let session = self.session {
                                                        if isAccept == true {
                                                            self.openCall(withSession: session,
                                                                          uuid: callUUID,
                                                                          sessionConferenceType: sessionConferenceType)
                                                            print("[WaitingRoomVC]  onAcceptAction")
                                                        } else {
                                                            session.rejectCall(["reject": "busy"])
                                                            print("[WaitingRoomVC] endCallAction")
                                                        }
                                                    } else {
                                                        if isAccept == true {
                                                            self.openCall(withSession: nil,
                                                                          uuid: callUUID,
                                                                          sessionConferenceType: sessionConferenceType)
                                                            print("[WaitingRoomVC]  onAcceptAction")
                                                        } else {
                                                            
                                                            print("[WaitingRoomVC] endCallAction")
                                                        }
                                                        self.prepareBackgroundTask()
                                                    }
                                                    completion()
                                                    
            }, completion: { (isOpen) in
                if QBChat.instance.isConnected == false {
                    self.connectToChat { (error) in
                        if error == nil {
                            print("mo error in didreceiveIncoming withpush")
                            fetchUsersCompletion(opponentsIDs)
                        }
                    }
                } else {
                    print("ABChat not connect in didreceiveIncoming withpush")
                    fetchUsersCompletion(opponentsIDs)
                }
                self.prepareBackgroundTask()
                print("[WaitingRoomVC] callKit did presented")
        })
    }
}

Navya-ios avatar Sep 24 '20 07:09 Navya-ios

What you are writing in didReceiveIncomingPushWith methods, Can you share here.

func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) { print("didReceiveIncomingPushwith payload") let application = UIApplication.shared

//in case of bad internet we check how long the VOIP Push was delivered for call(1-1)
//if time delivery is more than “answerTimeInterval” - return
if type == .voIP,
    payload.dictionaryPayload[UsersConstant.voipEvent] != nil {
    if let timeStampString = payload.dictionaryPayload["timestamp"] as? String,
        let opponentsIDsString = payload.dictionaryPayload["opponentsIDs"] as? String {
        let opponentsIDsArray = opponentsIDsString.components(separatedBy: ",")
        if opponentsIDsArray.count == 2 {
            let formatter = DateFormatter()
            formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
            if let startCallDate = formatter.date(from: timeStampString) {
                if Date().timeIntervalSince(startCallDate) > QBRTCConfig.answerTimeInterval() {
                    print("[WaitingRoomVC] timeIntervalSinceStartCall > QBRTCConfig.answerTimeInterval")
                    return
                }
            }
        }
    }
}

if type == .voIP,
    payload.dictionaryPayload[UsersConstant.voipEvent] != nil,
    application.applicationState == .background {
    var opponentsIDs: [String]? = nil
    var opponentsNumberIDs: [NSNumber] = []
    var opponentsNamesString = "incoming call. Connecting..."
    var sessionID: String? = nil
    var callUUID = UUID()
    var sessionConferenceType = QBRTCConferenceType.audio
    self.isUpdatedPayload = false
    
    if let opponentsIDsString = payload.dictionaryPayload["opponentsIDs"] as? String,
        let allOpponentsNamesString = payload.dictionaryPayload["contactIdentifier"] as? String,
        let sessionIDString = payload.dictionaryPayload["sessionID"] as? String,
        let callUUIDPayload = UUID(uuidString: sessionIDString) {
        self.isUpdatedPayload = true
        self.sessionID = sessionIDString
        sessionID = sessionIDString
        callUUID = callUUIDPayload
        if let conferenceTypeString = payload.dictionaryPayload["conferenceType"] as? String {
            sessionConferenceType = conferenceTypeString == "1" ? QBRTCConferenceType.video : QBRTCConferenceType.audio
        }
        
        let profile = Profile()
        guard profile.isFull == true else {
            return
        }
        let opponentsIDsArray = opponentsIDsString.components(separatedBy: ",")
        
        var opponentsNumberIDsArray = opponentsIDsArray.compactMap({NSNumber(value: Int($0)!)})
        var allOpponentsNamesArray = allOpponentsNamesString.components(separatedBy: ",")
        for i in 0...opponentsNumberIDsArray.count - 1 {
            if opponentsNumberIDsArray[i].uintValue == profile.ID {
                opponentsNumberIDsArray.remove(at: i)
                allOpponentsNamesArray.remove(at: i)
                break
            }
        }
        opponentsNumberIDs = opponentsNumberIDsArray
        opponentsIDs = opponentsNumberIDs.compactMap({ $0.stringValue })
        opponentsNamesString = allOpponentsNamesArray.joined(separator: ", ")
    }
    
    let fetchUsersCompletion = { [weak self] (usersIDs: [String]?) -> Void in
        if let opponentsIDs = usersIDs {
            QBRequest.users(withIDs: opponentsIDs, page: nil, successBlock: { [weak self] (respose, page, users) in
                if users.isEmpty == false {
                    self?.dataSource.update(users: users)
                }
            }) { (response) in
                print("[WaitingRoomVC] error fetch usersWithIDs")
            }
        }
    }
    
    CallKitManager.instance.reportIncomingCall(withUserIDs: opponentsNumberIDs,
                                               outCallerName: opponentsNamesString,
                                               session: nil,
                                               sessionID: sessionID,
                                               sessionConferenceType: sessionConferenceType,
                                               uuid: callUUID,
                                               onAcceptAction: { [weak self] (isAccept) in
                                                guard let self = self else {
                                                    return
                                                }
                                                
                                                if let session = self.session {
                                                    if isAccept == true {
                                                        self.openCall(withSession: session,
                                                                      uuid: callUUID,
                                                                      sessionConferenceType: sessionConferenceType)
                                                        print("[WaitingRoomVC]  onAcceptAction")
                                                    } else {
                                                        session.rejectCall(["reject": "busy"])
                                                        print("[WaitingRoomVC] endCallAction")
                                                    }
                                                } else {
                                                    if isAccept == true {
                                                        self.openCall(withSession: nil,
                                                                      uuid: callUUID,
                                                                      sessionConferenceType: sessionConferenceType)
                                                        print("[WaitingRoomVC]  onAcceptAction")
                                                    } else {
                                                        
                                                        print("[WaitingRoomVC] endCallAction")
                                                    }
                                                    self.prepareBackgroundTask()
                                                }
                                                completion()
                                                
        }, completion: { (isOpen) in
            if QBChat.instance.isConnected == false {
                self.connectToChat { (error) in
                    if error == nil {
                        print("mo error in didreceiveIncoming withpush")
                        fetchUsersCompletion(opponentsIDs)
                    }
                }
            } else {
                print("ABChat not connect in didreceiveIncoming withpush")
                fetchUsersCompletion(opponentsIDs)
            }
            self.prepareBackgroundTask()
            print("[WaitingRoomVC] callKit did presented")
    })
}

}

Navya-ios avatar Sep 24 '20 07:09 Navya-ios

@Navya-ios

Comment below section,

if type == .voIP, payload.dictionaryPayload[UsersConstant.voipEvent] != nil { if let timeStampString = payload.dictionaryPayload["timestamp"] as? String, let opponentsIDsString = payload.dictionaryPayload["opponentsIDs"] as? String { let opponentsIDsArray = opponentsIDsString.components(separatedBy: ",") if opponentsIDsArray.count == 2 { let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd HH:mm:ss" if let startCallDate = formatter.date(from: timeStampString) { if Date().timeIntervalSince(startCallDate) > QBRTCConfig.answerTimeInterval() { print("[WaitingRoomVC] timeIntervalSinceStartCall > QBRTCConfig.answerTimeInterval") return } } } } }

and Are you getting notification?

riteshpatel0 avatar Sep 24 '20 08:09 riteshpatel0

@Navya-ios

Comment below section,

if type == .voIP, payload.dictionaryPayload[UsersConstant.voipEvent] != nil { if let timeStampString = payload.dictionaryPayload["timestamp"] as? String, let opponentsIDsString = payload.dictionaryPayload["opponentsIDs"] as? String { let opponentsIDsArray = opponentsIDsString.components(separatedBy: ",") if opponentsIDsArray.count == 2 { let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd HH:mm:ss" if let startCallDate = formatter.date(from: timeStampString) { if Date().timeIntervalSince(startCallDate) > QBRTCConfig.answerTimeInterval() { print("[WaitingRoomVC] timeIntervalSinceStartCall > QBRTCConfig.answerTimeInterval") return } } } } }

and Are you getting notification?

no, im not getting any notification app was in background only

Navya-ios avatar Sep 24 '20 09:09 Navya-ios

@Navya-ios

Comment below section,

if type == .voIP, payload.dictionaryPayload[UsersConstant.voipEvent] != nil { if let timeStampString = payload.dictionaryPayload["timestamp"] as? String, let opponentsIDsString = payload.dictionaryPayload["opponentsIDs"] as? String { let opponentsIDsArray = opponentsIDsString.components(separatedBy: ",") if opponentsIDsArray.count == 2 { let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd HH:mm:ss" if let startCallDate = formatter.date(from: timeStampString) { if Date().timeIntervalSince(startCallDate) > QBRTCConfig.answerTimeInterval() { print("[WaitingRoomVC] timeIntervalSinceStartCall > QBRTCConfig.answerTimeInterval") return } } } } }

and Are you getting notification?

Can you please tell me the step by step procedure to work for background call?

Navya-ios avatar Sep 24 '20 11:09 Navya-ios

@Navya-ios

How many places to call below method, func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType)

didReceiveIncomingPushWith method is call when your app is in foreground? if not, then first delete the app the reinstall the app.

riteshpatel0 avatar Sep 24 '20 11:09 riteshpatel0

@Navya-ios

How many places to call below method, func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType)

didReceiveIncomingPushWith method is call when your app is in foreground? if not, then first delete the app the reinstall the app. func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) this is only in one view controller

and didReceiveIncomingPushWith method is call when your app is in foreground? this method is not calling in foreground

Navya-ios avatar Sep 24 '20 11:09 Navya-ios

Please try to delete the app and reinstall the app. then check it. if this method is not called then your notification is blocked by apple due to not reporting incoming call.

riteshpatel0 avatar Sep 24 '20 12:09 riteshpatel0

Please try to delete the app and reinstall the app. then check it. if this method is not called then your notification is blocked by apple due to not reporting incoming call.

Yes tried but no progress

Navya-ios avatar Sep 24 '20 12:09 Navya-ios

Can you share your chat related files?

riteshpatel0 avatar Sep 24 '20 12:09 riteshpatel0

Can you share your chat related files?

its for video calling

Navya-ios avatar Sep 25 '20 08:09 Navya-ios

Yes

riteshpatel0 avatar Sep 25 '20 09:09 riteshpatel0

Yes

I am getting this crash log if i call in foreground. Any idea or suggestion?

2020-09-25 15:53:49.679 rtc::[Signaling Processor] - Did receive signal: call from: 25150912 2020-09-25 15:53:49.700 rtc::[RTCClient] Initializing SSL... 2020-09-25 15:53:49.702 rtc::Create audio track: RTCMediaStreamTrack: audio audioTrack enabled Live 2020-09-25 15:53:49.702 rtc::[CAPT] Init. 2020-09-25 15:53:49.702 rtc::Create video track: RTCMediaStreamTrack: video videoTrack enabled Live 2020-09-25 15:53:49.703 rtc::initialize - QBRTCRecorder 2020-09-25 15:53:49.704 rtc::[SESS]<d44a536b-21bb-419e-84e1-34e2c24272a3, I:25150912, O:[25157650], T:V> Init. 2020-09-25 15:53:49.705 rtc::[TASK]<ID:13, l:session answer time out> Start. 2020-09-25 15:53:49.705 rtc::[RTCClient] <QBRTCClient: 0x2824ac440> created new [SESS]<d44a536b-21bb-419e-84e1-34e2c24272a3, I:25150912, O:[25157650], T:V> didChange : QBRTCSessionState Could not cast value of type '__NSDictionaryM' (0x1ef977050) to 'NSString' (0x1ef97ff18). 2020-09-25 15:53:52.306573+0530 Vivadox[30613:2395236] Could not cast value of type '__NSDictionaryM' (0x1ef977050) to 'NSString' (0x1ef97ff18).

Navya-ios avatar Sep 25 '20 11:09 Navya-ios

@riteshpatel0 @ritesh5553 @soulfly @dgem

For voIP notification (for call) In admin panel, I am able to get success in subscriptions but not i am not able to get in queue and not receiving any incoming call when app is in background.

Navya-ios avatar Nov 02 '20 12:11 Navya-ios

@Navya-ios

While you are going to subscribe, did you check QBChat service is connected or not?. If QBChat service is not connected then the subscription will not show in the admin panel. and where you are added PKPushRegistryDelegate. if you are not added in appdelegate then VoIP will not work in background and terminate.

riteshpatel0 avatar Nov 03 '20 04:11 riteshpatel0

@riteshpatel0

We are disconnecting chat service when app goes to background state so, in that case we are not getting video call in background And if we don't disconnect chat service in didEnterBackground we are not getting messages when app is in background

How to overcome this issue?

Navya-ios avatar Nov 03 '20 11:11 Navya-ios

@Navya-ios

Can you share VoIP Call functionality related files?

riteshpatel0 avatar Nov 03 '20 11:11 riteshpatel0

Hello,

This is Nikolay from QuickBlox support.

Please let me know if the issue is still relevant.

Also, please update the SDK to the latest version: https://github.com/QuickBlox/quickblox-ios-sdk/releases/tag/2.17.10

Additionally, please check our new samples: https://docs.quickblox.com/docs/code-samples#video-calling-samples

ghost avatar Oct 25 '21 13:10 ghost

@muteKey Could you please describe how offline call push notification should work? According to the docs it is not clear who should send this notification(voip)? Is this done on QB backend or each client implementation should send it?

vitalyiegorov avatar Dec 06 '22 17:12 vitalyiegorov