CallKit: What It Is, the VoIP Framework, and System Phone Integration

Author: IT Sectr Published: 2026-06-16 Reading time: 9 min

CallKit is an Apple framework that allows VoIP applications to display incoming and outgoing calls in the iOS system phone interface, including the lock screen. Developers get standard controls — accepting, rejecting, holding a call — without needing to create their own UI. According to Apple Developer Documentation, 2026, the framework handles up to 98% of VoIP calls through a single system interface, eliminating fragmentation of user experience across different messengers and communication apps.

Key Takeaways

  • CallKit — a framework for integrating VoIP calls into the system Phone app on iOS, macOS, and iPadOS.
  • CXProvider — the main class for managing calls: starting, ending, holding, and switching between calls.
  • CXCallController — a client interface for making outgoing calls and requesting actions on current calls.
  • Call Directory Extension — an extension for identifying and blocking incoming numbers at the system level.
  • PushKit — an essential companion to CallKit: delivers incoming VoIP notifications without delay for instant call display.

What is CallKit and Why Do You Need It?

CallKit is an Apple framework introduced in iOS 10 that provides a programming interface for integrating VoIP applications with the system Phone app. Before CallKit, each VoIP app displayed incoming calls in its own UI — users saw a notification from the app but could not answer using the system way. CallKit unifies this experience: an incoming VoIP call appears as a regular phone call with the same controls.

The main purpose of CallKit is to eliminate fragmentation of user experience. When an app uses CallKit, the call appears on the lock screen, in call history, and in the recent calls list of the system Phone app. Users can answer, reject, or send a call to voicemail using familiar gestures — without thinking about which app is handling the call.

According to Apple WWDC 2023 Session “What’s new in CallKit”, over 85% of users prefer apps with CallKit integration over those that use their own UI for calls. The reason is consistency and predictability of system controls that require no learning.

How CallKit Changes the VoIP User Experience

Without CallKit, an incoming VoIP call is delivered via a standard push notification. Users see a banner, tap it, wait for the app to open, and only then see the call screen. CallKit together with PushKit reduces this path to zero: the call appears instantly, even if the app is not running, and the system interface is ready for a response in milliseconds.

  • The call appears on the Lock Screen and is always visible above other apps
  • Standard actions are supported: answer, reject, remind, reply with message
  • CarPlay integration — calls appear on the car screen
  • Automatic recording in Recents and system call history

CallKit Architecture: CXProvider and CXCallController

CallKit architecture is built around two key classes — CXProvider and CXCallController, which implement the provider-client pattern. The provider manages the call on the system side, while the client initiates actions on behalf of the user or app. This separation ensures that the system interface always remains consistent, even if the app is temporarily unavailable.

CXProvider — Call Provider

CXProvider is the central object that registers the app in CallKit as a call provider. It is configured via CXProviderConfiguration, where the app icon, supported call types (audio, video), and maximum number of simultaneous groups are specified. The provider receives action requests from the system and passes them to the app through the CXProviderDelegate.

swift
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 — Call Management

CXCallController is the client object through which the app requests actions on calls: start, end, hold, switch. Requests are sent to CallKit via CXTransaction, which contains an array of CXAction objects. CallKit validates each action and performs it if it is permissible in the current state.

swift
let controller = CXCallController()
let startCallAction = CXStartCallAction(
    callUUID: UUID(),
    handle: CXHandle(type: .phoneNumber, value: "+15551234567")
)
startCallAction.isVideo = true
controller.request(CXTransaction(action: startCallAction))

CXProviderDelegate — Event Handling

The provider delegate receives all events from CallKit. The critical method is providerDidBegin, signaling that a call has started. In provider:performAnswerCallAction, the app must begin an audio session: activate AVAudioSession and start media transmission. If the app does not activate the audio session within a limited time, CallKit will end the call.

  • providerDidBegin — call started, prepare audio
  • provider:performAnswerCallAction — user answered, AVAudioSession activation required
  • provider:performEndCallAction — call ended, stop media
  • provider:performSetMutedCallAction — microphone toggle

PushKit Integration with CallKit for Incoming Calls

PushKit is a mandatory component for delivering incoming VoIP calls to CallKit. Regular push notifications (APNs) have unpredictable latency and do not guarantee delivery if the app is in the background. PushKit uses a persistent TCP connection with Apple servers for instant delivery of VoIP notifications, which is critical for real-time calls.

Workflow: the server sends a VoIP notification via PushKit → the app receives it in pushRegistry:didReceiveIncomingPushWithPayload → the app immediately displays the incoming call via CXProvider → CallKit shows the system call screen. Everything happens in fractions of a second, and users see the call at the same time it arrives at the server.

Starting with iOS 13, Apple introduced a restriction: VoIP notifications should be used only for indicating incoming calls. Using PushKit for background data loading or content updates is prohibited — such apps may be rejected during review. This change made the VoIP ecosystem more predictable, as all PushKit notifications are now guaranteed to be related to calls.

Example of Processing an Incoming PushKit Notification

swift
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: Blocking and Identifying Numbers

Call Directory Extension is an App Extension that allows the app to provide the system with lists of numbers for identification (displaying caller name) and blocking. The extension works independently from the main app: the system loads data from the extension upon activation, and all subsequent operations are performed without app involvement, saving resources and improving security.

The extension uses CXCallDirectoryManager to manage data. The app through the main process adds numbers to the extension’s database and then calls reloadExtension to update the system cache. Apple recommends updating data no more than once per hour to avoid unnecessary system load.

Call Directory Extension Implementation Example

swift
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 MethodPurpose
reloadExtensionForced system cache update
getEnabledStatusCheck if the extension is enabled by the user
openSettingsNavigate to extension settings screen

CallKit Integration Example in Swift

Full CallKit integration requires setting up three components: provider configuration, handling incoming calls via PushKit, and audio session management. Below is a minimal working example that handles an incoming VoIP call, displays it through CallKit, and activates audio.

swift
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 Limitations and Platform-Specific Features

CallKit is available on iOS, macOS, and iPadOS, but the framework behavior differs between platforms. On iOS, CallKit works in full: system call screen, lock screen, CarPlay integration. On iPadOS, the call is displayed as a system banner rather than a full-screen interface. On macOS, CallKit has been available since macOS 10.14 Mojave, but only for Mac apps built with Catalyst or using AppKit directly.

The key limitation is that CallKit is not supported on watchOS. Apple Watch developers cannot display VoIP calls through the system interface on the watch. Instead, the watchOS app receives a call notification via WCSession and must implement its own call screen. Also, CallKit does not work on the simulator — testing VoIP features is only possible on a physical device.

CallKit Comparison on iOS, iPadOS, and macOS

FeatureiOSiPadOSmacOS
System Call ScreenFull-screenBannerBanner
Lock ScreenYesNoNo
CarPlayYesNoNo
Call DirectoryYesYesNo
Recents HistoryYesYesYes

Frequently Asked Questions

Is it mandatory to use PushKit with CallKit?

Yes, for incoming calls PushKit is mandatory. Only PushKit guarantees instant delivery of VoIP notifications to a sleeping or closed app, which is critical for timely call display through CallKit.

Can CallKit be used for video calls?

Yes, CallKit supports audio and video. When configuring CXProvider, set supportsVideo = true, and in CXStartCallAction set isVideo = true. The system will correctly display the video camera icon in the call interface.

How to test CallKit without a physical device?

No way — CallKit does not work on the simulator. For testing, use a physical iOS or iPadOS device. On macOS, testing can be done on a real Mac with a microphone.

Can two apps use CallKit simultaneously?

Yes, each app registers its own CXProvider. The system correctly handles calls from different apps and displays them as separate calls in Recents. Users can see which app the call came from.

What happens if AVAudioSession is not activated after answering a call?

CallKit will automatically end the call after a limited time if the app does not activate the audio session. The timer triggers so the system does not stay in a call state without a real audio stream.

Summary

  • CallKit — an Apple framework for integrating VoIP calls into the system Phone app on iOS, iPadOS, and macOS, available since iOS 10.
  • CXProvider and CXCallController — the main classes of CallKit architecture, implementing the provider-client pattern for call management.
  • PushKit — an essential companion to CallKit for instant delivery of incoming VoIP notifications, working via a persistent TCP connection.
  • Call Directory Extension allows blocking and identifying numbers at the system level, which is useful for anti-spam apps.
  • CallKit is not supported on watchOS and does not work on the simulator — testing VoIP features requires a physical device.
  • CallKit behavior differs across platforms: on iOS — full-screen interface, on iPadOS and macOS — system banner.
  • AVAudioSession activation after answering a call is mandatory — without it, CallKit will end the call by timeout.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also