iOS Calling UI SDK

The iOS Calling UI SDK wraps Apple's native calling infrastructure to deliver a full incoming-call screen experience in your iOS app with minimal code. When a VoIP push arrives, the SDK presents the familiar iOS system-level calling UI — the same one users see for regular phone calls — and routes accept, reject, hold, and end actions back to your app via callback delegates. Combined with the iOS UI Kit, you can enable complete app-to-app audio/video calling in under five minutes.

iOS Calling UI SDK v1.0.3  ·  Released February 18, 2024

Ships as Enx_CallKit_iOS.framework. Install via CocoaPods or add manually to your Xcode project.

⬇  Download iOS Calling UI SDK
How It Works

The SDK's core class is EnxCallKit. You create a single instance in the class where your app handles incoming VoIP push notifications, then call reportIncomingCall to trigger the system calling UI. The SDK handles the full UI lifecycle and calls back your delegate methods when the user interacts with the screen.

When loading the calling UI, you must wrap the operation in an iOS background task to ensure the system gives your app enough time to present the screen before being suspended.

Prerequisites

Before integrating the iOS Calling UI SDK, ensure:

Always use PushKit VoIP pushes (not regular APNs) to trigger incoming calls on iOS. VoIP pushes have higher delivery priority and grant the app immediate CPU time to present the system calling UI. Regular APNs notifications will not work reliably for this purpose.
Installation

Via CocoaPods

Open your Podfile and add the SDK pod, then install:

pod 'Enx_CallKit_iOS'
cd /path/to/your-project/
pod install

Manual Installation

  1. Download Enx_CallKit_iOS.framework_1.0.3.zip using the button above and unzip it.
  2. In Xcode, open your project navigator and drag Enx_CallKit_iOS.framework into the Frameworks, Libraries, and Embedded Content section of your target.
  3. Set the embed option to Embed & Sign.
Integration

1. Import and Initialise

Go to the class where you receive VoIP push notifications (typically your PKPushRegistryDelegate implementation). Import the framework and create an EnxCallKit instance:

import Enx_CallKit_iOS

class CallNotificationHandler: NSObject, PKPushRegistryDelegate, EnxCallKitDelegate {

    var callManager: EnxCallKit?

    func setup() {
        callManager = EnxCallKit(self)
        // self: sets the callback delegate
    }
}

2. Report an Incoming Call

When a VoIP push arrives, use reportIncomingCall to present the system calling UI. Wrap it in a background task so iOS does not suspend the app before the UI appears:

func pushRegistry(_ registry: PKPushRegistry,
                  didReceiveIncomingPushWith payload: PKPushPayload,
                  for type: PKPushType,
                  completion: @escaping () -> Void) {

    // Begin a background task — iOS must not suspend the app during UI presentation
    let bgTask = UIApplication.shared.beginBackgroundTask(expirationHandler: nil)

    let callerName = payload.dictionaryPayload["callerName"] as? String ?? "Unknown"
    let hasVideo   = payload.dictionaryPayload["hasVideo"]   as? Bool   ?? false

    callManager?.reportIncomingCall(
        uuid:       UUID(),
        callerName: callerName,
        hasVideo:   hasVideo
    ) { _ in
        // End the background task once the calling UI has loaded
        UIApplication.shared.endBackgroundTask(bgTask)
        completion()
    }
}

3. End a Call Programmatically

To dismiss the calling UI from your code (for example when a session ends on the remote side), call endCall(). This closes the system calling screen immediately:

callManager?.endCall()
Callbacks

Implement the EnxCallKitDelegate protocol in the class you passed as self during initialisation. The SDK calls these methods based on user interaction with the calling screen:

CallbackWhen it firesWhat to do
func callAnswer() User taps Accept Connect to the EnableX video room and start the session
func callReject() User taps Decline Notify the remote caller, release resources
func callTimeOut() No response within 45 seconds Dismiss UI, notify caller of missed call
func callEnd() Active call ends (local or remote) Clean up the video session and return to the idle state
func callHold() User places the call on hold Pause media streams, show on-hold state in your UI
extension CallNotificationHandler: EnxCallKitDelegate {

    func callAnswer() {
        // Start the EnableX video session
        VideoSessionManager.shared.connect(roomId: roomId, token: token)
    }

    func callReject() {
        // Notify the remote caller and clean up
        signallingServer.sendReject(to: callerId)
    }

    func callTimeOut() {
        // 45 seconds elapsed with no response — call auto-dismissed
        signallingServer.sendMissedCall(to: callerId)
    }

    func callEnd() {
        // Session ended — return to idle
        VideoSessionManager.shared.disconnect()
    }

    func callHold() {
        // Put media on hold
        VideoSessionManager.shared.hold()
    }
}
Also See