CBCentralManager è la classe centrale del framework Core Bluetooth in iOS che gestisce la scansione, la connessione e l'interazione con i dispositivi periferici BLE. Core Bluetooth (iOS 5+, 2011) fornisce un'astrazione di alto livello sullo stack BLE a livello GATT, nascondendo allo sviluppatore i dettagli di Link Layer e HCI. CBCentralManager implementa il ruolo Central: scansiona l'etere tramite scanForPeripherals, avvia la connessione tramite connect, scopre i servizi tramite discoverServices e gestisce il trasferimento dati. Secondo la Documentazione Sviluppatori Apple (2024), CBCentralManager supporta fino a 7 connessioni simultanee a dispositivi BLE su dispositivi con BLE 5.0.
Punti Chiave
CBCentralManager è la classe principale di Core Bluetooth per implementare il ruolo Central nell'architettura BLE su iOS. Gestisce l'intero ciclo di vita della connessione BLE: dalla scansione dei dispositivi in pubblicità al trasferimento dati e alla disconnessione. CBCentralManager funziona in modo asincrono tramite il delegato CBCentralManagerDelegate, notificando all'app gli eventi nello stack Bluetooth.
L'inizializzazione di CBCentralManager avvia il processo di state restoration: il manager verifica lo stato del Bluetooth sul dispositivo e ripristina le connessioni precedenti se l'app è stata chiusa. Il processo di inizializzazione può richiedere da 50 a 500 ms a seconda dello stato del Bluetooth. L'app deve attendere la chiamata centralManagerDidUpdateState prima di iniziare qualsiasi operazione BLE.
L'architettura di Core Bluetooth si basa sul pattern di Delegation: CBCentralManager delega la gestione degli eventi (scoperta dispositivi, connessione, errori) al protocollo CBCentralManagerDelegate. Per lavorare con una periferica specifica, viene utilizzato il protocollo CBPeripheralDelegate, che notifica la scoperta di servizi, caratteristiche e dati ricevuti. Questo modello asincrono garantisce un'interfaccia utente non bloccante.
CBCentralManager attraversa diversi stati che determinano se lo stack BLE è disponibile. Lo stato viene trasmesso tramite il delegato: centralManagerDidUpdateState(_:). Lo sviluppatore deve gestire tutti gli stati — non solo poweredOn, ma anche i casi in cui Bluetooth è spento o non disponibile.
| Stato | Valore | Azione dello sviluppatore |
|---|---|---|
| .poweredOn | Bluetooth è acceso e pronto | Avviare scansione |
| .poweredOff | Bluetooth è spento | Mostrare avviso all'utente |
| .unauthorized | Nessun permesso | Richiedere permesso in Impostazioni |
| .unsupported | Dispositivo non supporta BLE | Nascondere funzioni BLE |
| .unknown | Stato non definito | Aspettare prossimo aggiornamento |
| .resetting | Bluetooth si sta riavviando | Aspettare ripristino |
Stato non autorizzato sta diventando sempre più comune da iOS 13+. A partire da questa versione, l'app deve avere il permesso NSBluetoothAlwaysUsageDescription in Info.plist. Senza di esso, il manager centrale passa allo stato .unauthorized e la scansione è impossibile. L'utente può modificare il permesso in Impostazioni > Privacy > Bluetooth in qualsiasi momento.
scanForPeripherals(withServices:options:) è il metodo principale per avviare la scansione. Il parametro withServices accetta un array di UUID di servizio per filtrare: se viene passato nil, tutti i dispositivi verranno scoperti, aumentando significativamente il consumo energetico. Si consiglia di filtrare sempre per gli UUID di servizio necessari all'app. Le opzioni di scansione includono CBCentralManagerScanOptionAllowDuplicatesKey (notifiche ripetute sullo stesso dispositivo).
import CoreBluetooth
class BLEController: NSObject,
CBCentralManagerDelegate {
private var centralManager: CBCentralManager!
override init() {
super.init()
centralManager =
CBCentralManager(
delegate: self,
queue: nil
)
}
func startScanning() {
let serviceUUID =
CBUUID("180F") // Servizio batteria
centralManager.scanForPeripherals(
withServices: [serviceUUID],
options: [
CBCentralManagerScanOptionAllowDuplicatesKey: false
]
)
}
}
Quando un dispositivo viene scoperto, viene chiamato centralManager(_:didDiscover:advertisementData:rssi:). Il parametro advertisementData contiene il dizionario completo dei dati del pacchetto di pubblicità, inclusi il nome del dispositivo (CBAdvertisementDataLocalNameKey), gli UUID di servizio (CBAdvertisementDataServiceUUIDsKey) e i dati del produttore (CBAdvertisementDataManufacturerDataKey). RSSI è il livello del segnale in dBm disponibile al momento della scoperta.
connect(_:options:) è il metodo per stabilire una connessione BLE con una periferica scoperta. Dopo aver chiamato connect, iOS tenta di connettersi al dispositivo. Una connessione riuscita viene confermata tramite centralManager(_:didConnect:), un errore tramite centralManager(_:didFailToConnect:error:). Le opzioni di connessione includono CBConnectPeripheralOptionNotifyOnConnectionKey, CBConnectPeripheralOptionNotifyOnDisconnectionKey e CBConnectPeripheralOptionNotifyOnNotificationKey per notifiche in background.
// Connetti al dispositivo BLE
func connectToPeripheral(
_ peripheral: CBPeripheral
) {
centralManager.connect(peripheral, options: nil)
// Imposta delegato per periferica
peripheral.delegate = self
}
// Delegato: connessione riuscita
func centralManager(
_ central: CBCentralManager,
didConnect peripheral: CBPeripheral
) {
print("Connesso a " +
"\(peripheral.name ?? "unknown")")
// Avvia scoperta servizi
peripheral.discoverServices(nil)
}
// Delegato: errore di connessione
func centralManager(
_ central: CBCentralManager,
didFailToConnect peripheral: CBPeripheral,
error: Error?
) {
print("Connection failed:
\(error?.localizedDescription ?? "")")
}
Il timeout di connessione su iOS è di 30 secondi. Se il dispositivo non ha risposto alla richiesta di connessione entro questo tempo, viene chiamato didFailToConnect. Il timeout è influenzato da: distanza dal dispositivo, interferenze e se il dispositivo sta attualmente pubblicizzando. Prima di connetterti, assicurati che il dispositivo sia in modalità pubblicità connettibile (ADV_IND, non ADV_NONCONN_IND).
Dopo la connessione, è necessario scoprire i servizi (discoverServices) e le caratteristiche (discoverCharacteristics) della periferica. Questo è un passaggio obbligatorio prima di leggere o scrivere dati. Il processo è asincrono: discoverServices restituisce i risultati tramite peripheral(_:didDiscoverServices:), e discoverCharacteristics tramite peripheral(_:didDiscoverCharacteristicsFor:error:).
Si consiglia di passare un array di UUID pertinenti a discoverServices invece di nil. Il filtraggio accelera la scoperta e risparmia energia. Se un servizio non viene trovato, iOS segnalerà un array vuoto. Dopo aver scoperto le caratteristiche, è possibile leggere i loro valori (readValue), sottoscriversi alle notifiche (setNotifyValue) o scrivere dati (writeValue).
Una sfumatura importante: il MTU viene negoziato automaticamente dopo la connessione. Per ottenere il MTU corrente, usa peripheral.maximumWriteValueLength(for: .withResponse) o .withoutResponse. Su iOS, il MTU massimo è di 512 byte per dispositivi BLE 5.0. Se è necessario trasferire dati più grandi del MTU, implementa la frammentazione a livello di applicazione.
La scansione in background di dispositivi BLE su iOS richiede una configurazione speciale. Core Bluetooth supporta l'esecuzione in background, ma con limitazioni significative. Per lavorare in background, è necessario: attivare bluetooth-central in Background Modes nelle Capabilities del progetto, inizializzare CBCentralManager con l'opzione CBCentralManagerOptionRestoreIdentifierKey per il ripristino dello stato e gestire gli eventi del manager centrale quando si passa in background.
Limitazioni BLE in background su iOS: scanForPeripherals senza filtraggio UUID non funziona in background. L'app deve specificare UUID di servizio concreti per la scansione. iOS può ritardare la consegna degli eventi BLE indefinitamente. Core Bluetooth riprende automaticamente la scansione quando viene scoperto un dispositivo corrispondente, anche se l'app è in background. Timeout per la scansione in background: iOS può interrompere la scansione dopo 10–30 minuti per risparmiare energia.
State Restoration è un meccanismo di Core Bluetooth che consente di ripristinare le connessioni BLE dopo un riavvio dell'app o un riavvio di iOS. Per utilizzarlo: specifica CBCentralManagerOptionRestoreIdentifierKey durante l'inizializzazione, implementa centralManager(_:willRestoreState:) nel delegato e ripristina l'elenco delle periferiche connesse dal dizionario passato. State Restoration è una funzionalità critica per le app BLE che funzionano in background, come tracker fitness o dispositivi medici.
CBCentralManager genera errori in diversi scenari: connessione fallita (didFailToConnect), connessione persa (didDisconnectPeripheral), caratteristica non disponibile per lettura/scrittura (errore didWriteValue). Tutti gli errori di Core Bluetooth vengono restituiti tramite l'oggetto Error con dominio CBErrorDomain. I codici più comuni: CBErrorConnectionTimeout (0x04), CBErrorPeripheralDisconnected (0x07), CBErrorOperationNotSupported (0x0A).
Strategia di recupero connessione: quando ricevi didDisconnectPeripheral, controlla il codice di errore. Se l'errore è CBErrorConnectionTimeout o CBErrorPeripheralDisconnected — pianifica una riconnessione automatica dopo 1–5 secondi. Se l'errore è CBErrorOperationNotSupported — registralo e non ripetere l'operazione. Per connessioni critiche (dispositivi medici), usa backoff esponenziale con un intervallo massimo di 60 secondi.
// Gestisci disconnessione con riconnessione automatica
func centralManager(
_ central: CBCentralManager,
didDisconnectPeripheral peripheral: CBPeripheral,
error: Error?
) {
guard let error = error else {
return // Disconnessione prevista
}
print("Disconnected: \(error.localizedDescription)")
// Riconnessione automatica
if shouldAutoReconnect {
DispatchQueue.main.asyncAfter(
deadline: .now() + reconnectDelay
) {
central.connect(peripheral)
}
}
}
Quando sviluppi un'app BLE robusta su iOS, tieni presente: Core Bluetooth non garantisce la consegna di tutti i pacchetti con segnale debole. Per una trasmissione affidabile, usa writeType .withResponse (scrittura confermata) e sottoscriviti alle notifiche (setNotifyValue) per ricevere dati dalla periferica. Mantieni un registro degli errori per diagnosticare problemi di connessione in produzione.
Domande Frequenti
Controlla lo stato del manager tramite centralManagerDidUpdateState. Assicurati che il permesso NSBluetoothAlwaysUsageDescription sia in Info.plist, che Bluetooth sia acceso sul dispositivo e che la periferica stia pubblicizzando con il tipo corretto (pubblicità connettibile, non non-connettibile).
Su dispositivi con BLE 5.0 (iPhone 8 e successivi) — fino a 7 connessioni simultanee. Su dispositivi più vecchi — fino a 3–5. Il numero di dispositivi scansionati è illimitato, ma le connessioni attive hanno un limite rigido impostato dal Controller Bluetooth.
Si consiglia di scansionare con filtraggio UUID e fermare la scansione quando il dispositivo viene trovato. La scansione continua scarica la batteria: 1 ora di scansione ininterrotta consuma ~10–15% della carica dell'iPhone. Usa timer e condizioni per fermare la scansione.
CBCentralManager è per scansionare e connettersi a dispositivi BLE esterni (ruolo Central). CBPeripheralManager è per far sì che il tuo dispositivo iOS agisca come periferica BLE (pubblicizzi servizi). Un'istanza può essere solo in un ruolo.
Implementa centralManager(_:didDisconnectPeripheral:error:). Se l'errore non è nil — pianifica una riconnessione automatica con backoff esponenziale (1 s → 2 s → 4 s → 8 s → max 60 s). Se l'errore è nil — il dispositivo si è disconnesso normalmente (ad esempio, l'utente ha premuto un pulsante sul dispositivo).
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