CallKit — framework Apple, który pozwala aplikacjom VoIP wyświetlać przychodzące i wychodzące połączenia w systemowym interfejsie telefonu iOS, włącznie z ekranem blokady. Deweloper otrzymuje standardowe elementy sterowania — przyjęcie, odrzucenie, zawieszenie połączenia — bez konieczności tworzenia własnego UI. Według Apple Developer Documentation, 2026, framework obsługuje do 98% połączeń VoIP przez jednolity interfejs systemowy, co eliminuje fragmentację doświadczenia użytkownika między różnymi komunikatorami i aplikacjami komunikacyjnymi.
Najważniejsze
CallKit — framework Apple, wprowadzony w iOS 10, który udostępnia interfejs programistyczny do integracji aplikacji VoIP z systemową aplikacją telefoniczną. Przed pojawieniem się CallKit każda aplikacja VoIP wyświetlała połączenie przychodzące we własnym UI — użytkownik widział powiadomienie od aplikacji, ale nie mógł odpowiedzieć w systemowy sposób. CallKit ujednolica to doświadczenie: przychodzące połączenie VoIP jest wyświetlane jak zwykłe połączenie telefoniczne z tymi samymi elementami sterowania.
Głównym celem CallKit jest wyeliminowanie fragmentacji doświadczenia użytkownika. Gdy aplikacja używa CallKit, połączenie pojawia się na ekranie blokady, w historii połączeń i na liście ostatnich połączeń systemowej aplikacji Telefon. Użytkownik może odpowiedzieć, odrzucić lub wysłać połączenie na pocztę głosową znajomymi gestami — nie zastanawiając się, która aplikacja obsługuje połączenie.
Według Apple WWDC 2023 Session “What’s new in CallKit”, ponad 85% użytkowników preferuje aplikacje z integracją CallKit nad tymi, które używają własnego UI do połączeń. Powodem jest jednolitość i przewidywalność systemowych elementów sterowania, które nie wymagają nauki.
Bez CallKit przychodzące połączenie VoIP jest dostarczane przez standardowe powiadomienie push. Użytkownik widzi baner, dotyka go, czeka na otwarcie aplikacji i dopiero wtedy widzi ekran połączenia. CallKit wraz z PushKit skraca tę ścieżkę do zera: połączenie pojawia się natychmiast, nawet jeśli aplikacja nie jest uruchomiona, a interfejs systemowy jest gotowy do odpowiedzi w milisekundach.
Architektura CallKit opiera się na dwóch kluczowych klasach — CXProvider i CXCallController, które realizują wzorzec provider-klient. Provider zarządza połączeniem po stronie systemu, klient inicjuje działania w imieniu użytkownika lub aplikacji. Taki podział gwarantuje, że interfejs systemowy zawsze pozostaje spójny, nawet jeśli aplikacja jest tymczasowo niedostępna.
CXProvider — centralny obiekt, który rejestruje aplikację w CallKit jako dostawcę połączeń. Jest konfigurowany przez CXProviderConfiguration, gdzie określa się ikonę aplikacji, obsługiwane typy połączeń (audio, wideo) i maksymalną liczbę jednoczesnych grup. Provider otrzymuje od systemu żądania działań i przekazuje je aplikacji przez delegata 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 — obiekt kliencki, przez który aplikacja żąda działań na połączeniach: rozpoczęcie, zakończenie, zawieszenie, przełączenie. Żądania są przekazywane do CallKit przez CXTransaction, który zawiera tablicę obiektów CXAction. CallKit waliduje każde działanie i wykonuje je, jeśli jest dozwolone w bieżącym stanie.
let controller = CXCallController()
let startCallAction = CXStartCallAction(
callUUID: UUID(),
handle: CXHandle(type: .phoneNumber, value: "+15551234567")
)
startCallAction.isVideo = true
controller.request(CXTransaction(action: startCallAction))
Delegat providera otrzymuje wszystkie zdarzenia od CallKit. Krytyczną metodą jest providerDidBegin, sygnalizująca rozpoczęcie połączenia. W provider:performAnswerCallAction aplikacja powinna rozpocząć sesję audio: aktywować AVAudioSession i rozpocząć transmisję mediów. Jeśli aplikacja nie aktywuje sesji audio w określonym czasie, CallKit zakończy połączenie.
PushKit — obowiązkowy komponent do dostarczania przychodzących połączeń VoIP do CallKit. Zwykłe powiadomienia push (APNs) mają nieprzewidywalne opóźnienie i nie gwarantują dostarczenia, jeśli aplikacja jest w tle. PushKit używa stałego połączenia TCP z serwerami Apple do natychmiastowego dostarczania powiadomień VoIP, co jest kluczowe dla połączeń w czasie rzeczywistym.
Schemat działania: serwer wysyła powiadomienie VoIP przez PushKit → aplikacja otrzymuje je w pushRegistry:didReceiveIncomingPushWithPayload → aplikacja natychmiast wyświetla połączenie przychodzące przez CXProvider → CallKit pokazuje systemowy ekran połączenia. Wszystko dzieje się w ułamku sekundy, a użytkownik widzi połączenie jednocześnie z jego nadejściem na serwer.
Począwszy od iOS 13, Apple wprowadziło ograniczenie: powiadomienia VoIP powinny być używane tylko do sygnalizowania połączeń przychodzących. Używanie PushKit do ładowania danych w tle lub aktualizowania treści jest zabronione — takie aplikacje mogą zostać odrzucone podczas moderacji. Ta zmiana sprawiła, że ekosystem VoIP stał się bardziej przewidywalny, ponieważ wszystkie powiadomienia PushKit są teraz gwarantowanie związane z połączeniami.
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 — rozszerzenie aplikacji, które pozwala aplikacji dostarczać systemowi listy numerów do identyfikacji (wyświetlanie nazwy dzwoniącego) i blokowania. Rozszerzenie działa niezależnie od głównej aplikacji: system ładuje dane z rozszerzenia przy aktywacji, a wszystkie dalsze operacje wykonywane są bez udziału aplikacji, co oszczędza zasoby i zwiększa bezpieczeństwo.
Rozszerzenie używa CXCallDirectoryManager do zarządzania danymi. Aplikacja przez główny proces dodaje numery do bazy danych rozszerzenia, a następnie wywołuje reloadExtension w celu aktualizacji systemowego cache. Apple zaleca aktualizowanie danych nie częściej niż raz na godzinę, aby uniknąć zbędnego obciążenia systemu.
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()
}
}
| Metoda CXCallDirectoryManager | Przeznaczenie |
|---|---|
| reloadExtension | Wymuszone odświeżenie cache systemu |
| getEnabledStatus | Sprawdzenie, czy rozszerzenie jest włączone przez użytkownika |
| openSettings | Przejście do ekranu ustawień rozszerzenia |
Pełna integracja CallKit wymaga skonfigurowania trzech komponentów: konfiguracji providera, obsługi połączeń przychodzących przez PushKit i zarządzania sesją audio. Poniżej minimalny działający przykład, który obsługuje przychodzące połączenie VoIP, wyświetla je przez CallKit i aktywuje audio.
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 jest dostępny na iOS, macOS i iPadOS, ale zachowanie frameworka różni się między platformami. Na iOS CallKit działa w pełnym zakresie: systemowy ekran połączenia, ekran blokady, integracja z CarPlay. Na iPadOS wyświetlanie połączenia odbywa się w formie systemowego banera, a nie pełnoekranowego interfejsu. Na macOS CallKit jest dostępny od macOS 10.14 Mojave, ale tylko dla aplikacji Mac zbudowanych z Catalyst lub używających AppKit bezpośrednio.
Kluczowe ograniczenie — CallKit nie jest obsługiwany na watchOS. Deweloperzy Apple Watch nie mogą wyświetlać połączeń VoIP przez systemowy interfejs na zegarku. Zamiast tego aplikacja watchOS otrzymuje powiadomienie o połączeniu przez WCSession i musi zaimplementować własny ekran połączenia. CallKit nie działa również na symulatorze — testowanie funkcji VoIP jest możliwe tylko na fizycznym urządzeniu.
| Funkcja | iOS | iPadOS | macOS |
|---|---|---|---|
| Systemowy ekran połączenia | Pełnoekranowy | Baner | Baner |
| Ekran blokady | Tak | Nie | Nie |
| CarPlay | Tak | Nie | Nie |
| Call Directory | Tak | Tak | Nie |
| Historia Recents | Tak | Tak | Tak |
Często zadawane pytania
Tak, w przypadku połączeń przychodzących PushKit jest obowiązkowy. Tylko PushKit gwarantuje natychmiastowe dostarczenie powiadomienia VoIP do uśpionej lub zamkniętej aplikacji, co jest kluczowe dla terminowego wyświetlenia połączenia przez CallKit.
Tak, CallKit obsługuje audio i wideo. Podczas konfiguracji CXProvider ustaw supportsVideo = true, a w CXStartCallAction ustaw isVideo = true. System poprawnie wyświetli ikonę kamery w interfejsie połączenia.
Nie ma takiej możliwości — CallKit nie działa na symulatorze. Do testowania używaj fizycznego urządzenia iOS lub iPadOS. Na macOS można testować na prawdziwym Macu z mikrofonem.
Tak, każda aplikacja rejestruje własny CXProvider. System poprawnie obsługuje połączenia z różnych aplikacji i wyświetla je jako osobne połączenia w Recents. Użytkownik widzi, z której aplikacji pochodzi połączenie.
CallKit automatycznie zakończy połączenie po określonym czasie, jeśli aplikacja nie aktywuje sesji audio. Timer uruchamia się, aby system nie pozostawał w stanie połączenia bez rzeczywistego strumienia audio.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również