CallKit là một framework của Apple cho phép các ứng dụng VoIP hiển thị cuộc gọi đến và đi trong giao diện điện thoại hệ thống iOS, bao gồm màn hình khóa. Nhà phát triển nhận được các điều khiển tiêu chuẩn — chấp nhận, từ chối, giữ cuộc gọi — mà không cần tạo giao diện người dùng riêng. Theo Apple Developer Documentation, 2026, framework xử lý tới 98% cuộc gọi VoIP thông qua một giao diện hệ thống duy nhất, loại bỏ sự phân mảnh trải nghiệm người dùng giữa các ứng dụng nhắn tin và liên lạc khác nhau.
Những điểm chính
CallKit là framework của Apple được giới thiệu trong iOS 10 cung cấp giao diện lập trình để tích hợp ứng dụng VoIP với ứng dụng Điện thoại hệ thống. Trước CallKit, mỗi ứng dụng VoIP hiển thị cuộc gọi đến trong giao diện người dùng riêng — người dùng thấy thông báo từ ứng dụng nhưng không thể trả lời theo cách hệ thống. CallKit hợp nhất trải nghiệm này: cuộc gọi VoIP đến hiển thị như cuộc gọi điện thoại thông thường với các điều khiển tương tự.
Mục đích chính của CallKit là loại bỏ sự phân mảnh trải nghiệm người dùng. Khi ứng dụng sử dụng CallKit, cuộc gọi xuất hiện trên màn hình khóa, trong lịch sử cuộc gọi và trong danh sách cuộc gọi gần đây của ứng dụng Điện thoại hệ thống. Người dùng có thể trả lời, từ chối hoặc gửi cuộc gọi đến hộp thư thoại bằng các thao tác quen thuộc — mà không cần suy nghĩ ứng dụng nào đang xử lý cuộc gọi.
Theo phiên Apple WWDC 2023 “What’s new in CallKit”, hơn 85% người dùng thích ứng dụng có tích hợp CallKit hơn những ứng dụng sử dụng giao diện người dùng riêng cho cuộc gọi. Lý do là sự nhất quán và khả năng dự đoán của các điều khiển hệ thống không yêu cầu học tập.
Không có CallKit, cuộc gọi VoIP đến được gửi qua thông báo push tiêu chuẩn. Người dùng thấy banner, chạm vào nó, chờ ứng dụng mở và chỉ sau đó mới thấy màn hình cuộc gọi. CallKit cùng với PushKit giảm đường dẫn này xuống bằng không: cuộc gọi xuất hiện ngay lập tức, ngay cả khi ứng dụng không chạy và giao diện hệ thống sẵn sàng trả lời trong mili giây.
Kiến trúc CallKit được xây dựng xung quanh hai lớp chính — CXProvider và CXCallController, triển khai mô hình nhà cung cấp-khách hàng. Nhà cung cấp quản lý cuộc gọi ở phía hệ thống, trong khi khách hàng khởi tạo hành động thay mặt người dùng hoặc ứng dụng. Sự tách biệt này đảm bảo giao diện hệ thống luôn nhất quán, ngay cả khi ứng dụng tạm thời không khả dụng.
CXProvider là đối tượng trung tâm đăng ký ứng dụng trong CallKit như một nhà cung cấp cuộc gọi. Nó được cấu hình qua CXProviderConfiguration, nơi chỉ định biểu tượng ứng dụng, loại cuộc gọi được hỗ trợ (âm thanh, video) và số lượng nhóm đồng thời tối đa. Nhà cung cấp nhận yêu cầu hành động từ hệ thống và chuyển chúng đến ứng dụng qua CXProviderDelegate.
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 là đối tượng khách hàng qua đó ứng dụng yêu cầu hành động trên cuộc gọi: bắt đầu, kết thúc, giữ, chuyển đổi. Yêu cầu được gửi đến CallKit qua CXTransaction, chứa một mảng các đối tượng CXAction. CallKit xác thực từng hành động và thực hiện nếu được phép trong trạng thái hiện tại.
let controller = CXCallController()
let startCallAction = CXStartCallAction(
callUUID: UUID(),
handle: CXHandle(type: .phoneNumber, value: "+15551234567")
)
startCallAction.isVideo = true
controller.request(CXTransaction(action: startCallAction))
Ủy quyền của nhà cung cấp nhận tất cả sự kiện từ CallKit. Phương thức quan trọng là providerDidBegin, báo hiệu cuộc gọi đã bắt đầu. Trong provider:performAnswerCallAction, ứng dụng phải bắt đầu phiên âm thanh: kích hoạt AVAudioSession và bắt đầu truyền phương tiện. Nếu ứng dụng không kích hoạt phiên âm thanh trong thời gian giới hạn, CallKit sẽ kết thúc cuộc gọi.
PushKit là thành phần bắt buộc để gửi cuộc gọi VoIP đến CallKit. Thông báo push thông thường (APNs) có độ trễ không thể dự đoán và không đảm bảo gửi nếu ứng dụng ở chế độ nền. PushKit sử dụng kết nối TCP liên tục với máy chủ Apple để gửi thông báo VoIP tức thì, điều này rất quan trọng cho cuộc gọi thời gian thực.
Luồng công việc: máy chủ gửi thông báo VoIP qua PushKit → ứng dụng nhận nó trong pushRegistry:didReceiveIncomingPushWithPayload → ứng dụng ngay lập tức hiển thị cuộc gọi đến qua CXProvider → CallKit hiển thị màn hình cuộc gọi hệ thống. Mọi thứ xảy ra trong tích tắc và người dùng thấy cuộc gọi cùng lúc nó đến máy chủ.
Bắt đầu từ iOS 13, Apple đã đưa ra hạn chế: thông báo VoIP chỉ được sử dụng để chỉ báo cuộc gọi đến. Việc sử dụng PushKit để tải dữ liệu nền hoặc cập nhật nội dung bị cấm — các ứng dụng như vậy có thể bị từ chối khi kiểm duyệt. Thay đổi này làm cho hệ sinh thái VoIP dễ dự đoán hơn vì tất cả thông báo PushKit hiện được đảm bảo liên quan đến cuộc gọi.
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 là phần mở rộng ứng dụng cho phép ứng dụng cung cấp cho hệ thống danh sách số để xác định (hiển thị tên người gọi) và chặn. Phần mở rộng hoạt động độc lập với ứng dụng chính: hệ thống tải dữ liệu từ phần mở rộng khi kích hoạt và tất cả các thao tác tiếp theo được thực hiện mà không có sự tham gia của ứng dụng, tiết kiệm tài nguyên và tăng cường bảo mật.
Phần mở rộng sử dụng CXCallDirectoryManager để quản lý dữ liệu. Ứng dụng qua tiến trình chính thêm số vào cơ sở dữ liệu của phần mở rộng, sau đó gọi reloadExtension để cập nhật bộ nhớ đệm hệ thống. Apple khuyến nghị cập nhật dữ liệu không quá một lần mỗi giờ để tránh tải không cần thiết lên hệ thống.
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()
}
}
| Phương thức CXCallDirectoryManager | Mục đích |
|---|---|
| reloadExtension | Cập nhật bộ nhớ đệm hệ thống bắt buộc |
| getEnabledStatus | Kiểm tra xem phần mở rộng có được người dùng bật không |
| openSettings | Chuyển đến màn hình cài đặt phần mở rộng |
Tích hợp CallKit đầy đủ yêu cầu thiết lập ba thành phần: cấu hình nhà cung cấp, xử lý cuộc gọi đến qua PushKit và quản lý phiên âm thanh. Dưới đây là ví dụ hoạt động tối thiểu xử lý cuộc gọi VoIP đến, hiển thị nó qua CallKit và kích hoạt âm thanh.
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 có sẵn trên iOS, macOS và iPadOS, nhưng hành vi của framework khác nhau giữa các nền tảng. Trên iOS, CallKit hoạt động đầy đủ: màn hình cuộc gọi hệ thống, màn hình khóa, tích hợp CarPlay. Trên iPadOS, cuộc gọi được hiển thị dưới dạng banner hệ thống thay vì giao diện toàn màn hình. Trên macOS, CallKit có sẵn từ macOS 10.14 Mojave, nhưng chỉ cho ứng dụng Mac được xây dựng với Catalyst hoặc sử dụng AppKit trực tiếp.
Hạn chế chính — CallKit không được hỗ trợ trên watchOS. Nhà phát triển Apple Watch không thể hiển thị cuộc gọi VoIP qua giao diện hệ thống trên đồng hồ. Thay vào đó, ứng dụng watchOS nhận thông báo cuộc gọi qua WCSession và phải tự triển khai màn hình cuộc gọi riêng. Ngoài ra, CallKit không hoạt động trên trình giả lập — thử nghiệm tính năng VoIP chỉ khả thi trên thiết bị vật lý.
| Tính năng | iOS | iPadOS | macOS |
|---|---|---|---|
| Màn hình cuộc gọi hệ thống | Toàn màn hình | Banner | Banner |
| Màn hình khóa | Có | Không | Không |
| CarPlay | Có | Không | Không |
| Danh bạ cuộc gọi | Có | Có | Không |
| Lịch sử gần đây | Có | Có | Có |
Câu hỏi thường gặp
Có, đối với cuộc gọi đến, PushKit là bắt buộc. Chỉ PushKit mới đảm bảo gửi thông báo VoIP tức thì đến ứng dụng đang ngủ hoặc đã đóng, điều này rất quan trọng để hiển thị cuộc gọi kịp thời qua CallKit.
Có, CallKit hỗ trợ âm thanh và video. Khi cấu hình CXProvider, đặt supportsVideo = true và trong CXStartCallAction đặt isVideo = true. Hệ thống sẽ hiển thị chính xác biểu tượng camera video trong giao diện cuộc gọi.
Không có cách nào — CallKit không hoạt động trên trình giả lập. Để kiểm tra, hãy sử dụng thiết bị iOS hoặc iPadOS vật lý. Trên macOS, bạn có thể kiểm tra trên Mac thật có micrô.
Có, mỗi ứng dụng đăng ký CXProvider riêng. Hệ thống xử lý chính xác cuộc gọi từ các ứng dụng khác nhau và hiển thị chúng dưới dạng cuộc gọi riêng biệt trong danh sách Gần đây. Người dùng có thể thấy cuộc gọi đến từ ứng dụng nào.
CallKit sẽ tự động kết thúc cuộc gọi sau một thời gian giới hạn nếu ứng dụng không kích hoạt phiên âm thanh. Bộ đếm thời gian hoạt động để hệ thống không ở trạng thái cuộc gọi mà không có luồng âm thanh thực.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm