Partager via


Intégrer à CallKit

Dans ce document, nous allons découvrir comment intégrer CallKit à votre application iOS.

Prerequisites

Intégration de CallKit (dans le SDK)

L’intégration de CallKit dans le Kit de développement logiciel (SDK) iOS Azure Communication Services gère l’interaction avec CallKit pour nous. Pour effectuer des opérations d’appel telles que couper/rétablir le son, mise en attente/reprise, il suffit d'appeler l’API de l’Azure Communication Services SDK.

Initialiser l’agent d’appel avec CallKitOptions

Avec l’instance configurée de CallKitOptions, nous pouvons créer le CallAgent tout en gérant le 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
})

Spécifier les informations du destinataire d’appel pour les appels sortants

Tout d’abord, nous devons créer une instance d’appels StartCallOptions() sortants ou JoinCallOptions() d’appel de groupe :

let options = StartCallOptions()

or

let options = JoinCallOptions()

Ensuite, créez une instance de CallKitRemoteInfo

options.callKitRemoteInfo = CallKitRemoteInfo()
  1. Attribuez une valeur à callKitRemoteInfo.displayNameForCallKit pour personnaliser le nom d'affichage des destinataires d’appel et configurez la valeur de CXHandle. La valeur spécifiée dans displayNameForCallKit est exactement telle qu’elle apparaît dans le dernier journal des appels composés. dans le dernier journal des appels composés.
options.callKitRemoteInfo.displayNameForCallKit = "DISPLAY_NAME"
  1. Attribuez la valeur cxHandle pour que l’application la reçoive quand l’utilisateur rappelle ce contact
options.callKitRemoteInfo.cxHandle = CXHandle(type: .generic, value: "VALUE_TO_CXHANDLE")

Spécifier les informations du destinataire d’appel pour les appels entrants

Tout d’abord, nous devons créer une instance de CallKitOptions:

let callKitOptions = CallKitOptions(with: createProviderConfig())

Configurez les propriétés de l’instance CallKitOptions :

Le bloc passé à la variable provideRemoteInfo sera appelé par le SDK lorsque nous recevrons un appel entrant et que nous devrons obtenir un nom d'affichage pour l'appelant entrant, que nous devons passer à 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
}

Configurer la session audio

La session audio sera configurée avant de placer ou d'accepter un appel entrant et avant de reprendre l'appel après l'avoir mis en attente.

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
}

REMARQUE : Dans les cas où Contoso a déjà configuré des sessions audio, ne fournissez pas nil mais retournez une erreur nil dans le bloc.

callKitOptions.configureAudioSession = self.configureAudioSession

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

si nil est fourni pour configureAudioSession, donc le Kit de développement logiciel (SDK), appelle l’implémentation par défaut dans le SDK.

Gérer la charge utile des notifications Push entrantes.

Lorsque l'application reçoit un payload de notification push entrante, nous devons utiliser handlePush pour le traiter. Le SDK d'appel Azure Communication Services déclenchera l'événement IncomingCall.

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, didReceiveIncomingCall incomingcall: IncomingCall) {
}

Nous pouvons utiliser reportIncomingCall pour gérer les notifications Push lorsque l’application est fermée ou autrement.

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)
      }
  }
}

Intégration de CallKit (dans l’application)

Si vous souhaitez intégrer le CallKit dans l’application et ne pas utiliser l’implémentation CallKit dans le Kit de développement logiciel (SDK), reportez-vous à l’exemple de démarrage rapide ici. Mais l’une des choses importantes à prendre en charge est de démarrer l’audio au bon moment. Comme suit :

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)

Désactiver le haut-parleur et le microphone pour garantir que les périphériques audio physiques ne sont pas utilisés jusqu'à ce que CallKit appelle le didActivateAudioSession sur le CXProviderDelegate. Sinon, l’appel peut être supprimé ou l’audio ne fonctionnera pas. Quand didActivateAudioSession est le moment où les flux audio doivent être démarrés.

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()
}
    

Il est important de couper également le son sortant avant d'arrêter le son, dans les cas où CallKit ne déclenche pas . L’utilisateur peut ensuite activer manuellement le micro.

Note

Dans certains cas, CallKit n’appelle didActivateAudioSession pas même si l’application dispose d’autorisations audio élevées, dans ce cas, l’audio reste muet jusqu’à ce que le rappel soit reçu. Et l’interface utilisateur doit refléter l’état du haut-parleur et du microphone. Le participant/s distant dans l’appel voit également que l’utilisateur a désactivé l’audio. L’utilisateur devra désactiver manuellement le son dans ces cas.

Étapes suivantes