CallKit은 Apple 프레임워크로, VoIP 애플리케이션이 iOS 시스템 전화 인터페이스에서 수신 및 발신 통화를 표시할 수 있도록 합니다. 잠금 화면도 포함됩니다. 개발자는 자체 UI를 만들 필요 없이 표준 제어(수락, 거부, 통화 보류)를 얻을 수 있습니다. Apple Developer Documentation, 2026에 따르면, 이 프레임워크는 단일 시스템 인터페이스를 통해 최대 98%의 VoIP 통화를 처리하여 다양한 메신저 및 통신 앱 간의 사용자 경험 파편화를 제거합니다.
주요 포인트
CallKit은 iOS 10에서 도입된 Apple 프레임워크로, VoIP 애플리케이션을 시스템 전화 앱과 통합하기 위한 프로그래밍 인터페이스를 제공합니다. CallKit 이전에는 각 VoIP 앱이 자체 UI에 수신 통화를 표시했습니다. 사용자는 앱에서 알림을 보았지만 시스템 방식으로 응답할 수 없었습니다. CallKit은 이 경험을 통합합니다: 수신 VoIP 통화가 동일한 제어 기능을 가진 일반 전화 통화로 표시됩니다.
CallKit의 주요 목적은 사용자 경험의 파편화를 제거하는 것입니다. 앱이 CallKit을 사용하면 통화가 잠금 화면, 통화 기록 및 시스템 전화 앱의 최근 통화 목록에 표시됩니다. 사용자는 익숙한 제스처로 통화에 응답, 거부 또는 음성 메일로 보낼 수 있습니다. 어떤 앱이 통화를 처리하고 있는지 생각할 필요가 없습니다.
Apple WWDC 2023 세션 “What’s new in CallKit”에 따르면, 85% 이상의 사용자가 통화에 자체 UI를 사용하는 앱보다 CallKit 통합 앱을 선호합니다. 그 이유는 학습이 필요 없는 시스템 제어의 일관성과 예측 가능성 때문입니다.
CallKit이 없으면 수신 VoIP 통화는 표준 푸시 알림을 통해 전달됩니다. 사용자는 배너를 보고, 탭하고, 앱이 열릴 때까지 기다린 후에야 통화 화면을 봅니다. CallKit은 PushKit과 함께 이 경로를 0으로 줄입니다: 앱이 실행 중이 아니어도 통화가 즉시 표시되며 시스템 인터페이스는 밀리초 단위로 응답할 준비가 됩니다.
CallKit 아키텍처는 두 가지 주요 클래스 — CXProvider와 CXCallController — 를 중심으로 구축되며, 공급자-클라이언트 패턴을 구현합니다. 공급자는 시스템 측에서 통화를 관리하고, 클라이언트는 사용자 또는 앱을 대신하여 작업을 시작합니다. 이 분리를 통해 앱을 일시적으로 사용할 수 없어도 시스템 인터페이스가 항상 일관되게 유지됩니다.
CXProvider는 CallKit에 앱을 통화 공급자로 등록하는 중앙 객체입니다. CXProviderConfiguration을 통해 구성되며, 앱 아이콘, 지원되는 통화 유형(오디오, 비디오) 및 최대 동시 그룹 수가 지정됩니다. 공급자는 시스템으로부터 작업 요청을 받아 CXProviderDelegate를 통해 앱에 전달합니다.
let configuration = CXProviderConfiguration(localizedName: "My VoIP App")
configuration.supportedHandleTypes = [.phoneNumber, .generic]
configuration.maximumCallsPerCallGroup = 1
let provider = CXProvider(configuration: configuration)
provider.setDelegate(self, queue: .main)
CXCallController는 앱이 통화에 대한 작업(시작, 종료, 보류, 전환)을 요청하는 클라이언트 객체입니다. 요청은 CXAction 객체의 배열을 포함하는 CXTransaction을 통해 CallKit으로 전송됩니다. CallKit은 각 작업의 유효성을 검사하고 현재 상태에서 허용되는 경우 실행합니다.
let controller = CXCallController()
let startCallAction = CXStartCallAction(
callUUID: UUID(),
handle: CXHandle(type: .phoneNumber, value: "+15551234567")
)
startCallAction.isVideo = true
controller.request(CXTransaction(action: startCallAction))
공급자 델리게이트는 CallKit으로부터 모든 이벤트를 받습니다. 중요한 메서드는 providerDidBegin으로, 통화가 시작되었음을 알립니다. provider:performAnswerCallAction에서 앱은 오디오 세션을 시작해야 합니다: AVAudioSession을 활성화하고 미디어 전송을 시작합니다. 앱이 제한된 시간 내에 오디오 세션을 활성화하지 않으면 CallKit이 통화를 종료합니다.
PushKit은 수신 VoIP 통화를 CallKit에 전달하기 위한 필수 구성 요소입니다. 일반 푸시 알림(APNs)은 예측할 수 없는 지연 시간이 있으며 앱이 백그라운드에 있을 때 전달을 보장하지 않습니다. PushKit은 Apple 서버와의 영구 TCP 연결을 사용하여 VoIP 알림을 즉시 전달하며, 이는 실시간 통화에 중요합니다.
워크플로우: 서버가 PushKit을 통해 VoIP 알림 전송 → 앱이 pushRegistry:didReceiveIncomingPushWithPayload에서 수신 → 앱이 CXProvider를 통해 즉시 수신 통화 표시 → CallKit이 시스템 통화 화면 표시. 모든 것이 순식간에 발생하며 사용자는 서버에 도착하는 동시에 통화를 볼 수 있습니다.
iOS 13부터 Apple은 제한을 도입했습니다: VoIP 알림은 수신 통화 표시에만 사용해야 합니다. 백그라운드 데이터 로딩이나 콘텐츠 업데이트에 PushKit을 사용하는 것은 금지되며, 이러한 앱은 검토에서 거부될 수 있습니다. 이 변경으로 모든 PushKit 알림이 통화와 관련됨이 보장되어 VoIP 생태계가 더 예측 가능해졌습니다.
func pushRegistry(
_ registry: PKPushRegistry,
didReceiveIncomingPushWith payload: PKPushPayload,
for type: PKPushType
) {
let uuid = UUID()
let update = CXCallUpdate()
update.remoteHandle = CXHandle(
type: .phoneNumber,
value: payload.dictionaryPayload["caller"]
)
update.hasVideo = false
provider.reportNewIncomingCall(with: uuid, update: update)
}
Call Directory Extension은 앱이 시스템에 식별(발신자 이름 표시) 및 차단을 위한 번호 목록을 제공할 수 있도록 하는 앱 확장 기능입니다. 확장 기능은 기본 앱과 독립적으로 작동합니다: 시스템은 활성화 시 확장 기능에서 데이터를 로드하고 이후 모든 작업은 앱의 개입 없이 수행되어 리소스를 절약하고 보안을 향상시킵니다.
확장 기능은 CXCallDirectoryManager를 사용하여 데이터를 관리합니다. 앱은 기본 프로세스를 통해 확장 기능의 데이터베이스에 번호를 추가한 다음 reloadExtension을 호출하여 시스템 캐시를 업데이트합니다. Apple은 불필요한 시스템 부하를 방지하기 위해 한 시간에 한 번 이상 데이터를 업데이트하지 않도록 권장합니다.
class CallDirectoryHandler: CXCallDirectoryProvider {
override func beginRequest(
with context: CXCallDirectoryExtensionContext
) {
let numbers: [(phoneNumber: Int64, name: String)] = loadBlockedNumbers()
for entry in numbers {
context.addIdentificationEntry(
withNextSequentialPhoneNumber: entry.phoneNumber,
label: entry.name
)
}
context.completeRequest()
}
}
| CXCallDirectoryManager 메서드 | 목적 |
|---|---|
| reloadExtension | 시스템 캐시 강제 업데이트 |
| getEnabledStatus | 확장 기능이 사용자에 의해 활성화되었는지 확인 |
| openSettings | 확장 기능 설정 화면으로 이동 |
완전한 CallKit 통합에는 세 가지 구성 요소의 설정이 필요합니다: 공급자 구성, PushKit을 통한 수신 통화 처리, 오디오 세션 관리. 다음은 수신 VoIP 통화를 처리하고 CallKit을 통해 표시하며 오디오를 활성화하는 최소한의 작동 예제입니다.
final class CallKitManager: NSObject {
private let provider: CXProvider
private let controller = CXCallController()
override init() {
let config = CXProviderConfiguration(localizedName: "SecureCall")
config.supportedHandleTypes = [.phoneNumber]
config.maximumCallGroups = 1
self.provider = CXProvider(configuration: config)
super.init()
provider.setDelegate(self, queue: .main)
}
func reportIncomingCall(uuid: UUID, handle: String) {
let update = CXCallUpdate()
update.remoteHandle = CXHandle(type: .phoneNumber, value: handle)
provider.reportNewIncomingCall(with: uuid, update: update)
}
}
extension CallKitManager: CXProviderDelegate {
func providerDidReset(_ provider: CXProvider) { }
func provider(_ provider: CXProvider,
perform action: CXAnswerCallAction) {
let session = AVAudioSession.sharedInstance()
try? session.setCategory(.playAndRecord)
try? session.setActive(true)
action.fulfill()
}
}
CallKit은 iOS, macOS 및 iPadOS에서 사용할 수 있지만 프레임워크의 동작은 플랫폼 간에 다릅니다. iOS에서 CallKit은 완전히 작동합니다: 시스템 통화 화면, 잠금 화면, CarPlay 통합. iPadOS에서는 통화가 전체 화면 인터페이스 대신 시스템 배너로 표시됩니다. macOS에서는 macOS 10.14 Mojave 이후 CallKit을 사용할 수 있지만 Catalyst로 빌드되거나 AppKit을 직접 사용하는 Mac 앱에 한합니다.
주요 제한 사항 — CallKit은 watchOS에서 지원되지 않습니다. Apple Watch 개발자는 시계의 시스템 인터페이스를 통해 VoIP 통화를 표시할 수 없습니다. 대신 watchOS 앱은 WCSession을 통해 통화 알림을 받고 자체 통화 화면을 구현해야 합니다. 또한 CallKit은 시뮬레이터에서 작동하지 않습니다 — VoIP 기능 테스트는 물리적 기기에서만 가능합니다.
| 기능 | iOS | iPadOS | macOS |
|---|---|---|---|
| 시스템 통화 화면 | 전체 화면 | 배너 | 배너 |
| 잠금 화면 | 예 | 아니오 | 아니오 |
| CarPlay | 예 | 아니오 | 아니오 |
| 통화 디렉토리 | 예 | 예 | 아니오 |
| 최근 기록 | 예 | 예 | 예 |
자주 묻는 질문
예, 수신 통화의 경우 PushKit이 필수입니다. PushKit만이 대기 중이거나 종료된 앱에 VoIP 알림의 즉각적인 전달을 보장하며, CallKit을 통한 적시 통화 표시에 중요합니다.
예, CallKit은 오디오 및 비디오를 지원합니다. CXProvider 구성 시 supportsVideo = true로 설정하고 CXStartCallAction에서 isVideo = true로 설정합니다. 시스템은 통화 인터페이스에 비디오 카메라 아이콘을 올바르게 표시합니다.
방법이 없습니다 — CallKit은 시뮬레이터에서 작동하지 않습니다. 테스트에는 물리적 iOS 또는 iPadOS 기기를 사용하십시오. macOS에서는 마이크가 있는 실제 Mac에서 테스트할 수 있습니다.
예, 각 앱은 자체 CXProvider를 등록합니다. 시스템은 다른 앱의 통화를 올바르게 처리하고 최근 통화에 별도의 통화로 표시합니다. 사용자는 어떤 앱에서 통화가 왔는지 확인할 수 있습니다.
앱이 제한된 시간 내에 오디오 세션을 활성화하지 않으면 CallKit이 자동으로 통화를 종료합니다. 실제 오디오 스트림 없이 시스템이 통화 상태로 남아 있지 않도록 타이머가 작동합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.