Integrera med CallKit

I det här dokumentet går vi igenom hur du integrerar CallKit med ditt iOS-program.

Förutsättningar

CallKit-integrering (inom SDK)

CallKit-integrering i Azure Communication Services iOS SDK hanterar interaktion med CallKit åt oss. För att utföra alla anropsåtgärder som att stänga av/slå på ljudet, hålla/återuppta behöver vi bara anropa API:et i Azure Communication Services SDK.

Initiera samtalsagenten med CallKitOptions

Med konfigurerad instans av CallKitOptionskan vi skapa CallAgent med hanteringen av 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
})

Ange information om samtalsmottagare för utgående samtal

Först måste vi skapa en instans av StartCallOptions() för utgående anrop eller JoinCallOptions() för gruppsamtal:

let options = StartCallOptions()

eller

let options = JoinCallOptions()

Skapa sedan en instans av CallKitRemoteInfo

options.callKitRemoteInfo = CallKitRemoteInfo()
  1. Tilldela värde för för callKitRemoteInfo.displayNameForCallKit att anpassa visningsnamnet för samtalsmottagare och konfigurera CXHandle värdet. Det här värdet som anges i displayNameForCallKit är exakt hur det visas i den senast uppringda samtalsloggen. i den senast uppringda samtalsloggen.
options.callKitRemoteInfo.displayNameForCallKit = "DISPLAY_NAME"
  1. Tilldela värdet cxHandle är vad programmet tar emot när användaren anropar den kontakten igen
options.callKitRemoteInfo.cxHandle = CXHandle(type: .generic, value: "VALUE_TO_CXHANDLE")

Ange information om samtalsmottagare för inkommande samtal

Först måste vi skapa en instans av CallKitOptions:

let callKitOptions = CallKitOptions(with: createProviderConfig())

Konfigurera egenskaperna CallKitOptions för instansen:

Blockera som skickas till variabeln provideRemoteInfo anropas av SDK när vi tar emot ett inkommande samtal och vi måste få ett visningsnamn för den inkommande anroparen, som vi måste skicka till 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
}

Konfigurera ljudsession

Konfigurera ljudsession anropas innan inkommande samtal skickas eller godkänns och innan samtalet återupptas efter att det har spärrats.

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
}

OBS! I de fall där Contoso redan har konfigurerat ljudsessioner ska du INTE ange nil men returnera nil fel i blocket

callKitOptions.configureAudioSession = self.configureAudioSession

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

om nil anges för configureAudioSession anropar SDK standardimplementeringen i SDK.

Hantera nyttolasten för inkommande push-meddelanden

När appen tar emot nyttolasten för inkommande push-meddelanden måste vi anropa handlePush för att bearbeta den. Azure Communication Services Calling SDK genererar IncomingCall händelsen.

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

Vi kan använda reportIncomingCall för att hantera push-meddelanden när appen stängs eller på annat sätt.

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

CallKit-integrering (inom appen)

Om du vill integrera CallKit i appen och inte använda CallKit-implementeringen i SDK kan du läsa snabbstartsexemplet här. Men en av de viktiga sakerna att ta hand om är att starta ljudet vid rätt tidpunkt. Som att följa

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)

Genom att stänga av högtalare och mikrofon ser du till att fysiska ljudenheter inte används förrän CallKit anropar på didActivateAudioSessionCXProviderDelegate. Annars kan samtalet tas bort eller så fungerar inte ljudet. När didActivateAudioSession ska ljudströmmarna startas.

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

Det är viktigt att även stänga av det utgående ljudet innan ljudet stoppas i fall då CallKit inte anropar didActivateAudioSession. Användaren kan sedan slå på mikrofonen manuellt.

Kommentar

I vissa fall anropar didActivateAudioSession inte CallKit trots att appen har utökade ljudbehörigheter, i så fall förblir ljudet avstängt tills återanropet tas emot. Och användargränssnittet måste återspegla talarens och mikrofonens tillstånd. Fjärrdeltagaren/-deltagarna i anropet ser också att användaren har stängt av ljudet. Användaren måste slå på ljudet manuellt i dessa fall.

Nästa steg