Introduction to CallKit
What is CallKit?
CallKit is an Apple framework introduced in iOS 10 that allows VoIP (Voice over Internet Protocol) apps to integrate seamlessly with the native iOS phone experience. Instead of operating as a separate entity, your VoIP app's calls appear and behave just like regular cellular calls.
Why Use CallKit?
Using CallKit provides numerous benefits:
- Improved User Experience: Users can answer VoIP calls from the lock screen, manage them using the built-in phone UI, and access call history in the Phone app.
- System Integration: CallKit integrates with features like Do Not Disturb, Call Waiting, and CarPlay.
- Enhanced Reliability: CallKit ensures proper call prioritization and resource management, leading to more reliable call handling.
- Familiar Interface: Users are already familiar with the native iOS call interface, reducing the learning curve for your VoIP app.
Key Features of CallKit
CallKit unlocks a powerful set of features for iOS VoIP applications:
- Incoming and Outgoing Call Management: Handles the presentation and management of both incoming and outgoing calls through the native iOS interface.
- Call Waiting and Holding: Supports putting calls on hold and managing multiple concurrent calls.
- Call Blocking and Identification: Integrates with iOS's call blocking and identification features.
- Contact Integration: Displays caller information from the user's contacts.
- CarPlay Support: Allows users to make and receive VoIP calls through CarPlay.
- Call Directory Extension: Enables your app to identify and filter incoming calls, marking potential spam or unwanted callers.
CallKit Architecture and Core Components
The CallKit framework revolves around several key components that work together to manage call interactions. Understanding these components is crucial for successful CallKit integration. The
CXProvider
is the central element, acting as the interface between your application and the system's call management. CXCallController
is used to request changes to the call state, like starting and ending calls. Finally, CXCallObserver
lets you monitor the call state.1sequenceDiagram
2 participant Your App
3 participant CXCallController
4 participant CXProvider
5 participant iOS System
6
7 Your App->>CXCallController: Initiate Outgoing Call (Request)
8 CXCallController->>CXProvider: Report Begin Call Transaction
9 CXProvider->>iOS System: Inform System about New Call
10 iOS System-->>CXProvider: Request Actions (e.g., Answer, End Call)
11 CXProvider->>Your App: Delegate Actions to Your App
12 Your App->>iOS System: Handle Audio and Signaling
13 Your App->>CXProvider: Report Call Events (Connected, Ended)
14 CXProvider->>iOS System: Update Call State in System UI
CXProvider: The Heart of CallKit
The
CXProvider
object is the central point of contact between your app and the CallKit framework. It's responsible for reporting call state changes to the system and receiving actions from the system, such as answering or ending a call. You need to create and configure a CXProvider
instance to represent your VoIP service.1import CallKit
2
3let providerConfiguration = CXProviderConfiguration(localizedName: "My VoIP App")
4providerConfiguration.supportsVideo = true
5providerConfiguration.maximumCallsPerCallGroup = 1
6
7let provider = CXProvider(configuration: providerConfiguration)
8provider.setDelegate(self, queue: nil)
This code snippet demonstrates the basic setup of a
CXProvider
. It initializes a CXProviderConfiguration
to define the properties of your VoIP service, such as its localized name and whether it supports video calls. The CXProvider
is then initialized with this configuration, and a delegate is set to handle incoming call-related events.CXCallController: Managing Call Interactions
The
CXCallController
is used to request changes to the call state, such as starting an outgoing call or ending an existing call. You use transactions to bundle multiple actions together, ensuring that they are executed atomically.1import CallKit
2
3let callController = CXCallController()
4
5func startCall(handle: String) {
6 let handle = CXHandle(type: .generic, value: handle)
7 let startCallAction = CXStartCallAction(call: UUID(), handle: handle)
8
9 let transaction = CXTransaction()
10transaction.addAction(startCallAction)
11
12 callController.request(transaction) { error in
13 if let error = error {
14 print("Error starting call: \(error.localizedDescription)")
15 } else {
16 print("Call started successfully")
17 }
18 }
19}
This snippet shows how to initiate an outgoing call using the
CXCallController
. A CXStartCallAction
is created with the recipient's handle, and this action is added to a CXTransaction
. The CXCallController
then executes this transaction, notifying the system that an outgoing call is being initiated.CXCallObserver: Monitoring Call Activity
The
CXCallObserver
allows your app to monitor call activity, such as changes in call state or the addition or removal of calls. This is useful for updating your app's UI to reflect the current call status.1import CallKit
2
3let callObserver = CXCallObserver()
4
5func observeCallChanges() {
6 callObserver.setDelegate(self, queue: nil)
7}
8
9extension YourViewController: CXCallObserverDelegate {
10 func callObserver(_ callObserver: CXCallObserver, callChanged call: CXCall) {
11 print("Call state changed for call with UUID: \(call.uuid)")
12 print("Call hasConnected: \(call.hasConnected)")
13 print("Call isOutgoing: \(call.isOutgoing)")
14 // Update UI based on call state
15 }
16}
This code demonstrates how to use
CXCallObserverDelegate
to observe call state changes and get notified when the properties of any CXCall
change. This delegate provides a means to react to any change related to any of your calls.Integrating CallKit into Your iOS App
Integrating CallKit involves several steps, from setting up your project to handling incoming and outgoing calls. Let's break down the process.
Setting up Your Project
First, you need to enable the CallKit capability in your Xcode project:
- Open your project in Xcode.
- Select your target in the Project navigator.
- Go to the "Signing & Capabilities" tab.
- Click the "+ Capability" button.
- Search for and add the "CallKit" capability.
This ensures that your app has the necessary entitlements to use the CallKit framework.
Requesting CallKit Capabilities
You must request permission to use CallKit by including the
NSVoIPBackgroundUsage
key in your app's Info.plist
file. This key explains to the user why your app needs VoIP capabilities. Without this, CallKit may not function correctly.Additionally, ensure your app has microphone permissions. If you want to use push notifications to indicate incoming calls, you need to request push notification permissions and set up your provisioning profiles for push notifications.
Handling Incoming Calls
When an incoming call arrives, CallKit will notify your app through the
CXProviderDelegate
methods. You need to implement these methods to respond to the incoming call, display the call UI, and establish the VoIP connection.1import CallKit
2
3extension YourProviderDelegate: CXProviderDelegate {
4 func providerDidReset(_ provider: CXProvider) {
5 // Handle provider reset (e.g., after a crash)
6 }
7
8 func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
9 // Answer the call
10 action.fulfill()
11 }
12
13 func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
14 // End the call
15 action.fulfill()
16 }
17
18 func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
19 // Start the outgoing call
20 action.fulfill()
21 }
22
23 func provider(_ provider: CXProvider, didReceive incomingCall: CXCallUpdate) {
24 // Handle incoming call
25 if let handle = incomingCall.remoteHandle {
26 incomingCall.answerCompletion(completion: { success in
27 if success {
28 // Successfully answered call
29 } else {
30 // Failed to answer call
31 }
32 })
33 } else {
34 }
35 }
36
37}
In this example, the
provider(_:didReceive:)
function is crucial for handling incoming calls. When a call is received, CallKit provides a CXCallUpdate
object that contains information about the call, such as the caller's handle (phone number or username). You must configure your CXProvider
with the incoming call, and then fulfill or fail the action to indicate success or failure. You would also handle actions to answer and end the call.Handling Outgoing Calls
To initiate an outgoing call, you use the
CXCallController
to create a CXStartCallAction
and execute it in a transaction. This informs CallKit that your app is initiating a call, and the system will display the native call UI.1import CallKit
2
3let callController = CXCallController()
4
5func startCall(phoneNumber: String) {
6 let handle = CXHandle(type: .phoneNumber, value: phoneNumber)
7 let startCallAction = CXStartCallAction(call: UUID(), handle: handle)
8
9 let transaction = CXTransaction()
10transaction.addAction(startCallAction)
11
12 callController.request(transaction) { error in
13 if let error = error {
14 print("Error starting call: \(error.localizedDescription)")
15 } else {
16 print("Call started successfully")
17 }
18 }
19}
This code snippet demonstrates how to initiate an outgoing call using
CXCallController
. It creates a CXStartCallAction
with the recipient's phone number and executes it in a transaction, thus notifying the system that an outgoing call is being initiated.Advanced CallKit Techniques
Beyond basic call handling, CallKit offers several advanced features that can enhance your VoIP app.
Managing Multiple Calls
CallKit supports managing multiple concurrent calls, allowing users to put calls on hold and switch between them. You can use the
CXProvider
's reportCall
methods to update the call state and inform the system about call holding and switching.Integrating with Push Notifications
Push notifications can be used to alert users of incoming calls when your app is in the background. You need to configure your app to receive push notifications and use the
PKPushRegistry
API to register for VoIP push notifications. When a VoIP push notification arrives, you can use the information in the notification to create a CXCallUpdate
and report it to the CXProvider
.Handling CallKit Errors and Edge Cases
It's important to handle potential errors and edge cases in your CallKit integration. This includes handling situations where a call fails to connect, the user declines a call, or the network connection is lost. Implement error handling and logging to identify and address these issues.
Implementing Call Directory Integration
Call Directory integration allows your app to identify and filter incoming calls, marking potential spam or unwanted callers. You need to create a Call Directory extension and provide a list of phone numbers to block or identify. This extension runs in the background and intercepts incoming calls, providing a valuable service to your users.
Best Practices and Considerations
When developing with CallKit, consider these best practices for security, performance, and user experience.
Security and Privacy
- Secure VoIP Communication: Use encryption to protect the privacy of your users' calls. Implement TLS/SSL for secure signaling and SRTP/ZRTP for secure media transmission.
- Handle User Data Responsibly: Be transparent about how you collect and use user data, and comply with privacy regulations like GDPR.
Performance Optimization
- Minimize Battery Consumption: Optimize your app's code to reduce battery drain during calls. Use efficient audio codecs and minimize background activity.
- Handle Network Connectivity Gracefully: Implement robust error handling to deal with network connectivity issues. Use techniques like packet loss concealment to improve audio quality in poor network conditions.
User Experience
- Provide Clear Call Status Information: Display clear and concise information about the call status in your app's UI. This includes information about the caller, call duration, and call quality.
- Ensure Seamless Integration with Native UI: Strive for a seamless integration with the native iOS call UI. Use the same icons and terminology as the Phone app to avoid confusing users.
Conclusion
CallKit is a powerful framework that enables iOS developers to create seamless and integrated VoIP experiences. By understanding the core components, following best practices, and addressing potential issues, you can build robust and user-friendly VoIP apps that leverage the full potential of CallKit. Remember to consult Apple's official documentation and explore available resources to deepen your understanding and troubleshoot any challenges you may encounter.
- Apple's CallKit Documentation:
Learn more about the official Apple CallKit documentation.
- A Comprehensive CallKit Tutorial:
Check out this detailed tutorial for step-by-step guidance on integrating CallKit.
- Troubleshooting Common CallKit Issues:
Find solutions to common CallKit problems in this helpful resource.
Want to level-up your learning? Subscribe now
Subscribe to our newsletter for more tech based insights
FAQ