Mastering iOS VoIP: Implement CallKit & PushKit with Real‑World Tips
This guide walks through building a complete iOS VoIP solution using CallKit and PushKit, covering UI differences, step‑by‑step configuration of CXProvider and PKPushRegistry, handling incoming calls, common pitfalls such as lock‑screen crashes and token issues, and extending functionality with Call Directory extensions.
Overview
Apple introduced CallKit in iOS 10 and PushKit in iOS 8, together providing a complete VoIP solution. This article records the problems encountered while implementing VoIP and explains the developer’s understanding of both frameworks.
UI Differences
CallKit uses the system‑provided call UI. The screenshots show the iOS 10 style (left two images) and the iOS 11 style (right image).
CallKit Implementation
The core classes are CXProvider, CXCallController and CXProviderConfiguration. A custom manager class must adopt CXProviderDelegate. The implementation steps are:
Configure CXProviderConfiguration (localized name, video support, call limits, handle types, icon images, etc.).
Initialize CXProvider and CXCallController, set the delegate, and assign a dispatch queue.
Implement the callback methods provider:performStartCallAction:, provider:performAnswerCallAction:, provider:performEndCallAction:, etc., and always call [action fulfill] to avoid call‑failure errors.
Report incoming calls with reportNewIncomingCallWithUUID:update:completion:, creating a CXCallUpdate that sets properties such as supportsDTMF, supportsHolding, localizedCallerName, and a generic CXHandle containing the session identifier.
End a call by creating a CXEndCallAction, adding it to a CXTransaction, and requesting the transaction from the CXCallController.
PushKit Implementation
PushKit provides VoIP‑type push notifications that can wake a terminated app. The implementation consists of three steps:
Register a PKPushRegistry for PKPushTypeVoIP (usually in application:didFinishLaunchingWithOptions:) and set its delegate.
Implement pushRegistry:didUpdatePushCredentials:forType: to extract the token, strip the “<…>” characters and spaces, and upload it to the server.
Implement pushRegistry:didReceiveIncomingPushWithPayload:forType: to parse the payload, extract the alert field, and forward the call to CallKit.
Common Pitfalls and Solutions
Lock‑screen crash: Receiving a VoIP push while the device is locked may trigger a watchdog timeout (termination reason SPRINGBOARD). Reduce work in didFinishLaunching and avoid VoIP on low‑end armv7 devices.
Missing VoIP token: Enable the three background modes (Background fetch, Remote notifications, Voice over IP) and Push Notifications in the Xcode capabilities.
Call‑record click handling: Use application:continueUserActivity:restorationHandler: and inspect userActivity.activityType (e.g., INStartAudioCallIntent or INStartVideoCallIntent).
Audio session: Set AVAudioSessionCategoryPlayAndRecord in performAnswerCallAction for both parties; restore the session when the call ends.
Facetime button: The button cannot be hidden, but setting supportsVideo to true changes it to a custom video button that launches the app.
Custom button icons: Use an alpha‑channel PNG for iconMask to avoid a solid white square.
Extension example: Call Directory extensions can block spam numbers by loading a sorted list of phone numbers (with country code “86” for China) from a shared app‑group container.
Version Compatibility
PushKit is available from iOS 8, CallKit from iOS 10. For earlier versions the app falls back to APNS. The server checks whether a user has a VoIP token; if not, it sends a normal APNS payload. The client also guards calls with @available(iOS 10, *) checks.
Certificates
VoIP certificates are created in the Apple Developer portal the same way as APNS certificates. A single certificate works for both development and production; the bundle identifier must match the APNS bundle ID.
References
CallKit Documentation: https://developer.apple.com/reference/callkit
PushKit Documentation: https://developer.apple.com/documentation/pushkit?language=objc
Speakerbox Sample: https://developer.apple.com/library/prerelease/content/samplecode/Speakerbox/Introduction/Intro.html
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
