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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.