Display the system-calling UI for your app’s VoIP services, and coordinate your calling services with other apps and the system.
SDKs
- iOS 10.0+
- macOS 10.15+
- Mac Catalyst 13.0+
Overview
CallKit lets you integrate your calling services with other call-related apps on the system. CallKit provides the calling interface, and you handle the back-end communication with your VoIP service. For incoming and outgoing calls, CallKit displays the same interfaces as the Phone app, giving your app a more native look and feel. And CallKit responds appropriately to system-level behaviors such as Do Not Disturb.
In addition to handling calls, you can provide a Call Directory app extension to provide caller ID information and a list of blocked numbers associated with your service.
Receiving an Incoming Call
To configure your app to receive incoming calls, first create a CXProvider
object and store it for global access. An app reports an incoming call to the provider in response to an external notification, such as a VoIP push notification generated by PushKit.
Receiving a VoIP–related push notification
// MARK: PKPushRegistryDelegate
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, forType type: PKPushType) {
// report new incoming call
}
Note
For more information about VoIP push notifications and PushKit, see Voice Over IP (VoIP) Best Practices.
Using the information provided by the external notification, the app creates a UUID and a CXCall
object to uniquely identify the call and the caller, and passes them both to the provider using the report
method.
Handling an incoming call
if let uuidString = payload.dictionaryPayload["UUID"] as? String,
let identifier = payload.dictionaryPayload["identifier"] as? String,
let uuid = UUID(uuidString: uuidString)
{
let update = CXCallUpdate()
update.callerIdentifier = identifier
provider.reportNewIncomingCall(with: uuid, update: update) { error in
// …
}
}
After the call is connected, the system calls the provider(_:
method of the provider delegate. In your implementation, the delegate is responsible for configuring an AVAudio
and calling fulfill()
on the action when finished.
Initiating the audio for a call
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
// configure audio session
action.fulfill()
}
Making Outgoing Calls
A user can initiate an outgoing call with a VoIP app in any of the following ways:
Performing an interaction within the app
Opening a link with a supported custom URL scheme
Initiating a VoIP call using Siri
To make an outgoing call, an app requests a CXStart
object from its CXCall
object. The action consists of a UUID to uniquely identify the call and a CXHandle
object to specify the recipient.
Starting an outgoing call
let uuid = UUID()
let handle = CXHandle(type: .emailAddress, value: "jappleseed@apple.com")
let startCallAction = CXStartCallAction(call: uuid)
startCallAction.destination = handle
let transaction = CXTransaction(action: startCallAction)
callController.request(transaction) { error in
if let error = error {
print("Error requesting transaction: \(error)")
} else {
print("Requested transaction successfully")
}
}
Note
For more information about registering and handling URLs, see Using URL Schemes to Communicate with Apps.
For more information about initiating a call using Siri, see the INStart
protocol.
After the recipient answers the call, the system calls the provider delegate’s provider(_:
method. In your implementation of that method, configure an AVAudio
and call the fulfill()
method on the action object when finished.
Initiating the call
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
// configure audio session
action.fulfill()
}
Call Blocking and Identification
Apps can create a Call Directory app extension to identify and block incoming callers by their phone number.
Note
Phone numbers in a Call Directory extension are represented by the CXCall
type and consist of a country calling code (such as 1
for the United States) followed by a sequence of digits.
Creating a Call Directory App Extension
You can create a Call Directory extension for your containing app by adding a new project target and selecting the Call Directory Extension template under Application Extensions.

You set up both identification and blocking of incoming calls in the implementation of the begin
method of the CXCall
subclass of your Call Directory extension. This method is called when the system launches the app extension.
For more information about how app extensions work, see App Extension Programming Guide.
Identifying Incoming Callers
When a phone receives an incoming call, the system first consults the user’s contacts to find a matching phone number. If no match is found, the system then consults your app’s Call Directory extension to find a matching entry to identify the phone number. This is useful for applications that maintain a contact list for a user that’s separate from the system contacts, such as a social network, or for identifying incoming calls that may be initiated from within the app, such as for customer service support or a delivery notification.
For example, consider a user who is friends with Jane in a social networking app, but who doesn’t have her phone number in their contacts. The social networking app has a Call Directory app extension, which downloads and adds the phone numbers of all of the user’s friends. Because of this, when the user gets an incoming call from Jane, the system displays something like “(App Name) Caller ID: Jane Appleseed” rather than “Unknown Caller”.
To provide identifying information about incoming callers, you use the add
method in the implementation of begin
.
class CustomCallDirectoryProvider: CXCallDirectoryProvider {
override func beginRequest(with context: CXCallDirectoryExtensionContext) {
let labelsKeyedByPhoneNumber: [CXCallDirectoryPhoneNumber: String] = [ … ]
for (phoneNumber, label) in labelsKeyedByPhoneNumber.sorted(by: <) {
context.addIdentificationEntry(withNextSequentialPhoneNumber: phoneNumber, label: label)
}
context.completeRequest()
}
}
Because this method is called only when the system launches the app extension and not for each individual call, you must specify call identification information all at once; you cannot, for example, make a request to a web service to find information about an incoming call.
Blocking Incoming Calls
When a phone receives an incoming call, the system first consults the user’s block list to determine whether a call should be blocked. If the phone number is not on a user- or system-defined block list, the system then consults your app’s Call Directory extension to find a matching blocked number. This is useful for apps that, for example, maintain a database of known solicitors, or allow the user to block any numbers that match a set of criteria.
To block incoming calls for a particular phone number, you use the add
method in the implementation of begin
.
Note
You can specify that your Call Directory app extension add identification and/or block phone numbers in its implementation of begin
.
class CustomCallDirectoryProvider: CXCallDirectoryProvider {
override func beginRequest(with context: CXCallDirectoryExtensionContext) {
let blockedPhoneNumbers: [CXCallDirectoryPhoneNumber] = [ … ]
for phoneNumber in blockedPhoneNumbers.sorted(by: <) {
context.addBlockingEntry(withNextSequentialPhoneNumber: phoneNumber)
}
context.completeRequest()
}
}