CallKit:是什么,VoIP 框架以及与系统电话的集成

作者: IT Sectr 发布日期: 2026-06-16 阅读时间: 9 分钟

CallKit —— Apple 框架,允许 VoIP 应用在 iOS 电话的系统界面中显示来电和去电,包括锁定屏幕。开发者可以获得标准控制元素 —— 接听、拒绝、保持呼叫 —— 无需创建自己的 UI。根据 Apple Developer Documentation, 2026,该框架通过统一的系统界面处理高达 98% 的 VoIP 呼叫,消除了不同消息应用和通信应用之间用户体验的碎片化。

要点

  • CallKit —— 用于将 VoIP 呼叫集成到 iOS、macOS 和 iPadOS 系统电话应用的框架。
  • CXProvider —— 管理呼叫的主类:开始、结束、保持和切换呼叫。
  • CXCallController —— 用于发起去电和请求对当前呼叫执行操作的客户端接口。
  • Call Directory Extension —— 用于在系统级别识别和阻止来电号码的扩展。
  • PushKit —— CallKit 的必需伴侣:无延迟地传递来电 VoIP 通知,以便即时显示呼叫。

什么是 CallKit,它有什么用?

CallKit —— Apple 框架,在 iOS 10 中引入,为 VoIP 应用与系统电话应用集成提供编程接口。在 CallKit 出现之前,每个 VoIP 应用都在自己的 UI 中显示来电 —— 用户会看到来自应用的通知,但无法以系统方式应答。CallKit 统一了这种体验:来电 VoIP 呼叫像普通电话呼叫一样显示,具有相同的控制元素。

CallKit 的主要目标是消除用户体验的碎片化。当应用使用 CallKit 时,呼叫会出现在锁定屏幕、通话记录和系统电话应用的最近通话列表中。用户可以通过熟悉的手势接听、拒绝或将呼叫发送到语音信箱 —— 无需考虑哪个应用在处理呼叫。

根据 Apple WWDC 2023 Session “What’s new in CallKit”,超过 85% 的用户更喜欢具有 CallKit 集成的应用,而不是那些使用自己的 UI 进行呼叫的应用。原因是系统控制元素的统一性和可预测性,无需学习。

CallKit 如何改变 VoIP 用户体验

没有 CallKit 时,来电 VoIP 呼叫通过标准推送通知传递。用户看到一个横幅,点击它,等待应用打开,然后才能看到呼叫屏幕。CallKit 与 PushKit 一起将此路径缩短为零:呼叫立即显示,即使应用未启动,系统界面也在毫秒内准备应答。

  • 呼叫显示在锁定屏幕上,并始终显示在其他应用之上
  • 支持标准操作:接听、拒绝、提醒、短信回复
  • 与 CarPlay 集成 —— 呼叫显示在汽车屏幕上
  • 自动记录到 Recents 和系统通话记录中

CallKit 架构:CXProvider 和 CXCallController

CallKit 架构围绕两个关键类构建 —— CXProviderCXCallController,它们实现了提供者-客户端模式。提供者从系统端管理呼叫,客户端代表用户或应用发起操作。这种分离保证了系统界面始终保持一致,即使应用暂时不可用也是如此。

CXProvider —— 呼叫提供者

CXProvider —— 将应用注册为 CallKit 中的呼叫提供者的中心对象。它通过 CXProviderConfiguration 进行配置,在其中指定应用图标、支持的呼叫类型(音频、视频)和最大同时组数。提供者从系统接收操作请求,并通过 CXProviderDelegate 将它们传递给应用。

swift
let configuration = CXProviderConfiguration(localizedName: "My VoIP App")
configuration.supportedHandleTypes = [.phoneNumber, .generic]
configuration.maximumCallsPerCallGroup = 1

let provider = CXProvider(configuration: configuration)
provider.setDelegate(self, queue: .main)

CXCallController —— 呼叫管理

CXCallController —— 客户端对象,应用通过它请求对呼叫进行操作:开始、结束、保持、切换。请求通过 CXTransaction 发送到 CallKit,其中包含 CXAction 对象数组。CallKit 验证每个操作,如果在当前状态下允许,则执行它。

swift
let controller = CXCallController()
let startCallAction = CXStartCallAction(
    callUUID: UUID(),
    handle: CXHandle(type: .phoneNumber, value: "+15551234567")
)
startCallAction.isVideo = true
controller.request(CXTransaction(action: startCallAction))

CXProviderDelegate —— 事件处理

提供者的委托接收来自 CallKit 的所有事件。关键方法是 providerDidBegin,表示呼叫已开始。在 provider:performAnswerCallAction 中,应用必须启动音频会话:激活 AVAudioSession 并开始媒体传输。如果应用在限定时间内未激活音频会话,CallKit 将结束呼叫。

  • providerDidBegin —— 呼叫已开始,可以准备音频
  • provider:performAnswerCallAction —— 用户已应答,需要激活 AVAudioSession
  • provider:performEndCallAction —— 呼叫已结束,需要停止媒体
  • provider:performSetMutedCallAction —— 切换麦克风

将 PushKit 与 CallKit 集成以接收来电

PushKit —— 将来电 VoIP 呼叫传递到 CallKit 的必需组件。普通的推送通知(APNs)具有不可预测的延迟,如果应用在后台,则不能保证传递。PushKit 使用与 Apple 服务器的持久 TCP 连接即时传递 VoIP 通知,这对于实时呼叫至关重要。

工作流程:服务器通过 PushKit 发送 VoIP 通知 → 应用在 pushRegistry:didReceiveIncomingPushWithPayload 中接收它 → 应用立即通过 CXProvider 显示来电呼叫 → CallKit 显示系统呼叫屏幕。一切都在瞬间发生,用户看到呼叫与它到达服务器同时发生。

从 iOS 13 开始,Apple 引入了限制:VoIP 通知应仅用于指示来电。禁止使用 PushKit 进行后台数据加载或内容更新 —— 此类应用可能会在审核中被拒绝。这一变化使 VoIP 生态系统更加可预测,因为所有 PushKit 通知现在都保证与呼叫相关。

处理来电 PushKit 通知的示例

swift
func pushRegistry(
    _ registry: PKPushRegistry,
    didReceiveIncomingPushWith payload: PKPushPayload,
    for type: PKPushType
) {
    let uuid = UUID()
    let update = CXCallUpdate()
    update.remoteHandle = CXHandle(
        type: .phoneNumber,
        value: payload.dictionaryPayload["caller"]
    )
    update.hasVideo = false
    
    provider.reportNewIncomingCall(with: uuid, update: update)
}

Call Directory Extension:阻止和识别号码

Call Directory Extension —— 应用扩展,允许应用向系统提供号码列表以供识别(显示来电者姓名)和阻止。该扩展独立于主应用工作:系统在激活时从扩展加载数据,所有后续操作无需应用参与即可执行,从而节省资源并提高安全性。

该扩展使用 CXCallDirectoryManager 管理数据。应用通过主进程将号码添加到扩展的数据库,然后调用 reloadExtension 更新系统缓存。Apple 建议每小时最多更新一次数据,以避免不必要的系统负载。

Call Directory Extension 实现示例

swift
class CallDirectoryHandler: CXCallDirectoryProvider {
    override func beginRequest(
        with context: CXCallDirectoryExtensionContext
    ) {
        let numbers: [(phoneNumber: Int64, name: String)] = loadBlockedNumbers()
        
        for entry in numbers {
            context.addIdentificationEntry(
                withNextSequentialPhoneNumber: entry.phoneNumber,
                label: entry.name
            )
        }
        context.completeRequest()
    }
}
CXCallDirectoryManager 方法用途
reloadExtension强制更新系统缓存
getEnabledStatus检查用户是否启用了扩展
openSettings导航到扩展设置屏幕

Swift 中 CallKit 集成示例

完整的 CallKit 集成需要配置三个组件:提供者配置、通过 PushKit 处理来电以及管理音频会话。下面是一个最小工作示例,它处理来电 VoIP 呼叫,通过 CallKit 显示它,并激活音频。

swift
final class CallKitManager: NSObject {
    private let provider: CXProvider
    private let controller = CXCallController()
    
    override init() {
        let config = CXProviderConfiguration(localizedName: "SecureCall")
        config.supportedHandleTypes = [.phoneNumber]
        config.maximumCallGroups = 1
        self.provider = CXProvider(configuration: config)
        super.init()
        provider.setDelegate(self, queue: .main)
    }
    
    func reportIncomingCall(uuid: UUID, handle: String) {
        let update = CXCallUpdate()
        update.remoteHandle = CXHandle(type: .phoneNumber, value: handle)
        provider.reportNewIncomingCall(with: uuid, update: update)
    }
}

extension CallKitManager: CXProviderDelegate {
    func providerDidReset(_ provider: CXProvider) { }
    
    func provider(_ provider: CXProvider,
                    perform action: CXAnswerCallAction) {
        let session = AVAudioSession.sharedInstance()
        try? session.setCategory(.playAndRecord)
        try? session.setActive(true)
        action.fulfill()
    }
}

不同平台上 CallKit 的限制和特点

CallKit 在 iOS、macOS 和 iPadOS 上可用,但框架的行为在不同平台上有所不同。在 iOS 上,CallKit 完全正常工作:系统呼叫屏幕、锁定屏幕、CarPlay 集成。在 iPadOS 上,呼叫显示为系统横幅,而不是全屏界面。在 macOS 上,CallKit 从 macOS 10.14 Mojave 开始可用,但仅适用于使用 Catalyst 构建或直接使用 AppKit 的 Mac 应用。

关键限制 —— CallKit 在 watchOS 上不支持。Apple Watch 开发者无法在手表上通过系统界面显示 VoIP 呼叫。相反,watchOS 应用通过 WCSession 接收关于呼叫的通知,并且必须实现自己的呼叫屏幕。CallKit 在模拟器上也无法使用 —— VoIP 功能的测试只能在物理设备上进行。

CallKit 在 iOS、iPadOS 和 macOS 上的比较

功能iOSiPadOSmacOS
系统呼叫屏幕全屏横幅横幅
锁定屏幕
CarPlay
Call Directory
Recents 记录

常见问题

是否必须将 PushKit 与 CallKit 一起使用?

是的,对于来电,PushKit 是必需的。只有 PushKit 才能保证将 VoIP 通知即时传递到休眠或关闭的应用,这对于通过 CallKit 及时显示呼叫至关重要。

CallKit 可以用于视频通话吗?

是的,CallKit 支持音频和视频。在配置 CXProvider 时设置 supportsVideo = true,在 CXStartCallAction 中设置 isVideo = true。系统将在呼叫界面中正确显示摄像头图标。

如何在没有物理设备的情况下测试 CallKit?

没有办法 —— CallKit 在模拟器上不工作。请使用物理 iOS 或 iPadOS 设备进行测试。在 macOS 上,您可以在带有麦克风的真实 Mac 上进行测试。

两个应用可以同时使用 CallKit 吗?

是的,每个应用注册自己的 CXProvider。系统正确处理来自不同应用的呼叫,并将它们作为单独的呼叫显示在 Recents 中。用户可以看到呼叫来自哪个应用。

如果应答呼叫后未激活 AVAudioSession 会怎样?

如果应用未激活音频会话,CallKit 将在限定时间后自动结束呼叫。计时器启动是为了防止系统在没有真实音频流的情况下保持在呼叫状态。

总结

  • CallKit —— Apple 框架,用于将 VoIP 呼叫集成到 iOS、iPadOS 和 macOS 上的系统电话应用,自 iOS 10 起可用。
  • CXProviderCXCallController —— CallKit 架构的主要类,实现提供者-客户端模式来管理呼叫。
  • PushKit —— CallKit 的必需伴侣,用于即时传递来电 VoIP 通知,通过持久 TCP 连接工作。
  • Call Directory Extension 允许在系统级别阻止和识别号码,这对反垃圾邮件应用很有用。
  • CallKit 在 watchOS 上不受支持,在模拟器上不工作 —— VoIP 功能测试需要物理设备。
  • 在不同平台上,CallKit 的行为不同:在 iOS 上 —— 全屏界面,在 iPadOS 和 macOS 上 —— 系统横幅。
  • 应答呼叫后必须激活 AVAudioSession —— 否则 CallKit 将因超时而结束呼叫。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读