Swift

Receive a call

At the top of the ViewController class, below the client declaration, add a string property to hold a reference to any call in progress.

class ViewController: UIViewController {
    
    let connectionStatusLabel = UILabel()
    let client = VGVoiceClient()
    var callID: String?
    ...
}

When the application receives a call you will want to give the option to accept or reject the call. To do this add the displayIncomingCallAlert function to the ViewController class.

class ViewController: UIViewController {
    ...
    func displayIncomingCallAlert(callID: String, caller: String) {
        let alert = UIAlertController(title: "Incoming call from", message: caller, preferredStyle: .alert)
        
        alert.addAction(UIAlertAction(title: "Answer", style: .default, handler: { _ in
            self.client.answer(callID) { error in
                if error == nil {
                    self.callID = callID
                }
            }
        }))
        
        alert.addAction(UIAlertAction(title: "Reject", style: .destructive, handler: { _ in
            self.client.reject(callID) { error in
                if let error {
                    self.connectionStatusLabel.text = error.localizedDescription
                }
            }
        }))
        
        self.present(alert, animated: true, completion: nil)
    }
}

The displayIncomingCallAlert function takes a call ID and a caller as a parameters. Note in the UIAlertAction for answering the invite, if successful you will assign the callID to the property from earlier

To use displayIncomingCallAlert you need to use the VGVoiceClientDelegate which has a function that will be called when the client receives an incoming VGVoiceInvite.

extension ViewController: VGVoiceClientDelegate {
  ...

  func voiceClient(_ client: VGVoiceClient, didReceiveInviteForCall callId: String, from caller: String, withChannelType type: VGVoiceChannelType) {
      DispatchQueue.main.async { [weak self] in
          self?.displayIncomingCallAlert(callID: callId, caller: caller)
      }
  }
  
  func voiceClient(_ client: VGVoiceClient, didReceiveInviteCancelForCall callId: String, with reason: VGVoiceInviteCancelReason) {
      DispatchQueue.main.async { [weak self] in
          self?.dismiss(animated: true)
      }
  }
  
  func voiceClient(_ client: VGVoiceClient, didReceiveHangupForCall callId: String, withQuality callQuality: VGRTCQuality, reason: VGHangupReason) {
      DispatchQueue.main.async { [weak self] in
          self?.callID = nil
          self?.connectionStatusLabel.text = "Call Ended"
      }
  }
}

You can also implement the didReceiveHangupForCall delegate function which is called if the call is hung up.

NOTE: Also, please make sure that the webhook server you built in the previous steps is still running.

Press Cmd + R to build and run again, when you call the number linked with your application from earlier you will be presented with an alert. You can pick up and the call will be connected!

Incoming call alert

Webhooks

As you proceed with the call, please switch to the terminal and notice the /voice/answer endpoint being called to retrieve the NCCO:

NCCO request:
  - caller: 441234567890

Also, as the call progresses through various stages, /voice/event is being sent events:

EVENT:
{
  headers: {},
  from: '441234567890',
  to: '442038297050',
  uuid: '0779a56d002f1c7f47f82ef5fe84ab79',
  conversation_uuid: 'CON-8f5a100c-fbce-4218-8d4b-16341335bcd6',
  status: 'ringing',
  direction: 'inbound',
  timestamp: '2021-03-29T21:20:05.582Z'
}
---
EVENT:
{
  headers: {},
  from: '441234567890',
  to: '442038297050',
  uuid: '0779a56d002f1c7f47f82ef5fe84ab79',
  conversation_uuid: 'CON-8f5a100c-fbce-4218-8d4b-16341335bcd6',
  status: 'started',
  direction: 'inbound',
  timestamp: '2021-03-29T21:20:05.582Z'
}
---
EVENT:
{
  start_time: null,
  headers: {},
  rate: null,
  from: '441234567890',
  to: '442038297050',
  uuid: '0779a56d002f1c7f47f82ef5fe84ab79',
  conversation_uuid: 'CON-8f5a100c-fbce-4218-8d4b-16341335bcd6',
  status: 'answered',
  direction: 'inbound',
  network: null,
  timestamp: '2021-03-29T21:20:06.182Z'
}
---
EVENT:
{
  from: '441234567890',
  to: 'Alice',
  uuid: '944bf4bf-8dc7-4e23-86b2-2f4234777416',
  conversation_uuid: 'CON-8f5a100c-fbce-4218-8d4b-16341335bcd6',
  status: 'started',
  direction: 'outbound',
  timestamp: '2021-03-29T21:20:13.025Z'
}
---
EVENT:
{
  start_time: null,
  headers: {},
  rate: null,
  from: '441234567890',
  to: 'Alice',
  uuid: '944bf4bf-8dc7-4e23-86b2-2f4234777416',
  conversation_uuid: 'CON-8f5a100c-fbce-4218-8d4b-16341335bcd6',
  status: 'answered',
  direction: 'outbound',
  network: null,
  timestamp: '2021-03-29T21:20:13.025Z'
}
---
EVENT:
{
  headers: {},
  end_time: '2021-03-29T21:20:16.000Z',
  uuid: '944bf4bf-8dc7-4e23-86b2-2f4234777416',
  network: null,
  duration: '5',
  start_time: '2021-03-29T21:20:11.000Z',
  rate: '0.00',
  price: '0',
  from: '441234567890',
  to: 'Alice',
  conversation_uuid: 'CON-8f5a100c-fbce-4218-8d4b-16341335bcd6',
  status: 'completed',
  direction: 'outbound',
  timestamp: '2021-03-29T21:20:17.574Z'
}
---
EVENT:
{
  headers: {},
  end_time: '2021-03-29T21:20:18.000Z',
  uuid: '0779a56d002f1c7f47f82ef5fe84ab79',
  network: 'GB-FIXED',
  duration: '12',
  start_time: '2021-03-29T21:20:06.000Z',
  rate: '0.00720000',
  price: '0.00144000',
  from: ' 441234567890',
  to: '442038297050',
  conversation_uuid: 'CON-8f5a100c-fbce-4218-8d4b-16341335bcd6',
  status: 'completed',
  direction: 'inbound',
  timestamp: '2021-03-29T21:20:17.514Z'
}
---