Building iOS VoIP Apps with Swift and CallKit: Complete Guide
How to build a production iOS VoIP app in Swift: CallKit integration, PushKit for background VoIP calls, PJSIP/linphone SDK setup, audio session management, and App Store submission for VoIP apps.
Building iOS VoIP Apps with Swift and CallKit: Complete Guide
How to build a production iOS VoIP app in Swift: CallKit integration, PushKit for background VoIP calls, PJSIP/linphone SDK setup, audio session management, and App Store submission for VoIP apps.
Kaushik Parmar
Founder & VoIP Architect, CelloIP Technologies
Why Native Swift for iOS VoIP?
Native Swift development gives the deepest access to iOS APIs: CallKit for system-level call UI, PushKit for reliable background wakeup, AVAudioEngine for professional audio processing, and direct PJSIP/linphone SDK integration without abstraction layers. For production VoIP apps, native Swift delivers the best call quality, the most reliable background behaviour, and the cleanest App Store review path — especially for enterprise apps handling SRTP encrypted calls.
CallKit Integration in Swift
CallKit lets your VoIP app show native iOS call screens, appear in the Recents list, and integrate with Siri, CarPlay, and Bluetooth headsets. You implement CXProvider (to report calls to the system) and CXCallController (to request call actions). When an incoming call arrives via PushKit push, you call reportNewIncomingCall() on CXProvider before the push expires. When the user answers, CallKit calls provider(_:perform:) with CXAnswerCallAction — this is where you answer the SIP call in PJSIP.
AppDelegate.swift — CallKit provider setup
import CallKit
import PushKit
class CallProvider: NSObject, CXProviderDelegate {
let provider: CXProvider
let callController = CXCallController()
override init() {
let config = CXProviderConfiguration()
config.supportsVideo = false
config.maximumCallsPerCallGroup = 1
config.supportedHandleTypes = [.phoneNumber, .generic]
self.provider = CXProvider(configuration: config)
super.init()
provider.setDelegate(self, queue: nil)
}
func reportIncomingCall(uuid: UUID, handle: String, displayName: String) {
let update = CXCallUpdate()
update.remoteHandle = CXHandle(type: .generic, value: handle)
update.localizedCallerName = displayName
provider.reportNewIncomingCall(with: uuid, update: update) { error in
if let error = error { print("CallKit error: \(error)") }
}
}
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
// Answer the SIP call here via your PJSIP bridge
SIPManager.shared.answerCall(callId: action.callUUID.uuidString)
action.fulfill()
}
}PushKit for Background VoIP Calls
PushKit is mandatory for background VoIP call reception on iOS. Regular APNs push notifications cannot reliably wake a suspended VoIP app. PushKit wakes your app within milliseconds, giving it CPU time to register the incoming call with CallKit before the push expires. Critical: Apple requires that every PushKit push immediately triggers a CallKit reportNewIncomingCall() call. Apps that receive PushKit pushes without reporting a CallKit call are rejected from the App Store.
Frequently Asked Questions
Which SIP library should I use for iOS Swift?
PJSIP 2.x (cross-platform, battle-tested, wide codec support) and linphone SDK (easier integration, commercial support available) are the two main options. PJSIP gives more control; linphone is faster to integrate.
Why does my VoIP app get rejected from the App Store?
Common rejection reasons: using PushKit without immediately reporting a CallKit call, abusing the VoIP background mode for non-call purposes, not declaring voip in UIBackgroundModes, or failing to handle call state transitions correctly in CXProvider.
How do I handle audio interruptions (Siri, other calls)?
Implement AVAudioSessionDelegate and listen for AVAudioSession.interruptionNotification. Pause your SIP call's audio on interruption, resume on interruptionEnded with shouldResume=true. Also handle route changes (speaker, Bluetooth, headphones) via routeChangeNotification.
Need help implementing this for your project?
Talk to a VoIP Engineer