Core Bluetooth è il framework di Apple per interagire con Bluetooth Low Energy su iOS, iPadOS e macOS. Il framework fornisce un set completo di API per operare in entrambi i ruoli BLE: dispositivo centrale (CBCentralManager) per scansionare e connettersi ai dispositivi periferici, e dispositivo periferico (CBPeripheralManager) per emulare un server BLE. Core Bluetooth astrae lo stack del protocollo BLE dalla radio fisica al profilo GATT a livello applicativo. Secondo Apple Developer, 2026, Core Bluetooth è l’unica API ufficiale di Apple per lo sviluppo BLE, supportando BLE 4.0–5.4 con extended advertising, 2M PHY e LE Audio.
Punti chiave
Core Bluetooth divide lo stack BLE in due ruoli logici definiti dalla specifica Bluetooth SIG. Il ruolo di dispositivo centrale (Central) è rappresentato dalla classe CBCentralManager — avvia la scansione, stabilisce le connessioni e gestisce l’elenco dei CBPeripheral connessi. Il ruolo di dispositivo periferico (Peripheral) è rappresentato da CBPeripheralManager — pubblica servizi e caratteristiche, risponde alle richieste centrali e invia notifiche. Una singola sessione iOS può operare simultaneamente in entrambi i ruoli su diverse radio BLE, ma un’applicazione tipica utilizza un solo ruolo.
L’architettura di Core Bluetooth include cinque astrazioni chiave. CBCentralManager gestisce lo stato dell’adattatore Bluetooth del dispositivo: poweredOn (pronto per funzionare), poweredOff (Bluetooth disattivato), unauthorized (nessuna autorizzazione), unsupported (BLE non disponibile). CBPeripheral rappresenta un dispositivo BLE remoto con il suo UUID, nome, RSSI e gerarchia GATT. CBService — un gruppo logico di caratteristiche. CBCharacteristic — un punto dati per lettura/scrittura/notifiche. CBPeripheralManager crea un server GATT locale per emulare un dispositivo periferico.
| Classe | Ruolo | Metodi principali |
|---|---|---|
| CBCentralManager | Dispositivo centrale | scanForPeripherals, connect, cancelPeripheralConnection, retrievePeripherals |
| CBPeripheral | Periferico remoto | discoverServices, discoverCharacteristics, readValue, writeValue, setNotifyValue |
| CBPeripheralManager | Periferico locale | addService, removeService, startAdvertising, respondToRequest, updateValue |
| CBCentral | Centrale remoto | maximumUpdateValueLength, identifier, ancsAuthorized |
Gli stati di CBCentralManager controllano tutte le operazioni BLE. All’avvio dell’app, centralManagerDidUpdateState viene chiamato con lo stato Bluetooth corrente. Se lo stato non è .poweredOn, qualsiasi chiamata BLE viene ignorata dal sistema. Lo sviluppatore deve verificare lo stato prima di ogni scansione e connessione. La transizione da .poweredOff a .poweredOn avviene quando il Bluetooth viene attivato nelle Impostazioni iOS — il delegato riceve una chiamata ripetuta e l’app può riprendere la scansione.
CBCentralManager è il punto di ingresso per tutte le operazioni BLE dal lato del dispositivo centrale. L’inizializzazione accetta un delegato (CBCentralManagerDelegate) e una DispatchQueue — Apple raccomanda di usare la coda principale per semplicità o una coda seriale per le prestazioni. Dopo l’inizializzazione, il framework verifica automaticamente lo stato Bluetooth e chiama centralManagerDidUpdateState: — il primo delegato obbligatorio da gestire.
La scansione inizia con il metodo scanForPeripheralsWithServices:options:. Il primo parametro è un array di CBUUID dei servizi per filtrare: se gli UUID dei servizi desiderati sono noti, passarli riduce il consumo energetico e il tempo di ricerca. Se è nil, vengono scoperti tutti i dispositivi BLE nel raggio d’azione. Le opzioni includono .allowDuplicatesKey (scoperte ripetute dello stesso dispositivo) e .solicitedServiceUUIDsKey (per i servizi pubblicati sul centrale).
import CoreBluetooth
class BLECentral: NSObject {
private var centralManager: CBCentralManager!
private var discoveredPeripherals: [CBPeripheral] = []
override init() {
super.init()
centralManager = CBCentralManager(delegate: self, queue: .main)
}
// Avviare la scansione BLE
func startScan() {
guard centralManager.state == .poweredOn else {
print("Bluetooth non disponibile")
return
}
// Scansionare tutti i dispositivi (nil = nessun filtro)
centralManager.scanForPeripherals(withServices: nil,
options: [CBCentralManagerScanOptionAllowDuplicatesKey: true])
}
// Fermare la scansione
func stopScan() {
centralManager.stopScan()
}
// Connettersi al dispositivo selezionato
func connect(to peripheral: CBPeripheral) {
centralManager.connect(peripheral, options: nil)
}
}
// MARK: - CBCentralManagerDelegate
extension BLECentral: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
startScan()
}
}
func centralManager(_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral,
advertisementData: [String : Any],
rssi: NSNumber) {
if !discoveredPeripherals.contains(where: { $0.identifier == peripheral.identifier }) {
discoveredPeripherals.append(peripheral)
print("Found devices: \(peripheral.name ?? "Unknown"), RSSI: \(rssi)")
}
}
func centralManager(_ central: CBCentralManager,
didConnect peripheral: CBPeripheral) {
print("Connected: \(peripheral.identifier)")
peripheral.delegate = self
peripheral.discoverServices(nil)
}
func centralManager(_ central: CBCentralManager,
didDisconnectPeripheral peripheral: CBPeripheral,
error: Error?) {
print("Disconnected: \(peripheral.identifier)")
}
}
La classe BLECentral dimostra il ciclo completo di scansione e connessione dei dispositivi BLE. centralManagerDidUpdateState avvia la scansione quando il Bluetooth è attivato. didDiscoverPeripheral raccoglie i dispositivi trovati nell’array discoveredPeripherals con deduplicazione per identifier. Dopo la connessione (didConnect), la scoperta dei servizi inizia immediatamente — questo è un passaggio obbligatorio prima di qualsiasi operazione GATT.
CBPeripheralManager è la classe per emulare un dispositivo periferico BLE su iOS. Un’app nel ruolo periferico può pubblicare i propri servizi e caratteristiche, accettare richieste di lettura/scrittura in arrivo da un dispositivo centrale e inviare notifiche. CBPeripheralManager viene utilizzato per accessori BLE emulati da iPhone: telecomandi, tastiere, tracker, gateway IoT.
Il ciclo di vita di CBPeripheralManager inizia con l’inizializzazione e il delegato CBPeripheralManagerDelegate. Dopo aver ricevuto la conferma di poweredOn tramite peripheralManagerDidUpdateState:, i servizi vengono pubblicati (addService:) e l’advertising viene avviato (startAdvertising:). I dati di advertising CBAdvertisementData includono il nome locale (CBAdvertisementDataLocalNameKey), gli UUID dei servizi (CBAdvertisementDataServiceUUIDsKey) e il livello di potenza di trasmissione (CBAdvertisementDataTxPowerLevelKey). La dimensione massima del pacchetto di advertising è di 31 byte per BLE 4.0, 251 byte per extended advertising BLE 5.0+.
// Periferico BLE su iOS tramite CBPeripheralManager
class BLEPeripheral: NSObject {
private var peripheralManager: CBPeripheralManager!
let serviceUUID = CBUUID(string: "1234")
let characteristicUUID = CBUUID(string: "5678")
override init() {
super.init()
peripheralManager = CBPeripheralManager(delegate: self, queue: .main)
}
// Pubblicare servizio con caratteristica
func setupService() {
let characteristic = CBMutableCharacteristic(
type: characteristicUUID,
properties: [.read, .write, .notify],
value: nil,
permissions: [.readable, .writeable]
)
let service = CBMutableService(type: serviceUUID, primary: true)
service.characteristics = [characteristic]
peripheralManager.add(service)
}
// Avviare advertising
func startAdvertising() {
let advertisementData: [String: Any] = [
CBAdvertisementDataLocalNameKey: "My BLE Device",
CBAdvertisementDataServiceUUIDsKey: [serviceUUID]
]
peripheralManager.startAdvertising(advertisementData)
}
}
// MARK: - CBPeripheralManagerDelegate
extension BLEPeripheral: CBPeripheralManagerDelegate {
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
if peripheral.state == .poweredOn {
setupService()
}
}
func peripheralManager(_ peripheral: CBPeripheralManager,
didAdd service: CBService,
error: Error?) {
if error == nil {
startAdvertising()
}
}
// Gestire richiesta di lettura
func peripheralManager(_ peripheral: CBPeripheralManager,
didReceiveRead request: CBATTRequest) {
let data = "CurrentValue".data(using: .utf8)!
request.value = data
peripheralManager.respond(to: request, withResult: .success)
}
// Gestire richiesta di scrittura
func peripheralManager(_ peripheral: CBPeripheralManager,
didReceiveWrite requests: [CBATTRequest]) {
for request in requests {
if let value = request.value {
print("Write: \(value)")
}
}
peripheralManager.respond(to: requests.first!, withResult: .success)
}
}
La classe BLEPeripheral crea un server BLE con una singola caratteristica che supporta lettura, scrittura e notifiche. Dopo l’inizializzazione, peripheralManagerDidUpdateState pubblica il servizio tramite addService:, quindi avvia l’advertising tramite startAdvertising:. I gestori didReceiveRead e didReceiveWrite rispondono alle richieste GATT in arrivo dal dispositivo centrale. Il metodo updateValue:forCharacteristic:onSubscribedCentrals: viene utilizzato per inviare notifiche.
Le operazioni GATT (Generic Attribute Profile) sono il fondamento dello scambio di dati in Core Bluetooth. Dopo la scoperta di servizi e caratteristiche, il dispositivo centrale può eseguire tre tipi di operazioni: leggere un valore di caratteristica, scrivere un valore e sottoscrivere notifiche/indicazioni. Ogni operazione è asincrona e restituisce il risultato tramite il corrispondente delegato CBPeripheralDelegate.
Lettura: viene eseguita chiamando readValueForCharacteristic:. Il valore arriva in peripheral:didUpdateValueForCharacteristic:error:. Importante: la lettura restituisce il valore corrente dal dispositivo, non uno memorizzato nella cache. Se il dispositivo non supporta la lettura (proprietà .read), la chiamata restituirà un errore. Per valori grandi (più grandi di MTU), BLE frammenta e riassembla automaticamente i dati a livello GATT.
Scrittura: viene eseguita chiamando writeValue:forCharacteristic:type:. BLE supporta due modelli di scrittura: withResponse (affidabile, con conferma) e withoutResponse (veloce, senza conferma). La proprietà CBCharacteristic.properties definisce i tipi di scrittura disponibili. La dimensione massima di un singolo pacchetto di scrittura è limitata da MTU: 23 byte per BLE 4.0 (20 byte di payload + 3 byte di intestazione), fino a 247 byte per BLE 5.0 con MTU esteso (MTU 251).
Notifiche: vengono attivate chiamando setNotifyValue:true forCharacteristic:. Dopo la sottoscrizione, il dispositivo periferico invia automaticamente aggiornamenti tramite peripheral:didUpdateValueForCharacteristic: ogni volta che il valore della caratteristica cambia. Per disattivare le notifiche, chiamare setNotifyValue:false forCharacteristic:. Core Bluetooth gestisce automaticamente il descrittore CCCD sul periferico.
| Operazione | Metodo | Delegato | Tipo di trasferimento |
|---|---|---|---|
| Lettura | readValueForCharacteristic: | didUpdateValueForCharacteristic | Polling (richiesta-risposta) |
| Scrittura withResponse | writeValue:forCharacteristic:type:withResponse | didWriteValueForCharacteristic | Con conferma |
| Scrittura withoutResponse | writeValue:forCharacteristic:type:withoutResponse | Nessun delegato | Senza conferma |
| Notifica | setNotifyValue:true forCharacteristic: | didUpdateNotificationStateForCharacteristic + didUpdateValueForCharacteristic | Push dal periferico |
La modalità background di Core Bluetooth consente alle app BLE di continuare a scansionare, mantenere le connessioni e ricevere notifiche mentre sono in background. Per attivarla, è necessario abilitare la capacità “Uses Bluetooth LE accessories” in Xcode (Info.plist → Required background modes → App communicates using Core Bluetooth) e aggiungere la chiave “bluetooth-central” a UIBackgroundModes. Per il ruolo periferico — “bluetooth-peripheral”.
State Restoration è un meccanismo di Core Bluetooth per ripristinare lo stato delle connessioni BLE dopo un riavvio dell’app da parte di iOS. Quando la modalità background è attiva e viene specificato un restoreIdentifier nell’inizializzazione di CBCentralManager o CBPeripheralManager, iOS salva lo stato dello stack BLE alla chiusura dell’app e lo ripristina al successivo avvio. Il delegato centralManager:willRestoreState: riceve un dizionario con i CBPeripheral salvati e le connessioni in sospeso.
// Configurazione di Core Bluetooth con State Restoration
class BLECentralWithRestoration: NSObject {
let restoreIdentifier = "com.app.blecentral"
private var centralManager: CBCentralManager!
override init() {
super.init()
let options: [String: Any] = [
CBCentralManagerOptionRestoreIdentifierKey: restoreIdentifier,
CBCentralManagerOptionShowPowerAlertKey: true
]
centralManager = CBCentralManager(delegate: self,
queue: nil,
options: options)
}
}
extension BLECentralWithRestoration: CBCentralManagerDelegate {
// Ripristinare lo stato dopo il riavvio
func centralManager(_ central: CBCentralManager,
willRestoreState dict: [String : Any]) {
if let peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey]
as? [CBPeripheral] {
for peripheral in peripherals {
peripheral.delegate = self
// Ripristinare la scoperta GATT
peripheral.discoverServices(nil)
}
}
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
print("Bluetooth pronto dopo il ripristino")
}
}
}
Nella configurazione BLECentralWithRestoration, la chiave CBCentralManagerOptionRestoreIdentifierKey attiva la conservazione dello stato. Se l’app è stata terminata da iOS (ad esempio, per pressione della memoria), al successivo avvio centralManager:willRestoreState: riceve un elenco dei CBPeripheral precedentemente connessi. L’app ripristina i delegati ed esegue una riscoperta dei servizi — l’utente non nota l’interruzione della connessione. Senza State Restoration, tutte le sessioni BLE vengono perse alla chiusura dell’app.
Un esempio completo di un’app BLE in Swift combina dispositivi centrali e periferici in un unico progetto. L’app può funzionare in due modalità: scoprire e connettersi a dispositivi BLE (Centrale) o emulare un accessorio BLE (Periferico). Di seguito è riportata un’architettura con un gestore BLE comune che seleziona il ruolo all’avvio.
// Gestore BLE universale per centrale e periferico
class BLEManager {
enum Role {
case central
case peripheral
}
private let role: Role
private var centralManager: CBCentralManager?
private var peripheralManager: CBPeripheralManager?
let advertisedServiceUUID = CBUUID(string: "A001")
init(role: Role) {
self.role = role
switch role {
case .central:
centralManager = CBCentralManager(delegate: nil, queue: .main)
case .peripheral:
peripheralManager = CBPeripheralManager(delegate: nil, queue: .main)
}
}
// Dispositivo centrale: scansione
func scanForDevices() {
centralManager?.scanForPeripherals(withServices: nil, options: nil)
}
// Dispositivo periferico: advertising
func advertiseService() {
let data: [String: Any] = [
CBAdvertisementDataServiceUUIDsKey: [advertisedServiceUUID]
]
peripheralManager?.startAdvertising(data)
}
}
// Utilizzo all'avvio
let isCentral = UserDefaults.standard.bool(forKey: "isCentral")
let manager = BLEManager(role: isCentral ? .central : .peripheral)
if isCentral {
manager.scanForDevices()
} else {
manager.advertiseService()
}
Il BLEManager seleziona il ruolo all’inizializzazione e crea il Manager corrispondente (CBCentralManager o CBPeripheralManager). Il flag del ruolo può essere memorizzato in UserDefaults o passato tramite un server di configurazione. Questo approccio consente all’app BLE di adattarsi al caso d’uso: in un punto vendita, un iPhone funge da centrale per scansionare i terminali di pagamento; su un gateway IoT — come periferico per raccogliere dati dai sensori.
Domande frequenti
Core Bluetooth è il framework di Apple per lo sviluppo BLE su iOS, iPadOS e macOS. Fornisce API sia per dispositivi centrali (CBCentralManager) che periferici (CBPeripheralManager). Supporta BLE 4.0–5.4, extended advertising, 2M PHY e LE Audio. Core Bluetooth è l’unica API ufficiale di Apple per la comunicazione BLE, necessaria per tutte le app iOS che lavorano con Bluetooth Low Energy.
CBCentralManager è una classe per operare nel ruolo di dispositivo centrale: scansiona i dispositivi periferici BLE, stabilisce connessioni, legge e scrive caratteristiche. CBPeripheralManager è una classe per operare nel ruolo periferico: pubblica servizi, risponde a richieste di lettura/scrittura e invia notifiche. Un singolo iPhone può funzionare in entrambi i ruoli simultaneamente attraverso diverse istanze di gestori.
Per il funzionamento BLE in background, abilitare la capacità “Uses Bluetooth LE accessories” in Xcode e aggiungere la chiave “bluetooth-central” a UIBackgroundModes. Per il ruolo periferico — “bluetooth-peripheral”. Specificare un restoreIdentifier durante l’inizializzazione del gestore per State Restoration. Senza queste impostazioni, l’app in background non riceverà eventi BLE e perderà le connessioni.
Ragioni comuni: CBCentralManager.state != .poweredOn (Bluetooth disattivato o non autorizzato), delegato non impostato, dispositivo fuori portata o che non invia pacchetti di advertising. Verificare l’autorizzazione NSBluetoothAlwaysUsageDescription in Info.plist, lo stato Bluetooth in centralManagerDidUpdateState e assicurarsi che scanForPeripherals venga chiamato solo quando .poweredOn.
Sì, Core Bluetooth supporta connessioni simultanee a più dispositivi BLE. Ogni CBPeripheral viene gestito indipendentemente tramite il proprio delegato. iOS limita il numero di connessioni BLE simultanee a livello di sistema (tipicamente 5–7 per iPhone). Per scenari 1:N (ad esempio, un centro fitness con 10 tracker), è necessario mettere in coda e servire ciclicamente i dispositivi periferici.
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