Compartir vía


Integración con CallKit

En este documento, veremos cómo integrar CallKit con la aplicación iOS.

Requisitos previos

Integración de CallKit (dentro del SDK)

La integración de CallKit en el SDK de iOS de Azure Communication Services controla la interacción con CallKit para nosotros. Para realizar operaciones de llamada como silenciar o desactivar, mantener o reanudar, solo es necesario llamar a la API en el SDK de Azure Communication Services.

Inicialización del agente de llamadas con CallKitOptions

Con la instancia configurada de CallKitOptions, podemos crear con el CallAgent control de CallKit.

let options = CallAgentOptions()
let callKitOptions = CallKitOptions(with: createProviderConfig())
options.callKitOptions = callKitOptions

// Configure the properties of `CallKitOptions` instance here

self.callClient!.createCallAgent(userCredential: userCredential,
    options: options,
    completionHandler: { (callAgent, error) in
    // Initialization
})

Especificar la información del destinatario de la llamada para las llamadas salientes

En primer lugar, es necesario crear una instancia de StartCallOptions() para las llamadas salientes o JoinCallOptions() para la llamada de grupo:

let options = StartCallOptions()

o

let options = JoinCallOptions()

A continuación, cree una instancia de CallKitRemoteInfo

options.callKitRemoteInfo = CallKitRemoteInfo()
  1. Asigne valor para que personalice el nombre para callKitRemoteInfo.displayNameForCallKit mostrar de los destinatarios de la llamada y configure CXHandle el valor. Este valor especificado en displayNameForCallKit es exactamente cómo se muestra en el último registro de llamadas marcados. en el último registro de llamadas marcado.
options.callKitRemoteInfo.displayNameForCallKit = "DISPLAY_NAME"
  1. Asignar el cxHandle valor es lo que recibe la aplicación cuando el usuario vuelve a llamar a ese contacto.
options.callKitRemoteInfo.cxHandle = CXHandle(type: .generic, value: "VALUE_TO_CXHANDLE")

Especificar la información del destinatario de la llamada para las llamadas entrantes

En primer lugar, es necesario crear una instancia de CallKitOptions:

let callKitOptions = CallKitOptions(with: createProviderConfig())

Configure las propiedades de CallKitOptions la instancia:

El SDK llamará al bloque que se pasa a la variable provideRemoteInfo cuando recibamos una llamada entrante y necesitamos obtener un nombre para mostrar para el autor de la llamada entrante, que necesitamos pasar al CallKit.

callKitOptions.provideRemoteInfo = self.provideCallKitRemoteInfo

func provideCallKitRemoteInfo(callerInfo: CallerInfo) -> CallKitRemoteInfo
{
    let callKitRemoteInfo = CallKitRemoteInfo()
    callKitRemoteInfo.displayName = "CALL_TO_PHONENUMBER_BY_APP"      
    callKitRemoteInfo.cxHandle = CXHandle(type: .generic, value: "VALUE_TO_CXHANDLE")
    return callKitRemoteInfo
}

Configuración de la sesión de audio

Se llamará a la sesión de audio antes de colocar o aceptar la llamada entrante y antes de reanudar la llamada después de que se haya puesto en espera.

callKitOptions.configureAudioSession = self.configureAudioSession

public func configureAudioSession() -> Error? {
    let audioSession: AVAudioSession = AVAudioSession.sharedInstance()
    var configError: Error?
    do {
        try audioSession.setCategory(.playAndRecord)
    } catch {
        configError = error
    }
    return configError
}

NOTA: En los casos en los que Contoso ya ha configurado sesiones de audio NO proporcione nil pero devuelva nil el error en el bloque.

callKitOptions.configureAudioSession = self.configureAudioSession

public func configureAudioSession() -> Error? {
    return nil
}

si nil se proporciona para configureAudioSession , el SDK llama a la implementación predeterminada en el SDK.

Control de la carga de notificaciones de inserción entrantes

Cuando la aplicación recibe la carga de notificación de inserción entrante, es necesario llamar handlePush a para procesarla. El SDK de llamadas de Azure Communication Services generará el IncomingCall evento.

public func handlePushNotification(_ pushPayload: PKPushPayload)
{
    let callNotification = PushNotificationInfo.fromDictionary(pushPayload.dictionaryPayload)
    if let agent = self.callAgent {
        agent.handlePush(notification: callNotification) { (error) in }
    }
}

// Event raised by the SDK
public func callAgent(_ callAgent: CallAgent, didRecieveIncomingCall incomingcall: IncomingCall) {
}

Podemos usar reportIncomingCall para controlar las notificaciones push cuando la aplicación está cerrada o de lo contrario.

if let agent = self.callAgent {
  /* App is not in a killed state */
  agent.handlePush(notification: callNotification) { (error) in }
} else {
  /* App is in a killed state */
  CallClient.reportIncomingCall(with: callNotification, callKitOptions: callKitOptions) { (error) in
      if (error == nil) {
          DispatchQueue.global().async {
              self.callClient = CallClient()
              let options = CallAgentOptions()
              let callKitOptions = CallKitOptions(with: createProviderConfig())
              callKitOptions.provideRemoteInfo = self.provideCallKitRemoteInfo
              callKitOptions.configureAudioSession = self.configureAudioSession
              options.callKitOptions = callKitOptions
              self.callClient!.createCallAgent(userCredential: userCredential,
                  options: options,
                  completionHandler: { (callAgent, error) in
                  if (error == nil) {
                      self.callAgent = callAgent
                      self.callAgent!.handlePush(notification: callNotification) { (error) in }
                  }
              })
          }
      } else {
          os_log("SDK couldn't handle push notification", log:self.log)
      }
  }
}

Integración de CallKit (dentro de la aplicación)

Si desea integrar CallKit en la aplicación y no usar la implementación de CallKit en el SDK, consulte el ejemplo de inicio rápido aquí. Pero una de las cosas importantes que se deben cuidar es iniciar el audio en el momento adecuado. Al igual que a continuación

let outgoingAudioOptions = OutgoingAudioOptions()
outgoingAudioOptions.muted = true

let incomingAudioOptions = IncomingAudioOptions()
incomingAudioOptions.muted = true

var copyAcceptCallOptions = AcceptCallOptions()
copyStartCallOptions.outgoingAudioOptions = outgoingAudioOptions
copyStartCallOptions.incomingAudioOptions = incomingAudioOptions

callAgent.startCall(participants: participants,
                    options: copyStartCallOptions,
                    completionHandler: completionBlock)

Silenciar el altavoz y el micrófono garantizan que los dispositivos de audio físicos no se usen hasta que CallKit llame a didActivateAudioSession en CXProviderDelegate. De lo contrario, la llamada puede quitarse o el audio no funcionará. Cuando didActivateAudioSession es cuando se deben iniciar las secuencias de audio.

func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
    Task {
        guard let activeCall = await self.callKitHelper.getActiveCall() else {
            print("No active calls found when activating audio session !!")
            return
        }

        try await startAudio(call: activeCall)
    }
}

func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
    Task {
        guard let activeCall = await self.callKitHelper.getActiveCall() else {
            print("No active calls found when deactivating audio session !!")
            return
        }

        try await stopAudio(call: activeCall)
    }
}

private func stopAudio(call: Call) async throws {
    try await self.callKitHelper.muteCall(callId: call.id, isMuted: true)
    try await call.stopAudio(stream: call.activeOutgoingAudioStream)

    try await call.stopAudio(stream: call.activeIncomingAudioStream)
    try await call.muteIncomingAudio()
}

private func startAudio(call: Call) async throws {
    try await call.startAudio(stream: LocalOutgoingAudioStream())
    try await self.callKitHelper.muteCall(callId: call.id, isMuted: false)

    try await call.startAudio(stream: RemoteIncomingAudioStream())
    try await call.unmuteIncomingAudio()
}
    

Es importante silenciar también el audio saliente antes de detener el audio en casos en los que CallKit no invoca didActivateAudioSession. A continuación, el usuario puede desactivar manualmente el micrófono.

Nota:

En algunos casos CallKit no llama didActivateAudioSession a aunque la aplicación tenga permisos de audio elevados, en ese caso el audio permanecerá silenciado hasta que se reciba la devolución de llamada. Y la interfaz de usuario tiene que reflejar el estado del altavoz y el micrófono. Los participantes remotos de la llamada verán que el usuario también ha silenciado el audio. El usuario tendrá que desactivar manualmente en esos casos.

Pasos siguientes