Implement VoIP calling with CallKit and PushKit. Use when building incoming/outgoing call flows, registering for VoIP push notifications, configuring CXProvider and CXCallController, handling call actions, coordinating audio sessions, or creating Call Directory extensions for caller ID and call blocking.
91%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Build VoIP calling experiences that integrate with the native iOS call UI using CallKit and PushKit. Covers incoming and outgoing call flows, PushKit VoIP registration, audio session coordination, and call directory extensions. Targets Swift 6.4 / iOS 26+.
Enable Voice over IP in Signing & Capabilities > Background Modes, and add the Push Notifications capability.
| Type | Role |
|---|---|
CXProvider | Reports incoming calls, connections, and external resets to the system; receives CXAction requests |
CXCallController | Requests user-initiated call actions (CXStartCallAction, CXEndCallAction, CXSetHeldCallAction) |
CXCallUpdate | Encapsulates call metadata (remote handle, caller name, video capability) |
CXProviderDelegate | Handles telephony events, user interactions (answer, end, hold, mute), and audio session activation |
PKPushRegistry | Registers for and receives PushKit VoIP push notifications |
[PushKit VoIP Push] ──> [reportNewIncomingCall] ──> [System Call UI]
│ (user answers)
▼
[WebRTC / VoIP Stack] <── [configureAudioSession] <── [CXAnswerCallAction]CXProvider instance alive across the app lifecycle. Recreating providers disconnects active calls.@MainActor to prevent race conditions during call state transitions.action.fulfill() or action.fail() promptly inside delegate methods. Unfulfilled actions cause UI timeouts.Every VoIP push received via pushRegistry(_:didReceiveIncomingPushWith:for:) must immediately report an incoming call to CallKit via reportNewIncomingCall(with:update:completion:).
[!WARNING] Failing to report a new incoming call upon receiving a VoIP push causes iOS to terminate the app and revoke future VoIP push credentials. On iOS 26.4+, inspect
PKVoIPPushMetadatato check if a push is exempt from reporting.
Do not activate the AVAudioSession during call initialization. Only configure categories in advance (.playAndRecord with .voiceChat mode). Activate the audio session only when the system invokes provider(_:didActivate:). Deactivate audio in provider(_:didDeactivate:).
reportNewIncomingCall, causing iOS to terminate the app.AVAudioSession before the system calls provider(_:didActivate:).action.fulfill() in CXProviderDelegate action handlers.CXProvider instances instead of keeping a single shared instance for the app lifecycle.CXProvider shared across the application lifetimereportNewIncomingCallAVAudioSession activated exclusively inside provider(_:didActivate:)CXAction delegate callbacks call fulfill() or fail() promptlyproviderDidReset(_:) is triggered.phoneNumber, .emailAddress, .generic) match backend identifiersreportOutgoingCall(with:startedConnectingAt:) and connectedAt:thienqo/ios-skills
6bf8b62
Canonical home
since May 23, 2026
Also appears in
on Oct 29, 2026
since May 22, 2026
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.