https://a.storyblok.com/f/270183/75672/e5c89abdd6/ios-callkit_outbound-call.png

Como fazer chamadas de saída usando o CallKit do iOS

Publicado em October 18, 2023

Tempo de leitura: 5 minutos

Neste tutorial, você criará um aplicativo SwiftUI capaz de fazer chamadas telefônicas usando o Vonage Client SDK e o CallKit. O CallKit permite integrar seu aplicativo iOS ao sistema. Ao fazer chamadas, o histórico de chamadas do seu aplicativo ficará disponível no aplicativo Telefone do iOS.

Pré-requisitos

  • Um account da API da Vonage. Se você ainda não tiver um, pode se cadastrar hoje mesmo.

  • Um account de desenvolvedor da Apple e um dispositivo iOS

  • Xcode 12 e Swift 5 ou versão posterior.

  • Cocoapods para instalar o Vonage Client SDK para iOS.

Visão geral

Primeiro, você implantará um servidor para lidar com a criação de um aplicativo Vonage, usuários, geração de JWT e webhooks. Em seguida, você adaptará um aplicativo para iOS que, utilizando o Vonage Client SDK, fará chamadas por meio do CallKit.

Implantação do servidor

Este projeto utilizará o Vonage Cloud Runtime Marketplace para implantar um servidor de Voice do Client SDK na infraestrutura da Vonage. Abra o página do produto “Client SDK Voice Sample Server” . Abra a aba “Implantar código” e, caso ainda não tenha feito login, faça-o agora.

Digite um nome para a implantação e escolha uma região. Em seguida, clique no botão “Implantar código”.

Client SDK Voice Sample Server "Deploy Code" page

Para usar a Voice API, você precisa adquirir e atribuir um número da Vonage ao aplicativo. Clique em “Atribuir um número” para visualizar seus números existentes ou para adquirir um novo. Depois de fazer isso, clique em “Continuar”.

Assign number popup

Assim que a implantação for concluída, clique no botão “Iniciar”. Isso abrirá o aplicativo do servidor no seu navegador, onde você poderá ler mais sobre como o servidor funciona. Para continuar com o tutorial, você precisará criar um usuário. Os usuários são a forma de identificar um aplicativo que executa o Vonage Client SDK com as APIs da Vonage.

Client SDK Voice Sample Server landing page

Crie um usuário chamado “Alice” e gere um JWT. Os SDKs do cliente usam o JWT para autenticação na Vonage. Quando você fizer uma chamada para o número da Vonage que você atribuiu ao aplicativo anteriormente, a Vonage enviará uma solicitação a este servidor, que retornará um Objeto de Controle de Chamada que conectará a chamada recebida à usuária “Alice”.

Generating a JWT for a user on the Client SDK Voice Sample Server

Baixe o projeto inicial

Este tutorial será desenvolvido com base em um projeto já existente. Se você não estiver familiarizado com a realização de chamadas de saída usando o Vonage Client SDK, recomenda-se que você consulte o postagem do blog “Como fazer chamadas telefônicas com o SwiftUI” . Quando estiver pronto, você pode clonar o projeto inicial no GitHub usando este comando no seu terminal:

git clone git@github.com:Vonage-Community/blog-clientsdk-ios_swift-swiftui_app_to_phone.git

Em seguida, acesse a pasta do projeto e instale a VonageClientSDKVoice dependência:

cd blog-clientsdk-ios_swift-swiftui_app_to_phone
pod install

Quando estiver pronto, você poderá abrir o projeto com este comando:

xed .

Atualizando o projeto inicial

Como você vai usar o CallKit para fazer chamadas, pode atualizar a loginIfNeeded função no ContentView.swift arquivo para remover o isUsingCallKit sinalizador. O SDK define esse valor como “true” por padrão. Aqui você também pode colar o JWT da usuária “Alice” que criou anteriormente:

func loginIfNeeded() {
    guard status != "Connected" else { return }
    client.createSession("ey...") { error, sessionId in
        if let error {
            self.updateStatus(error.localizedDescription)
        } else {
            self.updateStatus("Connected")
        }
    }
}

Adicionando o modo de fundo do VoIP

O CallKit exige que você adicione o modo de fundo VoIP. Certifique-se de estar conectado à sua conta de desenvolvedor da Apple no Xcode, por meio das preferências. Se estiver, selecione seu destino e, em seguida, escolha Assinatura e Recursos:

Add capability button in Xcode

Em seguida, selecione o botão “Adicionar recurso” e o recurso “Modos de fundo”. No recurso “Modos de fundo”, selecione Voz sobre IP:

Adding the VoIP background mode in Xcode

Incorporando o CallKit Framework

Vincule a estrutura CallKit ao seu projeto, adicionando-a em Frameworks, Bibliotecas e Conteúdo Incorporado em Geral:

Adding the CallKit framework in Xcode

Criação do Gerenciador de Provedores

Agora, você vai criar uma classe, ProviderManager. O CallKit cuida da comunicação entre seu aplicativo e o sistema por meio do CXProvider. Crie um novo arquivo Swift (CMD + N) chamado ProviderManager:

import CallKit
import Foundation
import VonageClientSDKVoice

protocol ProviderManagerDelegate: AnyObject {
    func callReported(_ providerManager: ProviderManager, callUUID: UUID)
    func providerReset()
}

final class ProviderManager: NSObject {
    private static var providerConfiguration: CXProviderConfiguration = {
        let providerConfiguration = CXProviderConfiguration()
        providerConfiguration.maximumCallsPerCallGroup = 1
        providerConfiguration.supportedHandleTypes = [.generic, .phoneNumber]
        return providerConfiguration
    }()
    
    private let provider = CXProvider(configuration: ProviderManager.providerConfiguration)
    weak var delegate: ProviderManagerDelegate?
    
    override init() {
        super.init()
        provider.setDelegate(self, queue: nil)
    }
    
    public func reportOutgoingCall(callUUID: UUID) {
        provider.reportOutgoingCall(with: callUUID, connectedAt: .now)
    }
    
    public func reportFailedCall(callUUID: UUID) {
        provider.reportCall(with: callUUID, endedAt: .now, reason: .failed)
    }
    
    public func reportEndedCall(callUUID: UUID) {
        provider.reportCall(with: callUUID, endedAt: .now, reason: .remoteEnded)
    }
}

O código acima inclui a criação de um CXProvider e algumas funções auxiliares para informar o status das chamadas ao sistema. A função ProviderManagerDelegate também é definido aqui.

Implementação do CXProviderDelegate

O CXProviderDelegate fornece atualizações do sistema sobre as chamadas. Adicione uma extensão ao ProviderManager que implemente o delegado no mesmo arquivo:

extension ProviderManager: CXProviderDelegate {
    func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
        provider.reportOutgoingCall(with: action.callUUID, startedConnectingAt: .now)
        delegate?.callReported(self, callUUID: action.callUUID)
        action.fulfill()
    }
    
    func providerDidReset(_ provider: CXProvider) {
        delegate?.providerReset()
    }
    
    func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
        VGVoiceClient.enableAudio(audioSession)
    }
    
    func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
        VGVoiceClient.disableAudio(audioSession)
    }
}

Quando um usuário digita um número e inicia uma chamada, o aplicativo solicita ao CallKit que inicie a chamada. Se essa solicitação for bem-sucedida, a CXStartCallAction função delegada será chamada, informando sobre a chamada efetuada. É muito importante observar que VGVoiceClient.enableAudio/VGVoiceClient.disableAudio está sendo chamado aqui com a sessão de áudio fornecida pelo CallKit CXProviderDelegate.

Atualizando o CallModel

Agora que a ProviderManager está configurado, você já pode atualizar a CallModel classe para usá-lo. Abra o ContentView.swift arquivo e substitua as variáveis no início da CallModel classe:

final class CallModel: NSObject, ObservableObject, VGVoiceClientDelegate {

    @Published var status: String = ""
    @Published var isCalling: Bool = false
    private let client = VGVoiceClient()
    var number: String = ""


    private var callId: (vonage: String?, callkit: UUID?)
    private let callController = CXCallController()
    private let providerManager = ProviderManager()
    private let audioSession = AVAudioSession.sharedInstance()

    ...
}

callId agora foi alterado para uma tupla para gerenciar o mapeamento de um ID de chamada da Vonage para um UUID de chamada do CallKit, providerManager foi adicionado, assim como callController. callController é uma instância de CXCallController, que será usada para solicitar ao sistema que inicie uma chamada.

Atualizando o startCall Função

A startCall função agora precisa ser atualizada para solicitar uma chamada por meio da callController quando o usuário clicar na função “iniciar chamada”. Substitua a função existente pela nova:

func startCall() {
    isCalling = true
    let handle = CXHandle(type: .phoneNumber, value: number)
    self.callId.callkit = UUID()
    
    let startCallAction = CXStartCallAction(call: self.callId.callkit!, handle: handle)
    let transaction = CXTransaction(action: startCallAction)
    callController.request(transaction) { _ in }
}

Essa nova função cria um UUID, já que o CallKit precisa de uma forma de identificar a chamada, mas você não receberá o ID da chamada da Vonage até que o SDK realize uma chamada. Em seguida, um CXStartCallAction é usado em uma CXTransaction para solicitar uma chamada. Isso chamará a CXStartCallAction função no CXProviderDelegate que você definiu anteriormente.

Atualizando o endCall Função

Da mesma forma, a endCall função precisa ser atualizada para uso com o CallKit. Substitua a função existente pela nova:

func endCall() {
    client.hangup(callId.vonage!) { error in
        if error == nil {
            if let callkitUUID = self.callId.callkit {
                let transaction = CXTransaction(action: CXEndCallAction(call: callkitUUID))
                self.callController.request(transaction) { _ in }
            }
        }
    }
}

Essa nova função criou uma CXEndCallAction ação, que é utilizada em uma CXTransaction para solicitar o encerramento da chamada. Ao solicitar o encerramento da chamada, o sistema consegue calcular a duração da chamada com precisão. Você também precisa lidar com o caso em que a chamada é encerrada remotamente. Atualize a didReceiveHangupForCall função no VGVoiceClientDelegate delegado:

func voiceClient(_ client: VGVoiceClient, didReceiveHangupForCall callId: VGCallId, withQuality callQuality: VGRTCQuality, reason: VGHangupReason) {
    if let callkitUUID = self.callId.callkit {
        providerManager.reportEndedCall(callUUID: callkitUUID)
    }
    resetState()
}

Mais uma vez, isso garantiu que o término da chamada fosse registrado, por meio do providerManager, ao sistema, para que a duração da chamada possa ser calculada com precisão.

Implementação do ProviderManagerDelegate

Por fim, implemente o ProviderManagerDelegate para que o providerManager possa comunicar as alterações de volta ao CallModel:

extension CallModel: ProviderManagerDelegate {
    func callReported(_ providerManager: ProviderManager, callUUID: UUID) {
        client.serverCall(["to": number]) { error, callId in
            if error == nil {
                providerManager.reportOutgoingCall(callUUID: callUUID)
                self.callId.vonage = callId
            } else {
                providerManager.reportFailedCall(callUUID: callUUID)
            }
        }
    }
    
    func providerReset() {
        resetState()
    }
}

Quando o providerManager tiver reportado a chamada ao sistema com sucesso, essa função delegada cria uma chamada usando o Vonage Client SDK. Novamente, tanto no caso de sucesso quanto no de erro, o providerManager é usado para informar o status da chamada ao sistema. Agora você pode fazer uma chamada no aplicativo executando o projeto do Xcode em um dispositivo físico. Se você verificar as chamadas recentes no aplicativo de telefone do iOS, deverá ver sua chamada e o nome do aplicativo. Se clicar no ícone de informações, também poderá ver a duração da chamada.

Call log from phone app

E agora?

Você pode encontrar o projeto concluído no GitHub. É possível fazer muito mais com o Client SDK e o CallKit; você pode usar o CallKit para chamadas de saída. Saiba mais sobre o Client SDK na Visão geral do Vonage Client SDK e sobre o CallKit em developer.apple.com.

Compartilhar:

https://a.storyblok.com/f/270183/400x400/19c02db2d3/abdul-ajetunmobi.png
Abdul AjetunmobiEx-funcionário da Vonage

Abdul é um Developer Advocate da Vonage. Ele tem experiência profissional na área de produtos de consumo como engenheiro de iOS. Em seu tempo livre, ele gosta de andar de bicicleta, ouvir música e orientar quem está dando os primeiros passos na área de tecnologia