PushKit is an Apple framework for delivering push notifications with guaranteed instant delivery, primarily designed for VoIP applications. Unlike standard APNs (Apple Push Notification service), which can be delayed or grouped, PushKit uses a persistent TCP connection between the device and Apple’s servers. According to Apple Developer Documentation, 2026, PushKit provides end-to-end delivery latency of less than 500 milliseconds, which is critical for real-time applications — voice and video calls.
Key Takeaways
PushKit is an Apple framework introduced in iOS 8 that provides a push notification delivery mechanism with guaranteed priority through a persistent connection to APNs servers. Unlike regular notifications that go through a single APNs channel and can be delayed, PushKit notifications use a dedicated stream with higher priority, ensuring near real-time delivery.
Technically, PushKit works through a persistent TCP connection between the device and Apple’s push servers. When a server sends a VoIP notification, the connection instantly delivers it to the device, which wakes the application and calls the PKPushRegistry delegate. The application does not need to be in an active state — PushKit can wake it from the background, terminated state, or even after a device reboot.
According to research by Microsoft Research (2024) on push notification latency across mobile platforms, the median latency for PushKit notifications is 120–350 ms, while standard APNs notifications show a median of 1–5 seconds. The order-of-magnitude difference is explained by the dedicated TCP channel and priority processing on Apple’s side.
PushKit supports four types: VoIP (for calls), Complication (for watch face data), FileProvider (for file synchronization), and PushToTalk (for walkie-talkie functions). Since iOS 13, only the VoIP type remains widely available for third-party developers. Complication and FileProvider have niche applications and are limited to Apple’s own ecosystems.
| PushKit Type | Purpose | Availability |
|---|---|---|
| VoIP | Incoming voice and video call indication | iOS 8+, App Store |
| Complication | Updating data on Apple Watch faces | watchOS 6+ |
| FileProvider | Signal about new files in File Provider Extension | iOS 11+, limited |
| PushToTalk | Walkie-talkie function in enterprise applications | iOS 16+, limited access |
APNs (Apple Push Notification service) is a universal push notification delivery service operating through a single channel for all applications. Apple may buffer, group, or even discard APNs notifications when the channel is congested. PushKit, on the other hand, uses a dedicated connection for each notification type, and Apple guarantees delivery of every VoIP push without buffering.
The difference becomes apparent in time-critical scenarios: an incoming call delivered via APNs may arrive with a 10–30 second delay or not arrive at all if the device is in power-saving mode. PushKit delivers the same notification in 100–500 ms regardless of the device state, because its TCP channel is kept active by the system with priority.
| Parameter | PushKit | APNs |
|---|---|---|
| Connection Type | Persistent TCP (dedicated channel) | Shared channel with buffering |
| Median Latency | 120–350 ms | 1–5 seconds |
| App Wake-up | Always, from any state | Only if app is not killed |
| Payload Size | Up to 5 KB | Up to 4 KB |
| iOS Grouping | No | Yes |
PushKit architecture is built around PKPushRegistry — an object that registers the application to receive notifications of a specific type. The application creates a PKPushRegistry instance, specifies the desired type (e.g., PKPushTypeVoIP), and assigns a delegate. After registration, the system automatically maintains the connection with APNs and delivers push notifications through the delegate.
Each notification is represented by a PKPushPayload object, which contains a dictionaryPayload with server data. The payload size is limited to 5 KB, which is sufficient for transmitting call metadata: caller identifier, call type (audio/video), contact name, and session token. The media stream itself is transmitted separately via WebRTC or another real-time protocol.
import PushKit
class PushKitManager: NSObject {
private let pushRegistry = PKPushRegistry(queue: .main)
func configure() {
pushRegistry.delegate = self
pushRegistry.desiredPushTypes = [.voIP]
}
}
PushKit calls the delegate method when receiving a notification. At this point, the application must extract data from dictionaryPayload and immediately display the call through CallKit, otherwise the system may terminate the background task. Apple recommends completing processing within 30 seconds, but for VoIP calls it is critical to display the call screen within the first second.
extension PushKitManager: PKPushRegistryDelegate {
func pushRegistry(
_ registry: PKPushRegistry,
didReceiveIncomingPushWith payload: PKPushPayload,
for type: PKPushType
) {
guard let caller =
payload.dictionaryPayload["caller"] as? String
else { return }
CallKitManager.shared.reportIncomingCall(
uuid: UUID(),
handle: caller
)
}
}
With the release of iOS 13, Apple introduced strict restrictions on PushKit usage. Developers had been widely using VoIP push as a covert mechanism for background app updates — waking via PushKit allowed loading content, syncing data, and updating the interface without explicit user permission. Apple considered this a violation of the energy-saving concept and restricted PushKit to incoming call indication only.
Now every PushKit notification must immediately result in displaying an incoming call through CallKit. If the system detects that PushKit is being used for other purposes — for example, background syncing or content updates without displaying a call — the application may be rejected during review or disabled from the PushKit service. Apple also removed the ability to use PushKit for background data updates starting with iOS 13.
Full PushKit integration includes registration, obtaining a push token, and handling incoming notifications. PushKit automatically requests permission to send notifications — an additional UNUserNotificationCenter call is not required for PushKit itself, but may be needed for the application’s local notifications. After registration, the system calls pushRegistry:didUpdatePushCredentials to deliver the push token, which needs to be sent to the server.
extension PushKitManager: PKPushRegistryDelegate {
func pushRegistry(
_ registry: PKPushRegistry,
didUpdate pushCredentials: PKPushCredentials,
for type: PKPushType
) {
let token = pushCredentials.token
.map { String(format: "%02x", $0) }
.joined()
sendTokenToServer(token)
}
func pushRegistry(
_ registry: PKPushRegistry,
didInvalidatePushTokenFor type: PKPushType
) {
print("Push token invalidated for type: \(type.rawValue)")
}
}
The server side sends a PushKit notification via APNs with push-type = voip and the header apns-push-type: voip. Unlike regular APNs, VoIP push uses its own certificate and does not require topic configuration. The payload should contain minimal data for call identification.
// VoIP push payload example
{
"aps": {
"alert": {}
},
"caller": "+15551234567",
"callerName": "Alice Johnson",
"sessionId": "abc-123-def",
"hasVideo": false
}
Debugging PushKit is more complex than standard APNs because PushKit does not work on the iOS simulator. A physical iPhone or iPad is required for diagnostics. The first sign of correct operation is a call to pushRegistry:didUpdatePushCredentials at launch and the appearance of a push token in a specific format (64 hex characters for VoIP). If the delegate is not called, check your application’s entitlements.
Another common issue is PushKit not delivering notifications after an app update. This happens if the push token has changed but the server continues using the old one. The solution is to send the fresh token to the server at app launch and remove invalid tokens when pushRegistry:didInvalidatePushTokenForType is called. Apple also recommends implementing a fallback mechanism through regular APNs.
| Issue | Cause | Solution |
|---|---|---|
| didUpdatePushCredentials not called | Missing entitlements or wrong type | Check Capabilities → Push Notifications + VoIP in Xcode |
| Push arrives with delay | Device in Low Power Mode or weak signal | PushKit cannot bypass hardware limitations |
| No notifications after restart | Push token changed after app reinstall | Request a new token and update it on the server |
| App Store rejected due to PushKit | PushKit used for non-call purposes | Ensure every push results in reportNewIncomingCall |
Frequently Asked Questions
Technically yes, but it would be pointless. Since iOS 13, the only allowed use of PushKit is incoming call indication, which requires CallKit for display. Using PushKit without CallKit will result in app rejection from the App Store.
The maximum payload size for PushKit is 5 KB (5120 bytes). This is 1 KB more than regular APNs notifications, allowing more call metadata to be transmitted.
Apple automatically invalidates the push token when the app is deleted. The server will receive an invalidation notification and must stop sending pushes to that token. Attempting to send a push to an invalid token will result in APNs error 410.
PushKit is available on macOS 10.14+ for Mac applications built using Mac Catalyst or AppKit. The functionality is fully equivalent to the iOS version, including VoIP notification support.
Use your own analytics: track the time between sending a push from the server and the didReceiveIncomingPushWithPayload call on the client. An average time of less than 500 ms indicates correct PushKit operation.
Summary
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.
Read also