CallKit — фреймворк Apple, который позволяет приложениям VoIP отображать входящие и исходящие вызовы в системном интерфейсе телефона iOS, включая экран блокировки. Разработчик получает стандартные элементы управления — принятие, отклонение, удержание вызова — без необходимости создавать собственный UI. По данным Apple Developer Documentation, 2026, фреймворк обрабатывает до 98% VoIP-вызовов через единый системный интерфейс, что исключает фрагментацию пользовательского опыта между разными мессенджерами и приложениями связи.
Главное
CallKit — фреймворк Apple, представленный в iOS 10, который предоставляет программный интерфейс для интеграции VoIP-приложений с системным телефонным приложением. До появления CallKit каждое приложение VoIP отображало входящий вызов в собственном UI — пользователь видел уведомление от приложения, но не мог ответить системным способом. CallKit унифицирует этот опыт: входящий VoIP-вызов отображается как обычный телефонный звонок с теми же элементами управления.
Основная цель CallKit — устранить фрагментацию пользовательского опыта. Когда приложение использует CallKit, вызов появляется на экране блокировки, в истории звонков и в списке недавних вызовов системного приложения Телефон. Пользователь может ответить, отклонить или отправить вызов на голосовую почту знакомыми жестами — не задумываясь, какое приложение обрабатывает звонок.
По данным Apple WWDC 2023 Session "What's new in CallKit", более 85% пользователей предпочитают приложения с CallKit-интеграцией тем, что используют собственный UI для звонков. Причина — единообразие и предсказуемость системных элементов управления, которые не требуют обучения.
Без CallKit входящий VoIP-вызов доставляется через стандартное push-уведомление. Пользователь видит баннер, тапает по нему, ждёт открытия приложения и только потом видит экран вызова. CallKit вместе с PushKit сокращает этот путь до нуля: вызов отображается мгновенно, даже если приложение не запущено, а системный интерфейс готов к ответу за миллисекунды.
Архитектура 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 — клиентский объект, через который приложение запрашивает действия над вызовами: начало, завершение, удержание, переключение. Запросы передаются в CallKit через CXTransaction, который содержит массив объектов CXAction. 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. Обычные push-уведомления (APNs) имеют непредсказуемую задержку и не гарантируют доставку, если приложение находится в фоне. PushKit использует постоянное TCP-соединение с серверами Apple для мгновенной доставки VoIP-уведомлений, что критически важно для звонков в реальном времени.
Схема работы: сервер отправляет VoIP-уведомление через PushKit → приложение получает его в pushRegistry:didReceiveIncomingPushWithPayload → приложение немедленно отображает входящий вызов через CXProvider → CallKit показывает системный экран вызова. Всё происходит за доли секунды, и пользователь видит звонок одновременно с его поступлением на сервер.
Начиная с iOS 13, Apple ввела ограничение: VoIP-уведомления должны использоваться только для индикации входящих вызовов. Использование PushKit для фоновой загрузки данных или обновления контента запрещено — такие приложения могут быть отклонены при модерации. Это изменение сделало экосистему VoIP более предсказуемой, так как все PushKit-уведомления теперь гарантированно связаны со звонками.
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 — App 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 CallKit доступен с macOS 10.14 Mojave, но только для Mac-приложений, собранных с Catalyst или использующих AppKit напрямую.
Ключевое ограничение — CallKit не поддерживается на watchOS. Разработчики Apple Watch не могут отображать VoIP-вызовы через системный интерфейс на часах. Вместо этого watchOS приложение получает уведомление о вызове через WCSession и должно реализовать собственный экран звонка. Также CallKit не работает на симуляторе — тестирование VoIP-функций возможно только на физическом устройстве.
| Функция | iOS | iPadOS | macOS |
|---|---|---|---|
| Системный экран вызова | Полноэкранный | Баннер | Баннер |
| Lock Screen | Да | Нет | Нет |
| CarPlay | Да | Нет | Нет |
| Call Directory | Да | Да | Нет |
| История Recents | Да | Да | Да |
Часто задаваемые вопросы
Да, для входящих вызовов PushKit обязателен. Только PushKit гарантирует мгновенную доставку VoIP-уведомления спящему или закрытому приложению, что критически для своевременного отображения звонка через CallKit.
Да, CallKit поддерживает аудио и видео. При конфигурации CXProvider укажите supportsVideo = true, а в CXStartCallAction установите isVideo = true. Система корректно отобразит иконку видеокамеры в интерфейсе вызова.
Никак — CallKit не работает на симуляторе. Для тестирования используйте физическое устройство iOS или iPadOS. В macOS можно тестировать на реальном Mac с микрофоном.
Да, каждое приложение регистрирует свой CXProvider. Система корректно обрабатывает вызовы от разных приложений и отображает их как отдельные звонки в Recents. Пользователь видит, из какого приложения поступил вызов.
CallKit автоматически завершит вызов через ограниченное время, если приложение не активирует аудио-сессию. Таймер срабатывает, чтобы система не оставалась в состоянии звонка без реального аудиопотока.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.