2

I can make video calls by using AgoraRtcKit but i cannot send a notification when one device is calling the other.

I have done all the certificates and backgroundmodes things. Here is the viewcontroller's code.

And also i have tried this: Question

I can see the notification when i type the of didReceiveIncomingPushWith in viewDidLoad but cannot see when one device is calling the other. How to make them communicate with each other?

import UIKit
import AgoraRtcKit
import AVFoundation
import CallKit
import PushKit

class VideoCallViewController: UIViewController, CXProviderDelegate, PKPushRegistryDelegate
{
    @IBOutlet weak var localView: RoundedCornerView!
    @IBOutlet weak var remoteView: UIView!

    var apiCaller = ApiCaller()
    var videoCall = VideoCall(uId: String(Int.random(in: 1...100000)), channelName: "xxx")
        
    var agoraKit: AgoraRtcEngineKit?
    
    @IBAction func btnEndCall(_ sender: Any)
    {
        agoraKit?.leaveChannel(nil)
        AgoraRtcEngineKit.destroy()
        self.dismiss(animated: true, completion: nil)
    }
    
    override func viewDidLoad()
    {
        super.viewDidLoad()
        initializeAndJoinChannel()
        
        let registry = PKPushRegistry(queue: DispatchQueue.main)
        registry.delegate = self
        registry.desiredPushTypes = [PKPushType.voIP]
        
    }
    
    func providerDidReset(_ provider: CXProvider) {
    }
    
    func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
        action.fulfill()
    }
    
    func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
        action.fulfill()
    }
    
    
    func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
        print(pushCredentials.token.map { String(format: "%02.2hhx", $0) }.joined())
    }
    
    func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
        
        let config = CXProviderConfiguration()
        config.iconTemplateImageData = UIImage(named: "xxx")!.pngData()
        config.ringtoneSound = "ringtone.caf"
        config.includesCallsInRecents = false;
        config.supportsVideo = true;
        let provider = CXProvider(configuration: config)
        provider.setDelegate(self, queue: DispatchQueue.main)
        let update = CXCallUpdate()
        update.remoteHandle = CXHandle(type: .generic, value: "Person")
        update.hasVideo = true
        provider.reportNewIncomingCall(with: UUID(), update: update, completion: { error in })
                 
    }
    
    override func viewDidDisappear(_ animated: Bool)
    {
        super.viewDidDisappear(animated)
        agoraKit?.leaveChannel(nil)
        AgoraRtcEngineKit.destroy()
    }
    
    @objc func onDidReceiveAlert(_ notification:Notification? = nil) {
        print("Received Notification")
    }
    
    func initializeAndJoinChannel()
    {
        
        apiCaller.videoCallToken(videoCall: videoCall) { [self] rslt in
            switch rslt {
            case .success(let response):
                if response.result.success {
                    
                    agoraKit = AgoraRtcEngineKit.sharedEngine(withAppId: response.appId, delegate: self)
                    
                    DispatchQueue.main.async { [self] in
                        agoraKit?.enableVideo()
                    }
                    
                    let config = CXProviderConfiguration()
                    config.iconTemplateImageData = UIImage(named: "xxx")!.pngData()
                    config.supportsVideo = true;
                    
                    let provider = CXProvider(configuration: config)
                    provider.setDelegate(self, queue: DispatchQueue.main)
                    let controller = CXCallController()
                    let transaction = CXTransaction(action: CXStartCallAction(call: UUID(), handle: CXHandle(type: .generic, value: "Person")))
                    controller.request(transaction, completion: { error in })
                    
                    DispatchQueue.main.asyncAfter(wallDeadline: DispatchWallTime.now() + 5) {
                        provider.reportOutgoingCall(with: controller.callObserver.calls[0].uuid, connectedAt: nil)
                    }
                    
                    let videoCanvas = AgoraRtcVideoCanvas()
                    videoCanvas.uid = UInt(videoCall.uId)!
                    videoCanvas.renderMode = .hidden
                    videoCanvas.view = localView
                    agoraKit?.setupLocalVideo(videoCanvas)
                    agoraKit?.startPreview()
                    
                    let option = AgoraRtcChannelMediaOptions()
                    option.clientRoleType = .broadcaster
                                    
                    agoraKit?.joinChannel(
                        byToken: response.token, channelId: "xxx", uid: UInt(videoCall.uId)!, mediaOptions: option,
                        joinSuccess: { (channel, uid, elapsed) in
                            overlayChange = 1
                        
                        }
                    )
                }
                if response.result.success == false {
                    self.makeAlert(title: "Hata", message: "Bağlanırken hata oluştu!")
                }
            case .failure(_):
                self.makeAlert(title: "Hata", message: "Veriler alınamadı!")
            }
            
        }
    }
}

extension VideoCallViewController: AgoraRtcEngineDelegate
{
    func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int)
    {
        let videoCanvas = AgoraRtcVideoCanvas()
        videoCanvas.uid = uid
        videoCanvas.renderMode = .hidden
        videoCanvas.view = remoteView
        agoraKit?.setupRemoteVideo(videoCanvas)
        

    }
}


1 Answers1

1

I pretty sure you found an answer to your question by now but still for others who still wonder:

  • CallKit is the tool from which we trigger native UI/UX call. by definition, it's not connected to anything whatsoever. It handles the current call state on your device.

  • Agora is a service through which, when two devices are connected to the same channel can share data (frame, audio, message etc...). It could be Twillo or Steam.io or any RTC service.

In order to fully communicate, a third piece is missing => ️

When the first device needs to reach a second one, the second one needs to be informed of it somehow...

How ?

PushKit!

Great but it doesn't solve the communication problem.

You can add PushKit delegate to you code, it still does not trigger any communication to anyone so how ?

Here you need a backend service. To which the first device will send a request to, in order to send a Push notification (a PushKit notification) to the second.

When the second receives the push, he can then:

  • Trigger call kit.
  • Handle the call (and the payload attached to the notification) to navigate to the correct screen.
  • Update the first phone through the same backend, by sending another Pushkit notification that the conversation can start.

It kinda of sad, this kind of information is missing in their documentations.

Hope I made it clearer !

Ruben Mim
  • 136
  • 1
  • 6