Responding to VoIP Notifications from PushKit

Panther·2021년 8월 20일
0

https://developer.apple.com/documentation/pushkit/responding_to_voip_notifications_from_pushkit

"Receive incoming Voice-over-IP (VoIP) push notifications and use them to display the system call interface to the user."

수신 중인 Voice-over-IP(VoIP) 푸시 노티피케이션을 받고, 사용자에게 시스템 통화 인터페이스를 표시하기 위해 푸시 노티피케이션을 사용합니다.

Overview

앱이 Voice-over-IP(VoIP) 전화 서비스를 제공한다면, 사용자 기기에서 수신 중인 전화를 처리하기 위해 PushKit을 사용할 수 있습니다. PushKit은 실행중인 상태가 아니어도 전화를 받을 수 있도록 전화를 관리할 수 있는 효율적인 방법을 제공합니다. 특정 사용자에게 전화를 감지하면 서버는 해당 전화에 대한 정보를 푸시 노티피케이션으로 전송합니다. 노티피케이션을 받는 것에 따라 기기는 앱을 깨우고 사용자에게 알려줄 시간을 주며 전화 서비스에 연결합니다.

iOS 13 SDK 혹은 이후 버전을 사용해서 빌드된 앱의 경우 PushKit은 VoIP 통화 처리시 CallKit 사용을 요구할 것입니다. CallKit은 사용자의 기기에서 전화 관련 서비스를 제공하는 앱이 사용자의 기기에서 매끄럽게 작동하는 것을 보장하고, 방해금지와 같은 기능을 사용할 수 있게 해줍니다. 또한, CallKit은 시스템의 전화 관련 UI를 작동시키며, 이는 수신 혹은 발신 전화 스크린을 포함합니다. 이와 같은 인터페이스 제시를 위해, 그리고 인터페이스에 대한 상호작용 관리를 위해서 CallKit을 사용할 수 있습니다.

Important
앱에서 CallKit 지원이 불가능하다면 푸시 노티피케이션 처리를 위한 PushKit을 사용할 수 없습니다. 대신 앱의 푸시 노티피케이션 지원을 User Notifications 프레임워크로 지원해야 합니다. 컨텐트 암호해제와 같은 수신 노티피케이션의 응답을 작업할 필요가 있다면, 해당 작업을 수행하기 위해 노티피케이션 서비스 확장을 사용하시기 바랍니다. 노티피케이션 처리 및 노티피케이션 서비스 확장 구현에 대한 더 많은 정보는 User Notifications를 보시기 바랍니다.

User Notifications
https://developer.apple.com/documentation/usernotifications
https://velog.io/@panther222128/User-Notifications

PushKit 지원을 위한 설정 방법 내용은 Supporting PushKit Notifications in Your App을 보시기 바랍니다.

Supporting PushKit Notifications in Your App
https://developer.apple.com/documentation/pushkit/supporting_pushkit_notifications_in_your_app
https://velog.io/@panther222128/Supporting-PushKit-Notifications-in-Your-App

Create a Call Provider Object to Manage Calls in Your App

VoIP 앱은 시스템의 전화 관련 인터페이스를 제시하려면 CallKit을 사용해야 합니다. 수신 및 발신 전화에 대한 사용자 상호작용을 관리하는 CXProvider 객체를 사용해서 인터페이스를 제시할 수 있습니다. 앱 생명주기에서 제공자 객체를 생성하고 앱의 전화 관련 코드에서 이를 사용할 수 있도록 해야 합니다.

아래 코드 예시는 제공자 객체 생성 및 커스텀 딜리게이트를 제공자 객체에 할당하는 방법을 보여주고 있습니다. CXProvider 객체를 서비스에 대한 세부사항을 포함하는 설정 객체와 함께 초기화해야 합니다. 앱의 커스텀 객체 중 한 가지를 딜리게이트로 할당해야 하며, 사용자 액션 및 전화 관련 변경사항에 응답하기 위해 딜리게이트 객체를 사용해야 합니다.

// Configure the app's CallKit provider object.
let config = CXProviderConfiguration(localizedName: "VoIP Service")
config.supportsVideo = true
config.supportedHandleTypes = [.phoneNumber]
config.maximumCallsPerCallGroup = 1

// Create the provider and attach the custom delegate object
// used by the app to respond to updates.
callProvider = CXProvider(configuration: config)
callProvider?.setDelegate(callManager, queue: nil)

각각의 제공자 객체는 앱의 전화 서비스 하나의 인스턴스를 나타내고 있고 시스템과의 상호작용을 기능하게 합니다. 앱은 현재 사용자에게 전화 통화를 관리할 수 있도록 해주는 것으로 오직 하나의 제공자 객체만 필요합니다. 서비스가 다중 사용자 혹은 다중 계정을 허용하는 경우 수신 중인 통화를 적합한 사용자 계정으로 매핑시켜야 합니다.

Generate Push Notifications from Your Server

서버는 사용자 연결에 필요한 대부분의 통화 관련 작업을 처리합니다. 기기에서 사용자가 앱을 열 때마다 해당 앱으로부터 서버에 연결을 생성합니다. 사용자가 전화 통화를 시작하면 서버와 해당 통화의 세부사항을 커뮤니케이션하기 위해 앞서 생성한 연결을 사용합니다. 서버는 전화를 시작한 사람과 받아야 하는 사람의 연결을 시도해야 합니다. 전화를 받는 사람의 앱이 실행중이고 서버에 활성화된 연결을 갖고 있다면 기존 연결을 사용해서 앱과 직접 커뮤니케이션합니다. 앱이 실행중이지 않은 경우 앱을 개우기 위한 푸시 노티피케이션을 생성합니다.

각 푸시 노티피케이션은 기기 토큰과 전화의 세부사항을 담은 페이로드로 구성됩니다. 이후에 애플 푸시 노티피케이션 서비스(APNs)로 전송하게 될 HTTP 요청으로 이 정보를 번들화 할 수 있습니다. PushKit은 설정 타임에 사용자 기기에 대한 타깃 주소로써 사용할 수 있는 기기 토큰을 제공합니다.

VoIP 푸시 노티피케이션을 위한 HTTP 요청을 설정할 때, 항상 아래 정보를 함께 담아 요청을 설정해야 합니다.

  • apns-expiration 헤더 필드의 값을 0 혹은 몇 초 수준으로 설정합니다. 이 작업은 시스템이 늦지않게 노티피케이션을 전송할 수 있도록 합니다.
  • JSON 페이로드에 수신 전화에 대한 정보를 포함시킵니다. 예를 들어 서버가 전화를 추적하기 위해 사용하는 고유한 전화 아이덴티파이어를 포함시킵니다. 이후에 서버에 있는지 확인하기 위해 이 아이덴티파이어를 사용할 수 있습니다. 수신 전화 UI에서 발신자의 정보를 표시할 수 있도록 발신자에 대한 정보를 포함시킬 수 있습니다.

푸시 노티피케이션 생성을 위한 커스텀 서버 설정에 대한 정보는 Setting Up a Remote Notification Server를 보시기 바랍니다. 푸시 노티피케이션 전송에 대한 더 많은 정보는 Sending Notification Requests to APNs를 보시기 바랍니다.

Setting Up a Remote Notification Server
https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server
https://velog.io/@panther222128/Setting-Up-a-Remote-Notification-Server

Sending Notification Requests to APNs
https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/sending_notification_requests_to_apns
https://velog.io/@panther222128/Sending-Notification-Requests-to-APNs

Respond to VoIP Push Notifications in Your App

사용자 중 한 명이 전화 통화를 시작하면 서버는 수신자의 기기에서 앱을 연결시켜야 합니다. 서버가 앱으로의 활성화된 네트워크 연결을 갖지 않는다면 푸시 노티피케이션 전송을 통해 앱에게 확인을 요청할 수 있습니다. 서버에서 푸시 노티피케이션 연결을 구성하고, 이를 사용자 기기에 전달하기 위해 APNs로 보내시기 바랍니다. VoIP 노티피케이션의 경우 시스템은 앱을 launch 하거나 깨우고, 자신의 딜리게이트 메소드 pushRegistry(_:didReceiveIncomingPushWith:for:completion:)를 호출하는 PKPushRegistry 객체에 노티피케이션을 전달합니다. 수신 전화 UI 표시를 위해서, 그리고 VoIP 서버에 연결을 만들기 위해서 해당 메소드를 사용하시기 바랍니다.

아래 코드 예시는 pushRegistry(_:didReceiveIncomingPushWith:for:completion:) 메소드에서 수신 VoIP 푸시 노티피케이션 처리 방법을 보여주고 있습니다. 노티피케이션의 페이로드 딕셔너리로부터 전화 데이터를 추출한 후 CXCallUpdate 객체를 생성하고 앱 CXProvider 객체의 reportNewIncomingCall(with:update:completion:) 메소드에 전달해야 합니다. CallKit이 요청을 처리하는 동안 병렬로 VoIP 서버로의 연결을 만들어야 합니다. 문제 발생 시 이후에 언제나 CallKit에게 알려줄 수 있습니다. CallKit이 전화를 성공저긍로 처리하면 컴플리션 블록은 앱 내부에서 해당 전화를 관리하기 위해 커스텀 데이터 구조를 생성할 수 있습니다.

func pushRegistry(_ registry: PKPushRegistry, 
          didReceiveIncomingPushWith payload: PKPushPayload, 
          for type: PKPushType, 
          completion: @escaping () -> Void) {
   if type == .voIP {
      // Extract the call information from the push notification payload
      if let handle = payload.dictionaryPayload["handle"] as? String,
            let uuidString = payload.dictionaryPayload["callUUID"] as? String,
            let callUUID = UUID(uuidString: uuidString) {

         // Configure the call information data structures.
         let callUpdate = CXCallUpdate()
         let phoneNumber = CXHandle(type: .phoneNumber, value: handle)
         callUpdate.remoteHandle = phoneNumber
                
         // Report the call to CallKit, and let it display the call UI.
         callProvider?.reportNewIncomingCall(with: callUUID, 
                     update: callUpdate, completion: { (error) in
            if error == nil {
               // If the system allows the call to proceed, make a data record for it.
               let newCall = VoipCall(callUUID, phoneNumber: phoneNumber)
               self.callManager.addCall(newCall)
            }

            // Tell PushKit that the notification is handled.
            completion()
         })
                
         // Asynchronously register with the telephony server and 
         // process the call. Report updates to CallKit as needed.
         establishConnection(for: callUUID)
      }
   }
}

시스템이 전화를 계속할 것을 허용하면 reportNewIncomingCall(with:update:completion:) 메소드는 자신의 컴플리션 블록을 실행하고 CallKit은 수신 전화 인터페이스를 표시합니다. 이 시점에 인터페이스에 대한 사용자 상호작용에 응답하기 위해 CXProvider 객체의 딜리게이트를 사용하시기 바랍니다. 예를 등러 사용자가 전화를 받거나 끝낼 때 응답하기 위해 딜리게이트를 사용할 수 있습니다.

Note
노티피케이션의 페이로드에 발신자 정보를 넣지 않았다면 전화 인터페이스 업데이트를 위해 앱 제공자 객체의 reportCall(with:updated:) 메소드를 호출하시기 바랍니다. 전화를 업데이트하기 위해 언제든지 이 메소드를 호출할 수 있습니다. 예를 들어 앱이 VoIP 서버에서 업데이트된 발신자 정보를 가져온 후 이 메소드를 호출하시기 바랍니다.

전화 인터페이스에 대한 사용자 상호작용 처리 방법의 더 많은 정보는 CXProviderDelegate의 메소드를 보시기 바랍니다.

Respond to Call Hang Ups and Failures

여러 가지 이유로 VoIP 전화 연결 시 문제가 발생할 수 있으며, CallKit은 문제 발생 시 처리를 더 쉽게 해줄 수 있습니다.

전화를 시작한 사람이 전화를 끊으면 앱에게 알리기 위해 앱과 서버 사이의 네트워크 연결을 사용해야 합니다. 앱에서 전화의 끝에 대한 이유로 CXCallEndedReason.remoteEnded를 구체화하면서 CXProvider 객체의 reportCall(with:endedAt:reason:) 메소드를 호출하시기 바랍니다. 수신 전화 인터페이스가 화면에 나타나면 CallKit은 전화의 끝을 반영하기 위해 인터페이스를 업데이트하고, 인터페이스를 해제합니다.

앱이 서버로의 연결을 만들기 전에 전화를 받는 사람이 응답하면, 딜리게이트에 즉시 provider(_:perform:)로 전달되는 CXAnswerCallAction 객체를 충족시키지 못합니다. 대신 연결을 만들 때까지 기다리고 객체를 충족시켜야 합니다. 요청을 충족시키기 위해 앱이 기다리는 동안 수신 전화 인터페이스는 사용자가 전화 연결 진행중임(아직 준비는 되지 않은)을 알 수 있도록 해줍니다.

앱이 서버로의 연결을 만드는 데 실패하면 CXCallEndedReason.failed 옵션을 갖는 reportCall(with:endedAt:reason:) 메소드를 호출할 수 있습니다. 수신 전화 인터페이스가 현재 화면에 나타나 있는 경우 시스템은 통화 실패를 나타내기 위해 인터페이스를 업데이트합니다.

처음 푸시 노티피케이션을 전송한 후 전화 취소 혹은 앱의 다른 세부사항 커뮤니케이션을 위한 추가적인 푸시 노티피케이션은 전송하지 않으시길 바랍니다. 대신 앱과 서버 사이에 만들어진 네트워크 연결을 거쳐서 직접 앱과 커뮤니케이션하시기 바랍니다. 기존 네트워크 연결 사용은 푸시 노티피케이션 전송보다 더 빠르며, 네트워크 상태가 좋지 않으면 APNs는 기기에 푸시 노티피케이션 전달을 할 수 없을 것입니다.

0개의 댓글