본문으로 건너뛰기
Version: 5.5

1대1 음성 통화

1대1 음성 통화를 구현하는 예제 코드입니다.

필수 조건

시작하기 전에 다음 작업을 수행해야 합니다.

1대1 음성 통화 구현 시 고려 사항

착신자 측에서 통화 알림을 받으려면 알림용 시스템을 구현하거나 APNs(Apple Push Notification service) 같은 외부 푸시 알림 시스템을 연동해야 합니다.

또한 착신자에게 어떤 정보를 전달해야 하는지도 알아야 합니다. 앱 서버의 역할에서 애플리케이션이 전달해야 하는 데이터인 cc_param에 관해 볼 수 있습니다.

makeCall() 또는 verifyCall()을 호출한 다음에는 반환된 PlanetKitCallMakeResult 또는 PlanetKitCallVerifyResult에서 reason을 확인해야 합니다.

  • reasonPlanetKitStartFailReason.none인 경우 성공을 의미합니다.
  • 그렇지 않으면 실패를 의미하며 reason에 따라 적절한 조치를 취해야 합니다.

이벤트 대리자 구현

통화에서 사용될 이벤트 대리자(delegate)를 구현하세요.

  • PlanetKitCallDelegate는 통화 셋업과 관련된 이벤트를 처리하는 데 사용됩니다.
  • PlanetKitMyMediaStatusDelegate는 로컬 사용자의 미디어 상태 변경 이벤트를 처리하는 데 사용됩니다. 마이크가 음소거되거나 음소거 해제되는 경우 또는 오디오 설명이 업데이트되는 경우 등 이벤트를 기반으로 로컬 사용자의 UI를 업데이트할 수 있습니다.
extension CallDelegateExample : PlanetKitCallDelegate {
func didWaitConnect(_ call: PlanetKitCall) {

// This is called after making a call on the caller side.
// Write your own code here.

}

func didVerify(_ call: PlanetKitCall, peerStartMessage: PlanetKitCallStartMessage?, peerUseResponderPreparation: Bool) {

// This is called after verifying a call on the callee side.
// Write your own code here.

}

func didConnect(_ call: PlanetKitCall, connected: PlanetKitCallConnectedParam) {

// This is called after the call is connected on both sides.
// Write your own code here.

}

func didDisconnect(_ call: PlanetKitCall, disconnected: PlanetKitDisconnectedParam) {

// This is called after the call is disconnected on both sides.
// Write your own code here.

}

func didFinishPreparation(_ call: PlanetKitCall) {

// This is called after making a call on the caller side.
// Write your own code here.

}

...

//
// Also, you should implement other methods defined in the PlanetKitCallDelegate protocol.
//
...
}

extension MyMediaStatusDelegateExample: PlanetKitMyMediaStatusDelegate {
func didMuteMic(_ myMediaStatus: PlanetKitMyMediaStatus) {

// This is called when the local user's audio is muted.
// Write your own code here.

}

func didUnmuteMic(_ myMediaStatus: PlanetKitMyMediaStatus) {

// This is called when the local user's audio is unmuted.
// Write your own code here.

}

func didUpdateAudioDescription(_ myMediaStatus: PlanetKitMyMediaStatus, description: PlanetKitMyAudioDescription) {

// This is called when the local user's audio description is updated.
// Write your own code here.

}
}

통화 생성(발신 측)

통화를 생성하기 위한 절차는 다음과 같습니다.

  1. PlanetKitMakeCallSettingBuilder를 통해 설정 정보를 만드세요.
  2. PlanetKitCallDelegate를 포함하여 PlanetKitCallParam을 생성하세요.
  3. PlanetKitManager.shared.makeCall()를 호출하세요.
  4. PlanetKitCallMakeResult를 확인하세요.
class CallerExample
{
var call: PlanetKitCall?
var myMediaStatusDelegate: MyMediaStatusDelegateExample
}

...

extension CallerExample
{
func makeCallExample(myId: String, myServiceId: String, peerId: String, peerServiceId: String, accessToken: String, delegate: PlanetKitCallDelegate) {
let myUserId = PlanetKitUserId(id: myId, serviceId: myServiceId)
let peerUserId = PlanetKitUserId(id: peerId, serviceId: peerServiceId)

let settings = try! PlanetKitMakeCallSettingBuilder().build()
let param = PlanetKitCallParam(myUserId: myUserId, peerUserId: peerUserId, delegate: delegate, accessToken: accessToken)

let result = PlanetKitManager.shared.makeCall(param: param, settings: settings)

guard result.reason == .none else {
NSLog("Failed reason: result. \(result.reason)")
return
}

// The result.call instance is a call instance for calling other APIs from now on.
// You must keep this instance in your own context.
// In this example, the "call" variable holds the instance.
call = result.call
}
}

푸시 알림 수신(착신 측)

통화를 수신한 쪽은 푸시 알림 시스템을 통해 새로운 전화가 왔음을 알게 됩니다. 이때 userInfo로부터 cc_param을 파싱해야 합니다.

새로운 전화가 왔음을 알리는 푸시 알림을 받은 다음 사용자가 통화할 수 있도록 그 요청을 수신해야 합니다.

extension YourPushHandler : PKPushRegistryDelegate {

...

var callee: CalleeExample
var callDelegate: PlanetKitCallDelegate

func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
// Update the push token for the application's push server.
}

func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {

let message = payload.dictionaryPayload as NSDictionary as! [String: AnyObject]
let param = message["cc_param"] as! String
let ccParam = PlanetKitCCParam(ccParam: param)

// Now we received a push notification.
// We should write code here to respond to the new call.
// For convenience, we assume this is done in verifyCallExample()

let myUserId = "Callee User ID value"
let myServiceId = "Callee Service ID value"

callee.verifyCallExample(myId: myUserId, myServiceId: myServiceId, ccParam: ccParam, delegate: PlanetKitCallDelegate)
}
}

통화 수신(착신 측)

이제 PlanetKitManager.shared.verifyCall()를 호출해서 통화를 수신해 봅시다.

아래 코드의 verifyCallExample()에서 통화를 수신하는 방법을 볼 수 있습니다. 이때, myId는 착신자의 사용자 ID이고, myServiceId는 착신자의 서비스 ID입니다.

class CalleeExample
{
var verifiedCall: PlanetKitCall?
var myMediaStatusDelegate: MyMediaStatusDelegateExample
}

...

extension CalleeExample
{
func verifyCallExample(myId: String, myServiceId: String, ccParam: PlanetKitCCParam, delegate: PlanetKitCallDelegate) {
let settings = try! PlanetKitVerifyCallSettingBuilder().build()

let myUserId = PlanetKitUserId(id: myId, serviceId: myServiceId)

let result = PlanetKitManager.shared.verifyCall(myUserId: myUserId, ccParam: ccParam, delegate: delegate)

guard result.reason == .none else {
NSLog("Failed reason: result. \(result.reason)")
return
}

// The result.call instance is the verified call instance.
// You have to keep this instance in an application layer to call other APIs provided after call setup.
// This example keeps the verified call instance in the following verifiedCall variable.
verifiedCall = result.call
}
}

통화 응답(착신 측)

일반적으로 통화를 수신한 후 통화를 받을지 결정할 시간이 필요합니다. 통화에 응답하려면 acceptCall()을 호출하세요.

Tip

verifiedCall 변수는 verifyCall()에서 검증을 마친 PlanetKitCall 인스턴스입니다.

extension CalleeExample
{
func acceptCallExample() {
verifiedCall.acceptCall(startMessage: nil, useResponderPreparation: false)
}
}

로컬 사용자에 대한 미디어 상태 이벤트 대리자 설정

통화가 연결되면 로컬 사용자에 대한 미디어 상태 이벤트 대리자를 설정하세요.

// Caller side
extension CallerExample
{
func addMyMediaStatusHandlerExample() {
call.myMediaStatus.addHandler(myMediaStatusDelegate) {
// completion callback
}
}
}

// Callee side
extension CalleeExample
{
func addMyMediaStatusHandlerExample() {
verifiedCall.myMediaStatus.addHandler(myMediaStatusDelegate) {
// completion callback
}
}
}

통화 종료

통화를 종료하려면 endCall()을 호출하세요.

// Caller side
extension CallerExample
{
func endCallExample() {
call.endCall()
}
}

// Callee side
extension CalleeExample
{
func endCallExample() {
verifiedCall.endCall()
}
}

애플리케이션에서 Apple CallKit 구현

Note

이 단계는 iOS 애플리케이션에만 적용됩니다.

CallKit은 통화 인터페이스를 제공하는 Apple의 프레임워크 중 하나입니다. PlanetKit SDK는 CallKit에 대해 사용자 CallKit 구현 연동, PlanetKit 내부 구현 연동, 연동하지 않음이라는 세 가지 옵션을 지원합니다.

기본적으로 PlanetKit SDK는 CallKit 연동 없이 통화를 생성하거나 수신합니다. 자체 CallKit 구현을 연동하려면 PlanetKit SDK로 통화를 생성하거나 수신할 때 PlanetKitCallKitSettingtypeuser로 설정해야 합니다.

발신 측 구현 예제

  1. PlanetKitCallKitSetting 인스턴스를 생성하고 PlanetKitCallKitSettingtypeuser로 설정하세요.

  2. PlanetKitMakeCallSettingBuilderwithCallKitSettingsKey()에 인스턴스를 전달하고 통화 생성 설정(makeCallSettings)을 만드세요.

    // Code for Step 1 and Step 2

    func createMakeCallSettingsWithUserCallKitExample() -> [String: Any] {
    let callKitSetting = PlanetKitCallKitSetting(type: .user, param: nil)

    var settingsBuilder = try! PlanetKitMakeCallSettingBuilder().withCallKitSettingsKey(setting: callKitSetting)

    let makeCallSettings = settingsBuilder.build()
    }
  3. CXProviderDelegateprovider(_:didActivate:)에서 PlanetKitCallnotifyCallKitAudioActivation()을 호출하세요.

    // Code for Step 3

    extension YourCallKitHandler : CXProviderDelegate {
    ...

    func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
    // PlanetKitCall instance created from PlanetKitManager.makeCall()
    call.notifyCallKitAudioActivation()
    }

    ...
    }

착신 측 구현 예제

  1. PlanetKitCallKitSetting 인스턴스를 생성하고 PlanetKitCallKitSettingtypeuser로 설정하세요.

  2. PlanetKitVerifyCallSettingBuilderwithCallKitSettingsKey()에 인스턴스를 전달하고 통화 수신 설정(verifyCallSettings)을 만드세요.

    // Code for Step 1 and Step 2

    func createVerifyCallSettingsWithUserCallKitExample() -> [String: Any] {
    let callKitSetting = PlanetKitCallKitSetting(type: .user, param: nil)

    var settingsBuilder = try! PlanetKitVerifyCallSettingBuilder().withCallKitSettingsKey(setting: callKitSetting)

    let verifyCallSettings = settingsBuilder.build()
    }
  3. CallKit 동작을 처리하기 위한 CallKit 핸들러를 구현하세요.

    class YourCallKitHandler {
    static let shared = YourCallKitHandler()

    private var provider: CXProvider?
    private var callController: CXCallController?
    private var call: PlanetKitCall?
    ...
    // Implement initialization and other features to handle CallKit operations.
    }
  4. CXProviderDelegateprovider(_:perform:)CXCallAction을 기반으로 PlanetKitCallacceptCall()endCall()을 호출하세요.

    extension YourCallKitHandler : CXProviderDelegate {

    ...

    func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
    call.acceptCall(useResponderPreparation: false, startMessage: nil)
    action.fulfill()
    }

    func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
    call.endCall()
    action.fulfill()
    }

    func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
    call.muteMyAudio(action.isMuted) { success in }
    action.fulfill()
    }

    // Implement other delegate functions to handle other CXCallActions
    ...
    }
  5. CXProviderDelegateprovider(_:didActivate:)에서 PlanetKitCallnotifyCallKitAudioActivation()을 호출하세요.

    extension YourCallKitHandler : CXProviderDelegate {

    ...

    func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
    // PlanetKitCall instance created from PlanetKitManager.makeCall()
    call.notifyCallKitAudioActivation()
    }

    // Implement other delegate functions to handle other CXCallActions
    ...
    }
  6. CallKit과 PlanetKit을 동기화하기 위해 CallKit을 통해 PlanetKitCallmuteMyAudio(), hold()unhold()를 호출하도록 UI 코드를 수정하세요.

    extension YourCallViewController {
    @IBAction func muteCall(_ sender: UIButton) {
    YourCallKitHandler.shared.muteCall()
    }
    }

    extension YourCallKitHandler {
    func muteCall() {
    let action = CXSetMutedCallAction(call: call.uuid , muted: true)
    callController.requestTransaction(with: action) { error in
    NSLog("\(error)")
    }
    }
    }

상세한 정보는 CallKit 문서를 참고하세요.

관련 예제 코드

관련 문서