CallKit è un framework Apple che consente alle applicazioni VoIP di visualizzare le chiamate in arrivo e in uscita nell'interfaccia telefonica di sistema di iOS, incluso lo schermo di blocco. Lo sviluppatore ottiene controlli standard — accettare, rifiutare, mettere in attesa — senza dover creare la propria interfaccia utente. Secondo Apple Developer Documentation, 2026, il framework gestisce fino al 98% delle chiamate VoIP attraverso un'unica interfaccia di sistema, eliminando la frammentazione dell'esperienza utente tra diversi messaggeri e applicazioni di comunicazione.
Punti chiave
CallKit è un framework Apple introdotto in iOS 10 che fornisce un'interfaccia di programmazione per integrare le applicazioni VoIP con l'app Telefono di sistema. Prima di CallKit, ogni app VoIP visualizzava le chiamate in arrivo nella propria interfaccia utente — l'utente vedeva una notifica dall'app ma non poteva rispondere in modo sistemico. CallKit unifica questa esperienza: una chiamata VoIP in arrivo appare come una normale chiamata telefonica con gli stessi controlli.
L'obiettivo principale di CallKit è eliminare la frammentazione dell'esperienza utente. Quando un'app utilizza CallKit, la chiamata appare sullo schermo di blocco, nella cronologia chiamate e nell'elenco delle chiamate recenti dell'app Telefono di sistema. L'utente può rispondere, rifiutare o inviare una chiamata alla segreteria telefonica con gesti familiari — senza pensare a quale app sta gestendo la chiamata.
Secondo la sessione Apple WWDC 2023 “What’s new in CallKit”, oltre il 85% degli utenti preferisce le app con integrazione CallKit rispetto a quelle che utilizzano la propria interfaccia utente per le chiamate. Il motivo è l'uniformità e la prevedibilità dei controlli di sistema che non richiedono apprendimento.
Senza CallKit, una chiamata VoIP in arrivo viene consegnata tramite una notifica push standard. L'utente vede un banner, lo tocca, aspetta l'apertura dell'app e solo allora vede la schermata di chiamata. CallKit insieme a PushKit riduce questo percorso a zero: la chiamata appare istantaneamente, anche se l'app non è in esecuzione, e l'interfaccia di sistema è pronta a rispondere in millisecondi.
L'architettura di CallKit è costruita attorno a due classi chiave — CXProvider e CXCallController, che implementano il pattern provider-client. Il provider gestisce la chiamata sul lato sistema, mentre il client avvia azioni per conto dell'utente o dell'app. Questa separazione garantisce che l'interfaccia di sistema rimanga sempre coerente, anche se l'app è temporaneamente non disponibile.
CXProvider è l'oggetto centrale che registra l'app in CallKit come fornitore di chiamate. Viene configurato tramite CXProviderConfiguration, dove vengono specificati l'icona dell'app, i tipi di chiamata supportati (audio, video) e il numero massimo di gruppi simultanei. Il provider riceve le richieste di azioni dal sistema e le trasmette all'app attraverso 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'oggetto client attraverso il quale l'app richiede azioni sulle chiamate: avviare, terminare, mettere in attesa, passare. Le richieste vengono inviate a CallKit tramite CXTransaction, che contiene un array di oggetti CXAction. CallKit convalida ogni azione e la esegue se è consentita nello stato corrente.
let controller = CXCallController()
let startCallAction = CXStartCallAction(
callUUID: UUID(),
handle: CXHandle(type: .phoneNumber, value: "+15551234567")
)
startCallAction.isVideo = true
controller.request(CXTransaction(action: startCallAction))
Il delegato del provider riceve tutti gli eventi da CallKit. Il metodo critico è providerDidBegin, che segnala che una chiamata è iniziata. In provider:performAnswerCallAction, l'app deve avviare una sessione audio: attivare AVAudioSession e iniziare la trasmissione dei media. Se l'app non attiva la sessione audio entro un tempo limitato, CallKit terminerà la chiamata.
PushKit è un componente obbligatorio per la consegna delle chiamate VoIP in arrivo a CallKit. Le notifiche push standard (APNs) hanno una latenza imprevedibile e non garantiscono la consegna se l'app è in background. PushKit utilizza una connessione TCP persistente con i server Apple per la consegna istantanea delle notifiche VoIP, fondamentale per le chiamate in tempo reale.
Flusso di lavoro: il server invia una notifica VoIP tramite PushKit → l'app la riceve in pushRegistry:didReceiveIncomingPushWithPayload → l'app mostra immediatamente la chiamata in arrivo tramite CXProvider → CallKit mostra la schermata di chiamata di sistema. Tutto avviene in frazioni di secondo e l'utente vede la chiamata nello stesso momento in cui arriva al server.
A partire da iOS 13, Apple ha introdotto una restrizione: le notifiche VoIP devono essere utilizzate solo per indicare chiamate in arrivo. L'uso di PushKit per il caricamento di dati in background o l'aggiornamento dei contenuti è vietato — tali app possono essere respinte durante la moderazione. Questo cambiamento ha reso l'ecosistema VoIP più prevedibile, poiché tutte le notifiche PushKit sono ora garantite come correlate alle chiamate.
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 è un'estensione dell'app che consente all'app di fornire al sistema elenchi di numeri per l'identificazione (visualizzazione del nome del chiamante) e il blocco. L'estensione funziona indipendentemente dall'app principale: il sistema carica i dati dall'estensione all'attivazione e tutte le operazioni successive vengono eseguite senza il coinvolgimento dell'app, risparmiando risorse e migliorando la sicurezza.
L'estensione utilizza CXCallDirectoryManager per gestire i dati. L'app tramite il processo principale aggiunge numeri al database dell'estensione, quindi chiama reloadExtension per aggiornare la cache di sistema. Apple raccomanda di aggiornare i dati non più di una volta all'ora per evitare un carico inutile sul sistema.
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()
}
}
| Metodo di CXCallDirectoryManager | Scopo |
|---|---|
| reloadExtension | Aggiornamento forzato della cache di sistema |
| getEnabledStatus | Verificare se l'estensione è abilitata dall'utente |
| openSettings | Andare alla schermata delle impostazioni dell'estensione |
L'integrazione completa di CallKit richiede la configurazione di tre componenti: configurazione del provider, gestione delle chiamate in arrivo tramite PushKit e gestione della sessione audio. Di seguito un esempio funzionante minimo che gestisce una chiamata VoIP in arrivo, la visualizza tramite CallKit e attiva l'audio.
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 è disponibile su iOS, macOS e iPadOS, ma il comportamento del framework differisce tra le piattaforme. Su iOS, CallKit funziona completamente: schermata di chiamata di sistema, schermo di blocco, integrazione CarPlay. Su iPadOS, la chiamata viene visualizzata come un banner di sistema anziché un'interfaccia a schermo intero. Su macOS, CallKit è disponibile da macOS 10.14 Mojave, ma solo per app Mac create con Catalyst o che utilizzano AppKit direttamente.
La limitazione principale — CallKit non è supportato su watchOS. Gli sviluppatori di Apple Watch non possono visualizzare chiamate VoIP tramite l'interfaccia di sistema sull'orologio. Invece, l'app watchOS riceve una notifica di chiamata tramite WCSession e deve implementare la propria schermata di chiamata. Inoltre, CallKit non funziona sul simulatore — il test delle funzionalità VoIP è possibile solo su un dispositivo fisico.
| Funzionalità | iOS | iPadOS | macOS |
|---|---|---|---|
| Schermata di chiamata di sistema | Schermo intero | Banner | Banner |
| Schermo di blocco | Sì | No | No |
| CarPlay | Sì | No | No |
| Direttorio chiamate | Sì | Sì | No |
| Cronologia Recenti | Sì | Sì | Sì |
Domande frequenti
Sì, per le chiamate in arrivo PushKit è obbligatorio. Solo PushKit garantisce la consegna istantanea delle notifiche VoIP a un'app in sospensione o chiusa, il che è fondamentale per la visualizzazione tempestiva della chiamata tramite CallKit.
Sì, CallKit supporta audio e video. Quando configurate CXProvider, impostate supportsVideo = true e in CXStartCallAction impostate isVideo = true. Il sistema visualizzerà correttamente l'icona della videocamera nell'interfaccia di chiamata.
In nessun modo — CallKit non funziona sul simulatore. Per i test, utilizzate un dispositivo fisico iOS o iPadOS. Su macOS, potete testare su un Mac reale con microfono.
Sì, ogni app registra il proprio CXProvider. Il sistema gestisce correttamente le chiamate da diverse app e le visualizza come chiamate separate in Recenti. L'utente può vedere da quale app proviene la chiamata.
CallKit terminerà la chiamata automaticamente dopo un tempo limitato se l'app non attiva la sessione audio. Il timer scatta in modo che il sistema non rimanga in uno stato di chiamata senza un flusso audio reale.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche