다음을 통해 공유


CallKit과 통합

이 문서에서 CallKit을 iOS 애플리케이션과 통합하는 방법을 살펴보겠습니다.

필수 조건

CallKit 통합(SDK 내)

Azure Communication Services iOS SDK의 CallKit 통합은 CallKit과의 상호 작용을 처리합니다. 음소거/음소거 해제, 대기/다시 시작과 같은 통화 작업을 수행하려면 Azure Communication Services SDK에서 API를 호출하기만 하면 됩니다.

CallKitOptions로 통화 에이전트 초기화

CallKitOptions의 구성된 인스턴스를 사용하여 CallKit을 처리하여 CallAgent를 만들 수 있습니다.

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

발신 통화 수신자 정보 지정

먼저 발신 통화용 StartCallOptions() 또는 그룹 통화용 JoinCallOptions() 인스턴스를 만들어야 합니다.

let options = StartCallOptions()

또는

let options = JoinCallOptions()

그런 다음 CallKitRemoteInfo의 인스턴스를 만듭니다.

options.callKitRemoteInfo = CallKitRemoteInfo()
  1. 통화 수신자의 표시 이름을 사용자 지정하고 CXHandle 값을 구성하려면 callKitRemoteInfo.displayNameForCallKit에 대한 값을 할당합니다. displayNameForCallKit에 지정된 이 값은 마지막으로 전화를 건 호출 기록에 정확하게 표시되는 방식입니다. 마지막으로 전화를 거는 통화 로그에 있습니다.
options.callKitRemoteInfo.displayNameForCallKit = "DISPLAY_NAME"
  1. cxHandle 값 할당은 사용자가 해당 연락처로 다시 전화를 걸 때 애플리케이션이 받는 값입니다.
options.callKitRemoteInfo.cxHandle = CXHandle(type: .generic, value: "VALUE_TO_CXHANDLE")

수신 전화에 대한 통화 수신자 정보 지정

먼저 CallKitOptions의 인스턴스를 만들어야 합니다.

let callKitOptions = CallKitOptions(with: createProviderConfig())

CallKitOptions 인스턴스의 속성을 구성합니다.

변수 provideRemoteInfo에 전달된 블록은 수신 전화를 받을 때 SDK에 의해 호출되며 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
}

오디오 세션 구성

걸려오는 전화를 걸거나 받기 전과 보류된 통화를 다시 시작하기 전에 오디오 세션 구성이 호출됩니다.

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
}

참고: Contoso가 오디오 세션을 이미 구성한 경우 nil을 제공하지 않고 블록에서 nil 오류를 반환합니다.

callKitOptions.configureAudioSession = self.configureAudioSession

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

configureAudioSession에 대해 nil이 제공되면 SDK는 SDK의 기본 구현을 호출합니다.

들어오는 푸시 알림 페이로드 처리

앱이 수신 푸시 알림 페이로드를 수신하면 이를 처리하기 위해 handlePush를 호출해야 합니다. Azure Communication Services Calling SDK는 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, didRecieveIncomingCall incomingcall: IncomingCall) {
}

reportIncomingCall을 사용하여 앱이 닫혀 있거나 그렇지 않은 경우 푸시 알림을 처리할 수 있습니다.

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 통합(앱 내)

앱 내에 CallKit을 통합하고 SDK에서 CallKit 구현을 사용하지 않으려면 여기에서 빠른 시작 샘플을 참조하세요. 그러나 주의해야 할 중요한 사항 중 하나는 적시에 오디오를 시작하는 것입니다. 다음과 같습니다.

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)

스피커와 마이크를 음소거하면 CallKit이 CXProviderDelegate에서 didActivateAudioSession을 호출할 때까지 실제 오디오 디바이스가 사용되지 않습니다. 그렇지 않으면 통화가 끊기거나 오디오가 작동하지 않습니다. didActivateAudioSession은 오디오 스트림이 시작되어야 하는 때입니다.

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

CallKit에서 didActivateAudioSession을 호출하지 않는 경우 오디오를 중지하기 전에 나가는 오디오를 음소거하는 것도 중요합니다. 그러면 사용자가 마이크의 음소거를 수동으로 해제할 수 있습니다.

참고 항목

어떤 경우에는 앱이 높은 오디오 권한을 가지고 있어도 CallKit이 didActivateAudioSession을 호출하지 않습니다. 이 경우 콜백을 받을 때까지 오디오가 음소거 상태로 유지됩니다. 그리고 UI는 스피커와 마이크의 상태를 반영해야 합니다. 통화 중인 원격 참가자는 사용자가 음소거한 오디오도 볼 수 있습니다. 이러한 경우 사용자는 수동으로 음소거를 해제해야 합니다.

다음 단계