iOS 용 CallKit 자습서
앱이 시스템 수준 전화 통합을 위해 CallKit을 사용하는 방법과 통화 차단 / 식별을위한 디렉토리 확장을 구축하는 방법에 대해 알아보십시오.
번역
다행히도, 애플은 iOS 10에서 CallKit을 도입하여 그 모든 것을 바꾸 었습니다!
이 튜토리얼에서는 다음과 같은 앱을 빌드하여 CallKit의 힘을 엿볼 수 있습니다.
- 시스템 서비스를 사용하여 수신 및 발신 통화를보고합니다.
- 수신 전화를 식별하거나 차단하기 위해 전화 번호부를 관리합니다.
참고 : CallKit 기능은 시뮬레이터에서 작동하지 않습니다. 이 튜토리얼을 따라하기 위해서는 iOS 10.2가 설치된 iPhone이 필요합니다.
시작하기
이 자습서의 시작 프로젝트 를 다운로드하고 압축을 풉니 다. 장치에서 프로젝트를 디버그하려면 코드 서명을 설정해야합니다. Xcode에서 프로젝트 파일을 열고 프로젝트 탐색기에서 핫라인을 선택하십시오.
번들 식별자를 변경하여 시작합니다. 프로젝트가 선택되면 일반 탭으로 이동하여 신원 섹션을 찾으십시오. 번들 식별자를 고유 한 것으로 변경하십시오.
그런 다음 서명 섹션을 찾습니다. 팀 옆에있는 드롭 다운에서 원하는 개발 팀을 선택하십시오 (제 경우에는 개인 팀입니다). 또한 "자동 서명 관리"가 선택되어 있는지 확인하십시오. 그러면 Xcode가 앱의 프로비저닝 프로파일을 자동으로 만들 수 있습니다.
설정을 테스트하려면 iPhone에서 앱을 빌드하고 실행하십시오.
현재이 애플리케이션은별로 도움이되지 않지만 이미 스타터 프로젝트에 소스 파일이 상당히 많이 있음을 알 수 있습니다. 그들은 주로 UI를 설정하고 사용자 상호 작용을 다루는 책임이 있지만 다음 단계로 이동하기 전에 살펴볼 가치가있는 두 가지 주요 클래스가 있습니다.
Call
는 전화Call
나타냅니다. 이 클래스는 호출을 식별하는 속성 (예 : UUID 또는 핸들)과 사용자가 호출을 시작하거나 응답하거나 종료하는 시점을 나타내는 수명주기 콜백을 제공합니다.CallManager
현재 앱에서 진행중인 전화 목록을 유지 관리하며 전화를 추가하거나 제거하는 방법을 제공합니다. 튜토리얼 전반에 걸쳐이 클래스를 확장 할 것이다.
CallKit은 무엇입니까?
CallKit은 앱이 기본 전화 UI와 긴밀하게 통합되도록하여 VoIP 환경을 개선하는 것을 목표로하는 새로운 프레임 워크입니다. CallKit을 채택하면 앱에서 다음을 수행 할 수 있습니다.
- 잠금 상태와 잠금 해제 상태 모두에서 기본 수신 전화 화면을 사용하십시오.
- 기본 전화 응용 프로그램의 연락처, 즐겨 찾기 및 최근 화면에서 통화를 시작하십시오.
- 시스템의 다른 통화와 상호 작용하십시오.
이 섹션에서는 CallKit 아키텍처에 익숙해집니다. 아래 다이어그램은 모든 주요 플레이어를 보여줍니다.
CallKit으로 작업 할 때 상호 작용할 기본 클래스는 CXProvider
와 CXCallController
입니다.다이빙 할 시간!
CXProvider
앱은 CXProvider
를 사용하여 대역 외 통지를 시스템에보고합니다. 이들은 일반적으로 들어오는 호출과 같은 외부 이벤트입니다.
이러한 이벤트가 발생할 때마다 CXProvider
는 호출 업데이트를 만들어 시스템에 알립니다.통화 업데이트 란 무엇입니까? 통화 업데이트는 새롭거나 변경된 통화 관련 정보를 캡슐화합니다. CXCallUpdate
클래스는 호출자의 이름과 같은 속성을 노출하거나 오디오 전용인지 또는 화상 통화인지를 표시합니다.
차례대로 시스템이 앱에 이벤트를 알리려고 할 때마다 CXAction
인스턴스의 형태로 앱을 알립니다. CXAction
은 전화 통신 작업을 나타내는 추상 클래스입니다. 각 작업에 대해 CallKit은 CXAction의 다른 구체적인 구현을 제공합니다. 예를 들어 발신 전화 시작은 CXStartCallAction
, CXAnswerCallAction
은 수신 전화 응답에 사용됩니다. 동작은 고유 한 UUID로 식별되며 실패하거나 수행 할 수 있습니다.
Apps는 CXProviderDelegate
프로토콜을 통해 CXProvider
와 통신 할 수 있습니다. CXProviderDelegate
프로토콜은 공급자 라이프 사이클 이벤트 및 수신 작업에 대한 메서드를 정의합니다.
CXCallController
앱은 CXCallController
를 사용하여 "시작 통화"와 같은 사용자 요청에 대해 시스템에 CXCallController
. 이것은 CXProvider
와 CXProvider
의 주요 차이점입니다. 공급자의 작업이 시스템에보고하는 동안 호출 컨트롤러는 사용자를 대신하여 시스템에서 요청을합니다.
콜 컨트롤러는 트랜잭션을 사용하여 이러한 요청을 수행합니다. CXTransaction
대표되는 트랜잭션은 하나 이상의 CXAction
인스턴스를 포함합니다. 통화 컨트롤러는 시스템에 트랜잭션을 보내고 모든 것이 정상적으로 처리되면 시스템은 공급자에게 적절한 조치로 응답합니다.
물론 많은 정보가 있었지만 실제로 어떻게 작동합니까?
수신 전화
아래 다이어그램은 들어오는 콜 흐름의 개요입니다.
- 들어오는 호출이있을 때마다 앱은
CXCallUpdate
를 생성하고 제공자를 사용하여 시스템에 전송합니다. - 이 시점에서 시스템은 모든 서비스에 대한 수신 호출로이를 게시합니다.
- 사용자가 호출에 응답하면 시스템은
CXAnswerCallAction
인스턴스를 공급자에게 보냅니다. - 앱은 적절한
CXProviderDelegate
메소드를 구현하여 호출에 응답 할 수 있습니다.
첫 번째 단계는 공급자에 대한 대리자를 만드는 것입니다.
Xcode로 돌아가서 프로젝트 탐색기에서 강조 표시된 App 그룹을 사용하여 File \ New \ File ...으로 이동하여 iOS \ Source \ Swift File을 선택하십시오. 이름을 ProviderDelegate로 설정하고 작성을 클릭 하십시오 .
파일에 다음 코드를 추가하십시오.
AVFoundation 가져 오기
CallKit 가져 오기
클래스 ProviderDelegate : NSObject {
// 1.
fileprivate는 callManager를 사용합니다. CallManager
fileprivate는 공급자를 제공합니다 : CXProvider
init (callManager : CallManager) {
self.callManager = callManager
// 2.
공급자 = CXProvider (구성 : 형식 (of : self) .providerConfiguration)
super.init ()
// 삼.
provider.setDelegate (self, queue : nil)
}
// 4.
정적 var 공급자 구성 : CXProviderConfiguration {
let providerConfiguration = CXProviderConfiguration (localizedName : "핫라인")
providerConfiguration.supportsVideo = true
providerConfiguration.maximumCallsPerCallGroup = 1
providerConfiguration.supportedHandleTypes = [.phoneNumber]
return providerConfiguration
}
}
다음과 같은 상황이 발생합니다.
- 공급자 대리자는 공급자와 통화 컨트롤러와 상호 작용하므로 두 가지 모두에 대한 참조를 저장합니다. 속성은
fileprivate
로 표시되어 있으므로 동일한 파일의 확장명에서 속성에fileprivate
수 있습니다. - 아래의 정적 변수로 저장되는 적절한
CXProviderConfiguration
하여 공급자를 초기화합니다. 공급자 구성은 호출의 동작 및 기능을 지정합니다. - 공급자가 제공하는 이벤트에 응답하려면 해당 대리자를 설정합니다.
ProviderDelegate
가 아직CXProviderDelegate
따르지CXProviderDelegate
줄은 빌드 오류를 발생CXProviderDelegate
. - 핫라인의 경우 공급자 구성을 통해 화상 통화, 전화 번호 처리를 허용하고 통화 그룹 수를 하나로 제한 할 수 있습니다. 추가 사용자 정의는 CallKit 설명서를 참조하십시오.
구성 바로 아래에 다음 도우미 메서드를 추가하십시오.
func reportIncomingCall (uuid : UUID, handle : String, hasVideo : Bool = false, 완료 : ((NSError?) -> Void)?) {
// 1.
let update = CXCallUpdate ()
update.remoteHandle = CXHandle (type : .phoneNumber, value : handle)
update.hasVideo = hasVideo
// 2.
provider.reportNewIncomingCall (with : uuid, update : update) {오류
if error == nil {
// 삼.
let call = 호출 (uuid : uuid, handle : handle)
self.callManager.add (call : call)
}
// 4.
완료? (NSError로 오류)
}
}
이 도우미 메서드는 앱이 CXProvider
API를 호출하여 들어오는 호출을보고하도록 허용합니다. 다음은 진행 상황입니다.
- 모든 관련 통화 메타 데이터가 포함 된 시스템의 통화 업데이트를 준비합니다.
- 공급자에서
reportIncomingCall(with:update:completion)
을 호출하면 시스템에 수신 호출이 통지됩니다. - 시스템이 호출을 처리하면 완료 핸들러가 호출됩니다. 오류가없는 경우
Call
인스턴스를 만들고CallManager
를 통해 호출 목록에 추가합니다. - 완성 처리기를 호출합니다 (
nil
이 아닌 경우).
이 메소드는 수신 호출을 시뮬레이트하기 위해 앱의 다른 클래스에서 호출 할 수 있습니다.
다음 단계는 프로토콜 준수를 보장하는 것입니다. 여전히 ProviderDelegate.swift 에서 CXProviderDelegate
를 준수하도록 새 확장을 선언하십시오.
확장 ProviderDelegate : CXProviderDelegate {
func providerDidReset (_ 공급자 : CXProvider) {
stopAudio ()
callManager.calls {
call.end ()
}
callManager.removeAllCalls ()
}
}
CXProviderDelegate
는 하나의 필수 메서드 인 providerDidReset(_:)
만을 지정합니다. 공급자는 재설정 될 때이 메소드를 호출하여 앱이 진행중인 모든 호출을 정리하고 정리 상태로 되돌릴 수있는 기회를 제공합니다. 이 구현에서는 진행중인 오디오 세션을 종료하고 모든 활성 통화를 처리합니다.
이제 ProviderDelegate
는 들어오는 호출을보고하는 방법을 ProviderDelegate
합니다. 이제 사용할 것입니다!
프로젝트 탐색기에서 App 그룹이 강조 표시된 상태에서 AppDelegate.swift 를 열어 편집합니다. 먼저 새 속성을 클래스에 추가합니다.
lazy var providerDelegate : ProviderDelegate = ProviderDelegate (callManager : self.callManager)
공급자 대리인을 사용할 준비가되었습니다! AppDelegate
다음 메서드를 추가합니다.
func displayIncomingCall (uuid : UUID, handle : String, hasVideo : Bool = false, 완료 : ((NSError?) -> Void)?) {
providerDelegate.reportIncomingCall (uuid : uuid, handle : 핸들, hasVideo : hasVideo, 완료 : 완료)
}
이 메서드는 다른 클래스가 공급자 대리자의 도우미 메서드에 액세스 할 수있게합니다.
퍼즐의 마지막 부분은이 호출을 사용자 인터페이스에 연결하는 것입니다. 프로젝트 탐색기에서 UI /보기 컨트롤러 그룹을 확장하고 앱의 기본 화면에 대한 컨트롤러 인 CallsViewController.swift를 엽니 다. unwindSegueForNewCall(_:)
의 빈 구현을 찾아 다음 코드로 unwindSegueForNewCall(_:)
.
@IBAction 개인 기능 unwindForNewCall (_ segue : UIStoryboardSegue) {
// 1.
newCallController = segue.source를! NewCallViewController
가드하자 handle = newCallController.handle else {return}
let videoEnabled = newCallController.videoEnabled
// 2.
let backgroundTaskIdentifier = UIApplication.shared.beginBackgroundTask (expirationHandler : nil)
DispatchQueue.main.asyncAfter (wallDeadline : DispatchWallTime.now () + 1.5) {
AppDelegate.shared.displayIncomingCall (uuid : UUID (), handle : handle, hasVideo : videoEnabled) {_ in
UIApplication.shared.endBackgroundTask (backgroundTaskIdentifier)
}
}
}
스 니펫은 다음을 수행합니다.
- 이 unwind segue의 소스 인
NewCallViewController
에서 호출의 속성을 추출합니다. - 사용자는 작업이 완료되기 전에 앱을 일시 중지 할 수 있으므로 백그라운드 작업을 사용해야합니다.
이제 모든 것이 연결되고, 응용 프로그램을 빌드하고 실행하고 다음을 수행하십시오.
- 오른쪽 구석에있는 더하기 버튼을 누릅니다.
- 번호를 입력하고 분할 된 컨트롤에서 "수신"이 선택되어 있는지 확인한 다음 완료를 누릅니다.
- 화면을 잠급니다. 이 단계는 중요하고 풍부한 기본 네비게이션 UI에 액세스하는 유일한 방법이기 때문에 중요합니다.
몇 초 이내에 기본 수신 전화 UI가 표시됩니다.
그러나 전화를 받자 마자 UI가 다음 상태에 머물러 있다는 것을 알 수 있습니다.
이것은 전화 응답을 담당하는 조각을 구현해야하기 때문입니다. Xcode로 돌아가 ProviderDelegate.swift로 돌아와 다음 코드를 클래스 확장에 추가합니다.
func 공급자 (_ 공급자 : CXProvider, 작업 수행 : CXAnswerCallAction) {
// 1.
가드하자 call = callManager.callWithUUID (uuid : action.callUUID) else {
action.fail ()
반환
}
// 2.
configureAudioSession ()
// 삼.
call.answer ()
// 4.
action.fulfill ()
}
// 5.
func 공급자 (_ 공급자 : CXProvider, didActivate audioSession : AVAudioSession) {
startAudio ()
}
다음은 단계별 분석입니다.
- 응답 할 호출의 UUID에 해당하는 호출 관리자로부터 참조를 가져 오는 것으로 시작합니다.
- 호출에 대한 오디오 세션을 구성하는 것은 응용 프로그램의 책임입니다. 시스템은 높은 우선 순위로 세션을 활성화 처리합니다.
answer()
를 호출하면 호출이 현재 활성화되었음을 나타냅니다.- 행동을 처리 할 때 실패하거나 이행하는 것이 중요합니다. 프로세스 중에 오류가 없으면
fulfill()
을 호출하여 성공을 나타낼 수 있습니다. - 시스템이 공급자의 오디오 세션을 활성화하면 대리인에게 알립니다. 통화 오디오 처리를 시작할 수있는 기회입니다.
빌드 및 응용 프로그램을 실행하고 들어오는 호출을 다시 시작하십시오. 통화에 응답하면 시스템이 진행중인 통화 상태로 성공적으로 전환됩니다.
휴대 전화의 잠금을 해제하면 iOS와 앱 모두 현재 올바른 통화 상태를 반영합니다.
전화 끊기
전화를 받으면 새로운 문제가 있음을 알 수 있습니다. 현재 전화를 끊을 방법이 없습니다. 앱은 기본 통화 화면과 앱 내에서 통화를 종료하는 두 가지 방법을 지원합니다.
아래의 다이어그램은 두 경우 모두 무슨 일이 벌어지고 있는지 보여줍니다.
첫 번째 단계의 차이점을 확인하십시오. 사용자가 통화 중 화면 (1a)에서 통화를 종료하면 시스템에서 자동으로 CXEndCallAction
을 제공 업체에 보냅니다. 그러나 핫라인 (1b)을 사용하여 통화를 종료하려면 작업을 트랜잭션으로 랩핑하고 시스템에서 요청하십시오. 시스템이 요청을 처리하면 CXEndCallAction
다시 공급자에게 보냅니다.
결말 호출을 지원하려는 방식에 관계없이 앱이 작동하려면 필요한 CXProviderDelegate
메서드를 구현해야합니다. ProviderDelegate.swift를 열고 클래스 확장에 다음 구현을 추가합니다.
func 공급자 (_ 공급자 : CXProvider, 작업 수행 : CXEndCallAction) {
// 1.
가드하자 call = callManager.callWithUUID (uuid : action.callUUID) else {
action.fail ()
반환
}
// 2.
stopAudio ()
// 삼.
call.end ()
// 4.
action.fulfill ()
// 5.
callManager.remove (call : call)
}
그리 어렵지 않습니다! 다음은 진행 상황입니다.
- 먼저 통화 관리자로부터 통화에 대한 참조를 가져옵니다.
- 통화가 끝나기 직전에 통화 오디오 처리를 중지해야합니다.
end()
호출하면 호출 상태가 변경되므로 다른 클래스가 새 상태에 반응 할 수 있습니다.- 이 시점에서 조치가 완수 된 것으로 표시하게됩니다.
- 더 이상 전화가 필요 없기 때문에 통화 관리자는 통화를 처리 할 수 있습니다.
이것은 호출 UI를 처리합니다. 앱에서 전화를 CallManager
려면 CallManager
를 연장해야합니다. 프로젝트 탐색기에서 통화 관리 그룹을 확장하면 CallManager.swift가 열립니다.
호출 관리자는 CXCallController
와 통신하므로 인스턴스에 대한 참조가 필요합니다.CallManager
클래스에 다음 속성을 추가합니다.
비공개로 callController = CXCallController ()
이제 다음 메소드를 클래스에 추가하십시오.
func end (호출 : 호출) {
// 1.
let endCallAction = CXEndCallAction (call : call.uuid)
// 2.
let transaction = CXTransaction (액션 : endCallAction)
requestTransaction (transaction)
}
// 삼.
개인 func requestTransaction (_ 트랜잭션 : CXTransaction) {
callController.request (transaction) {오류
if let error = error {
print ( "트랜잭션을 요청하는 중 오류 : \ (오류)")
} else {
print ( "요청 된 트랜잭션")
}
}
}
다음과 같은 현상이 발생합니다.
- 먼저 "통화 종료"작업을 만듭니다. 호출의 UUID를 이니셜 라이저에 전달하므로 나중에 식별 할 수 있습니다.
- 다음 단계는 조치를 트랜잭션으로 랩핑하여 시스템에 보낼 수있게하는 것입니다.
- 마지막으로 호출 컨트롤러에서
request(_:completion:)
를 호출합니다. 시스템은 공급자가이 트랜잭션을 수행하도록 요청할 것이며,이 트랜잭션은 방금 구현 한 대리자 메서드를 호출합니다.
마지막 단계는 작업을 사용자 인터페이스에 연결하는 것입니다.CallViewViewController.swift를 열고 tableView(_:cellForRowAt:)
구현 바로 아래에서 다음 호출을 작성합니다.
재정의 func tableView (_ tableView : UITableView, 커밋 editingStyle : UITableViewCellEditingStyle, forRowAt indexPath : IndexPath) {
let call = callManager.calls [indexPath.row]
callManager.end (call : call)
}
사용자가 행에서 스 와이프 - 삭제를 호출하면 앱은 CallManager
에게 해당 통화를 종료하도록 요청합니다.
장치에서 프로젝트를 빌드하고 실행하고 다음 단계를 수행하십시오.
- 오른쪽 구석에있는 더하기 버튼을 누릅니다.
- 번호를 입력하고 분할 된 컨트롤에서 "수신"이 선택되어 있는지 확인한 다음 완료를 누릅니다.
- 몇 초 이내에 들어오는 전화를 받게됩니다. 대답을하면 UI에 활성 상태로 표시됩니다.
- 활성 통화를 나타내는 행에서 왼쪽으로 스 와이프 한 다음 끝내기를 누릅니다.
이 시점에서 통화가 종료됩니다. 잠금 / 홈 화면이나 앱에서 진행중인 통화를보고하지 않습니다.
기타 공급자 조치
CXProviderDelegate 의 설명서 페이지를 보면 공급자가 수행 할 수있는 더 많은 작업 (음소거 및 그룹화, 보류중인 통화 설정 포함)이 있음을 알 수 있습니다. 후자는 Hotline의 좋은 특징처럼 들리므로 지금 바로 구현할 것입니다.
사용자가 통화의 보류 상태를 설정하려고 할 때마다 앱은 CXSetHeldCallAction
인스턴스를 공급자에게 보냅니다. 관련 위임 메서드를 구현하는 것은 사용자의 임무입니다.ProviderDelegate.swift를 열고 클래스 확장에 다음 구현을 추가합니다.
func 공급자 (_ 공급자 : CXProvider, 작업 수행 : CXSetHeldCallAction) {
가드하자 call = callManager.callWithUUID (uuid : action.callUUID) else {
action.fail ()
반환
}
// 1.
call.state = action.isOnHold? .held : .active
// 2.
if call.state == .held {
stopAudio ()
} else {
startAudio ()
}
// 삼.
action.fulfill ()
}
이것은 매우 간단합니다.
- 호출에 대한 참조를 얻은 후에는 작업의
isOnHold
속성에 따라 상태를 업데이트합니다. - 새로운 상태에 따라 통화 오디오를 시작하거나 처리를 중지 할 수 있습니다.
- 이 시점에서 수행 된 작업을 표시 할 수 있습니다.
이것은 사용자가 시작한 행동이기 때문에 CallManager
클래스도 확장해야합니다.CallManager.swift를 열고 다음 구현을 end(call:)
바로 추가하십시오 end(call:)
.
func setHeld (호출 : 호출, onHold : Bool) {
setHeldCallAction = CXSetHeldCallAction (call : call.uuid, onHold : onHold)
let transaction = CXTransaction ()
transaction.addAction (setHeldCallAction)
requestTransaction (transaction)
}
이 코드는 end(call:)
메소드와 매우 유사합니다. 실제로이 두 메소드의 유일한 차이점은이 메소드가 CXSetHeldCallAction
의 인스턴스를 트랜잭션으로 래핑한다는 것입니다. 작업에는 통화의 UUID 및 보류 상태가 포함됩니다.
이제이 작업을 UI에 연결해야 할 때입니다. CallsViewController.swift를 열고 파일 끝에 UITableViewDelegate
로 표시된 클래스 확장을 찾습니다.tableView(_:titleForDeleteConfirmationButtonForRowAt:)
바로 아래의 클래스 확장에 다음 구현을 추가합니다.
재정의 func tableView (_ tableView : UITableView, didSelectRowAt indexPath : IndexPath) {
let call = callManager.calls [indexPath.row]
call.state = call.state == .held? .active : .held
callManager? .setHeld (call : call, onHold : call.state == .held)
tableView.reloadData ()
}
사용자가 행을 탭할 때마다 위의 코드는 해당 통화의 보류 상태를 업데이트합니다.
응용 프로그램을 빌드하고 실행하고 새로운 수신 전화를 시작하십시오. 통화 셀을 탭하면 상태 라벨이 '활성'에서 '대기 중'으로 변경됩니다.
발신 통화 처리
마지막으로 사용자가 실행하는 작업은 발신 전화를 걸기위한 것입니다.ProviderDelegate.swift를 열고 다음 구현을 CXProviderDelegate
클래스 확장에 추가합니다.
func 공급자 (_ 공급자 : CXProvider, 작업 수행 : CXStartCallAction) {
call = 통화 (uuid : action.callUUID, 발신 : true, 처리 : action.handle.value)
// 1.
configureAudioSession ()
// 2.
call.connectedStateChanged = {[약한 자기, 약한 호출] in
가드하자 강하다 셀프 = 자기, 전화 = 다른 통화 {돌아 가기}
if call.connectedState == .pending {
strongSelf.provider.reportOutgoingCall (with : call.uuid, startedConnectingAt : nil)
} else if call.connectedState == .complete {
strongSelf.provider.reportOutgoingCall (with : call.uuid, connectedAt : nil)
}
}
// 삼.
call.start {[약한 자기, 약한 호출] 성공
가드하자 강하다 셀프 = 자기, 전화 = 다른 통화 {돌아 가기}
성공 {
action.fulfill ()
strongSelf.callManager.add (call : call)
} else {
action.fail ()
}
}
}
공급자는 발신 요청이있을 때이 위임 메서드를 호출합니다.
- 통화 관리자에서 통화의 UUID로
Call
를 만든 후에는 앱의 오디오 세션을 구성해야합니다. 수신 통화와 마찬가지로이 시점에서 귀하의 책임은 구성 일뿐입니다. 실제 처리는 나중에provider(_:didActivate)
대리자 메서드가 호출 될 때 시작됩니다. - 대리인은 통화의 수명주기를 모니터링합니다. 처음에는 발신 전화가 연결되기 시작했다고보고합니다. 통화가 마지막으로 연결되면 공급자 대리인도이를보고합니다.
- 호출에서
start()
를 호출하면 라이프 사이클이 변경됩니다. 연결이 성공하면 통화가 완료된 것으로 표시 될 수 있습니다.
프로 바이더 대리자가 발신 통화를 처리 할 준비가되었으므로 이제 앱을 만드는 법을 가르쳐야 할 때입니다. :] CallManager.swift를 열고 다음 메소드를 클래스에 추가하십시오.
func startCall (handle : String, videoEnabled : Bool) {
// 1
let handle = CXHandle (type : .phoneNumber, value : handle)
// 2
let startCallAction = CXStartCallAction (call : UUID (), handle : handle)
// 삼
startCallAction.isVideo = videoEnabled
let transaction = CXTransaction (액션 : startCallAction)
requestTransaction (transaction)
}
이 메소드는 CXTransaction
"Start call"액션을 래핑하여 시스템에 요청합니다.
CXHandle
표시된 핸들은 핸들 유형과 그 값을 지정할 수 있습니다. 핫라인은 전화 번호 핸들을 지원하므로 여기서도 사용하게됩니다.CXStartCallAction
은 고유 한 UUID와 핸들을 입력으로받습니다.- 액션의
isVideo
속성을 설정하여 호출이 오디오 전용인지 또는 비디오 호출인지를 지정할 수 있습니다.
새 액션을 UI에 연결해야 할 때입니다. CallsViewController.swift를 열고 unwindForNewCall unwindForNewCall(_:)
의 이전 구현을 unwindForNewCall(_:)
다음과 일치시킵니다.
@IBAction 개인 기능 unwindForNewCall (_ segue : UIStoryboardSegue) {
newCallController = segue.source를! NewCallViewController
가드하자 handle = newCallController.handle else {return}
수신 = newCallController.incoming하자
let videoEnabled = newCallController.videoEnabled
들어오는 경우 {
let backgroundTaskIdentifier = UIApplication.shared.beginBackgroundTask (expirationHandler : nil)
DispatchQueue.main.asyncAfter (wallDeadline : DispatchWallTime.now () + 1.5) {
AppDelegate.shared.displayIncomingCall (uuid : UUID (), handle : handle, hasVideo : videoEnabled) {_ in
UIApplication.shared.endBackgroundTask (backgroundTaskIdentifier)
}
}
} else {
callManager.startCall (handle : handle, videoEnabled : videoEnabled)
}
}
코드에 미묘한 변화가 있습니다. incoming
것이 틀릴 때마다 View Controller는 호출 관리자에게 나가는 호출을 시작하도록 요청합니다.
그게 전화를하는 데 필요한 전부입니다. 테스트를 시작할 시간입니다! 기기에서 앱을 만들고 실행하십시오. 새 통화를 시작하려면 오른편 모서리의 더하기 버튼을 탭하고, 이번에는 분류 된 컨트롤에서 "발신"을 선택하십시오.
이 시점에서 새 통화가 목록에 표시되어야합니다. 통화의 현재 단계에 따라 다른 상태 라벨이 표시됩니다.
여러 통화 관리
핫라인 사용자가 여러 번 전화를받는 시나리오를 상상하기 쉽습니다. 처음에는 전화를 걸고 들어오는 전화를 걸고 들어오는 전화가 오기 전에 홈 버튼을 눌러이를 시뮬레이트 할 수 있습니다.이 시점에서 앱은 사용자에게 다음 화면을 표시합니다.
시스템은 사용자가 문제점을 해결하는 방법을 결정하도록합니다. 선택에 따라 여러 액션을 CXTransaction
합니다. 예를 들어 사용자가 진행중인 통화를 종료하고 새 통화에 응답하면 시스템은 전자 CXStartCallAction
대한 CXStartCallAction
과 전자 CXEndCallAction
대한 CXEndCallAction
을 만듭니다. 두 작업 모두 트랜잭션으로 래핑되고 공급자에게 전송되며, 공급자는이를 개별적으로 처리합니다. 따라서 앱이 이미 개별 요청을 수행하는 방법을 알고 있다면 추가 조치가 필요하지 않습니다.
위의 시나리오를 해결하여 테스트 할 수 있습니다. 통화 목록에는 선택 사항이 반영됩니다.앱은 한 번에 하나의 오디오 세션 만 처리합니다. 재개를 선택하면 다른 통화가 자동으로 보류됩니다.
전화 번호 디렉터리 확장 만들기
디렉토리 확장은 CallKit에서 제공하는 새로운 확장 점입니다. 그것은 귀하의 VoIP 애플 리케이션 수 있습니다 :
- 시스템의 차단 목록에 전화 번호를 추가하십시오.
- 수신 전화를 자신의 전화 번호 또는 전자 메일 주소와 같은 기타 고유 식별 정보로 식별합니다.
시스템이 전화를받을 때마다 주소록에서 일치 사항을 확인합니다. 디렉토리를 찾지 못하면 앱 별 디렉토리 확장을 체크인 할 수 있습니다. 핫라인에 디렉토리 확장을 추가하지 않으시겠습니까?
Xcode로 돌아가 File \ New \ Target ...으로 이동하여 Call Directory Extension을 선택 하십시오 . HotlineDirectory로 이름을 지정하고 마침을 클릭하십시오. Xcode는 자동으로 CallDirectoryHandler.swift 라는 새 파일을 만듭니다. 프로젝트 탐색기에서 찾아 내고 내용을 확인하십시오.
첫 번째 방법은 beginRequest(with:)
입니다. 이 메소드는 확장 기능이 초기화 될 때 호출됩니다. 오류가있는 경우 확장 프로그램은 cancelRequest(withError:)
호출하여 호스트 응용 프로그램에 확장 요청을 취소하도록 cancelRequest(withError:)
합니다. 앱 별 디렉토리를 구축하는 두 가지 다른 방법에 의존합니다.
addBlockingPhoneNumber(to:)
는 차단되어야하는 모든 전화 번호를 수집합니다. 구현을 다음으로 바꾸십시오.
private func addBlockingPhoneNumbers (컨텍스트 : CXCallDirectoryExtensionContext) throws {
전화 번호를 알려주십시오 : [CXCallDirectoryPhoneNumber] = [1234]
phoneNumber에 대한 전화 번호 {
context.addBlockingEntry (withNextSequentialPhoneNumber : phoneNumber)
}
}
주어진 전화 번호로 addBlockingEntry(withNextSequentialPhoneNumber:)
를 호출하면 차단 목록에 추가됩니다. 번호가 차단되면 시스템 전화 통신 공급자는 해당 번호의 전화를 표시하지 않습니다.
이제 addIdentificationPhoneNumbers(to:)
살펴보십시오. 메소드 본문을 아래 코드로 바꿉니다.
private func addIdentificationPhoneNumbers (컨텍스트 : CXCallDirectoryExtensionContext) throws {
전화 번호를 알려주십시오 : [CXCallDirectoryPhoneNumber] = [1111]
let labels = [ "RW 튜토리얼 팀"]
for (phoneNumber, label) for zip (전화 번호, 레이블) {
context.addIdentificationEntry (withNextSequentialPhoneNumber : phoneNumber, label : label)
}
}
지정된 전화 번호와 레이블로 addIdentificationEntry(withNextSequentialPhoneNumber:label:)
를 호출하면 새로운 식별 항목이 작성됩니다. 시스템이이 번호에서 전화를 수신 할 때마다 호출 UI는 일치하는 레이블을 사용자에게 표시합니다.
새 확장 프로그램을 테스트 할 차례입니다. 장치에서 핫라인 구성표를 작성하고 실행하십시오. 이 시점에서 귀하의 내선 번호가 아직 활성화되지 않았을 수 있습니다. 사용하려면 다음 단계를 수행하십시오.
- 설정 앱으로 이동합니다.
- 전화 선택
- 통화 차단 및 식별 선택
- 핫라인 사용
차단 된 통화를 테스트하는 것은 쉽습니다. 핫라인을 실행하고 번호 1234 에서 수신 전화를 시뮬레이트합니다. 시스템에서 아무 것도보고하지 않는다는 것을 알 수 있습니다. 사실, ProviderDelegate
에서 reportIncomingCall(uuid:handle:hasVideo:completion:)
구현에 중단 점을 넣으면 reportNewIncomingCall(withupdate:completion:)
이 오류를보고하는 경우가 있습니다.
전화 식별을 테스트하려면 핫라인을 다시 실행하고 새 통화를 시뮬레이션하십시오. 이번에는 번호 1111을 입력하십시오. 다음 호출 UI가 표시됩니다.
축하해! 제 1 자 VoIP 경험을 제공하기 위해 CallKit을 활용하는 앱을 만들었습니다! :]
어디로 갈 것인가?
이 자습서의 완성 된 프로젝트를 다운로드 할 수 있습니다.
CallKit에 대해 더 자세히 알고 싶으면 세션 230 (WWDC 2016)을 확인하십시오.
나는 당신이 CallKit 튜토리얼을 즐겼기를 바랍니다. 질문이나 의견이 있으시면 아래 포럼 토론에 참여하십시오!
코멘트