CBCentralManager es la clase central del framework Core Bluetooth en iOS que gestiona el escaneo, la conexión y la interacción con dispositivos periféricos BLE. Core Bluetooth (iOS 5+, 2011) proporciona una abstracción de alto nivel sobre la pila BLE a nivel GATT, ocultando al desarrollador los detalles de Link Layer y HCI. CBCentralManager implementa el rol Central: escanea el aire mediante scanForPeripherals, inicia la conexión mediante connect, descubre servicios mediante discoverServices y gestiona la transferencia de datos. Según la Documentación para Desarrolladores de Apple (2024), CBCentralManager admite hasta 7 conexiones simultáneas a dispositivos BLE en dispositivos con BLE 5.0.
Puntos Clave
CBCentralManager es la clase principal de Core Bluetooth para implementar el rol Central en la arquitectura BLE en iOS. Gestiona todo el ciclo de vida de la conexión BLE: desde el escaneo de dispositivos que se anuncian hasta la transferencia de datos y la desconexión. CBCentralManager funciona de forma asíncrona a través del delegado CBCentralManagerDelegate, notificando a la aplicación sobre eventos en la pila Bluetooth.
La inicialización de CBCentralManager inicia el proceso de restauración de estado: el gestor verifica el estado de Bluetooth en el dispositivo y restaura conexiones anteriores si la aplicación se cerró. El proceso de inicialización puede tomar de 50 a 500 ms dependiendo del estado de Bluetooth. La aplicación debe esperar la llamada centralManagerDidUpdateState antes de iniciar cualquier operación BLE.
La arquitectura de Core Bluetooth se basa en el patrón de Delegación: CBCentralManager delega el manejo de eventos (descubrimiento de dispositivos, conexión, errores) al protocolo CBCentralManagerDelegate. Para trabajar con un Periférico específico, se utiliza el protocolo CBPeripheralDelegate, que notifica sobre servicios descubiertos, características y datos recibidos. Este modelo asíncrono garantiza una interfaz de usuario sin bloqueos.
CBCentralManager pasa por varios estados que determinan si la pila BLE está disponible. El estado se transmite a través del delegado: centralManagerDidUpdateState(_:). El desarrollador debe manejar todos los estados, no solo poweredOn, sino también los casos en que Bluetooth está apagado o no disponible.
| Estado | Valor | Acción del Desarrollador |
|---|---|---|
| .poweredOn | Bluetooth está encendido y listo | Iniciar escaneo |
| .poweredOff | Bluetooth está apagado | Mostrar alerta al usuario |
| .unauthorized | Sin permiso | Solicitar permiso en Ajustes |
| .unsupported | El dispositivo no admite BLE | Ocultar funciones BLE |
| .unknown | Estado no definido | Esperar la siguiente actualización |
| .resetting | Bluetooth se está reiniciando | Esperar recuperación |
Estado no autorizado es cada vez más común desde iOS 13+. A partir de esta versión, la aplicación debe tener el permiso NSBluetoothAlwaysUsageDescription en Info.plist. Sin él, el gestor central pasa al estado .unauthorized y el escaneo es imposible. El usuario puede cambiar el permiso en Ajustes > Privacidad > Bluetooth en cualquier momento.
scanForPeripherals(withServices:options:) es el método principal para iniciar el escaneo. El parámetro withServices acepta un array de UUID de servicio para filtrar: si se pasa nil, se descubrirán todos los dispositivos, lo que aumenta significativamente el consumo de energía. Se recomienda filtrar siempre por los UUID de servicio que necesita la aplicación. Las opciones de escaneo incluyen CBCentralManagerScanOptionAllowDuplicatesKey (notificaciones repetidas sobre el mismo 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") // Servicio de Batería
centralManager.scanForPeripherals(
withServices: [serviceUUID],
options: [
CBCentralManagerScanOptionAllowDuplicatesKey: false
]
)
}
}
Cuando se descubre un dispositivo, se llama a centralManager(_:didDiscover:advertisementData:rssi:). El parámetro advertisementData contiene el diccionario completo de datos del paquete de anuncio, incluyendo el nombre del dispositivo (CBAdvertisementDataLocalNameKey), los UUID de servicio (CBAdvertisementDataServiceUUIDsKey) y los datos del fabricante (CBAdvertisementDataManufacturerDataKey). RSSI es el nivel de señal en dBm disponible en el momento del descubrimiento.
connect(_:options:) es el método para establecer una conexión BLE con un Periférico descubierto. Después de llamar a connect, iOS intenta conectarse al dispositivo. Una conexión exitosa se confirma mediante centralManager(_:didConnect:), un error mediante centralManager(_:didFailToConnect:error:). Las opciones de conexión incluyen CBConnectPeripheralOptionNotifyOnConnectionKey, CBConnectPeripheralOptionNotifyOnDisconnectionKey y CBConnectPeripheralOptionNotifyOnNotificationKey para notificaciones en segundo plano.
// Conectar al dispositivo BLE
func connectToPeripheral(
_ peripheral: CBPeripheral
) {
centralManager.connect(peripheral, options: nil)
// Establecer delegado para Periférico
peripheral.delegate = self
}
// Delegado: conexión exitosa
func centralManager(
_ central: CBCentralManager,
didConnect peripheral: CBPeripheral
) {
print("Conectado a " +
"\(peripheral.name ?? "unknown")")
// Iniciar descubrimiento de servicios
peripheral.discoverServices(nil)
}
// Delegado: error de conexión
func centralManager(
_ central: CBCentralManager,
didFailToConnect peripheral: CBPeripheral,
error: Error?
) {
print("Connection failed:
\(error?.localizedDescription ?? "")")
}
El tiempo de espera de conexión en iOS es de 30 segundos. Si el dispositivo no ha respondido a la solicitud de conexión en este tiempo, se llama a didFailToConnect. El tiempo de espera se ve afectado por: la distancia al dispositivo, las interferencias y si el dispositivo se está anunciando actualmente. Antes de conectar, asegúrese de que el dispositivo esté en modo de anuncio conectable (ADV_IND, no ADV_NONCONN_IND).
Después de la conexión, debe descubrir los servicios (discoverServices) y las características (discoverCharacteristics) del Periférico. Este es un paso obligatorio antes de leer o escribir datos. El proceso es asíncrono: discoverServices devuelve resultados mediante peripheral(_:didDiscoverServices:), y discoverCharacteristics mediante peripheral(_:didDiscoverCharacteristicsFor:error:).
Se recomienda pasar un array de UUID relevantes a discoverServices en lugar de nil. El filtrado acelera el descubrimiento y ahorra energía. Si un servicio no se encuentra, iOS informará un array vacío. Después de descubrir las características, puede leer sus valores (readValue), suscribirse a notificaciones (setNotifyValue) o escribir datos (writeValue).
Un matiz importante: el MTU se negocia automáticamente después de la conexión. Para obtener el MTU actual, use peripheral.maximumWriteValueLength(for: .withResponse) o .withoutResponse. En iOS, el MTU máximo es de 512 bytes para dispositivos BLE 5.0. Si necesita transferir datos más grandes que el MTU, implemente la fragmentación a nivel de aplicación.
El escaneo en segundo plano de dispositivos BLE en iOS requiere configuración especial. Core Bluetooth admite la ejecución en segundo plano, pero con limitaciones significativas. Para trabajar en segundo plano, necesita: activar bluetooth-central en Background Modes en las Capacidades del proyecto, inicializar CBCentralManager con la opción CBCentralManagerOptionRestoreIdentifierKey para la restauración de estado y manejar los eventos del gestor central al pasar a segundo plano.
Limitaciones de BLE en segundo plano en iOS: scanForPeripherals sin filtrado por UUID no funciona en segundo plano. La aplicación debe especificar UUID de servicio concretos para el escaneo. iOS puede retrasar la entrega de eventos BLE indefinidamente. Core Bluetooth reanuda automáticamente el escaneo cuando se descubre un dispositivo coincidente, incluso si la aplicación está en segundo plano. Tiempo de espera para escaneo en segundo plano: iOS puede detener el escaneo después de 10–30 minutos para ahorrar energía.
Restauración de Estado es un mecanismo de Core Bluetooth que permite restaurar conexiones BLE después de un reinicio de la aplicación o del sistema iOS. Para usarlo: especifique CBCentralManagerOptionRestoreIdentifierKey durante la inicialización, implemente centralManager(_:willRestoreState:) en el delegado y restaure la lista de Periféricos conectados desde el diccionario pasado. La Restauración de Estado es una funcionalidad crítica para aplicaciones BLE que funcionan en segundo plano, como rastreadores de fitness o dispositivos médicos.
CBCentralManager genera errores en varios escenarios: conexión fallida (didFailToConnect), conexión perdida (didDisconnectPeripheral), característica no disponible para lectura/escritura (error didWriteValue). Todos los errores de Core Bluetooth se devuelven mediante el objeto Error con dominio CBErrorDomain. Los códigos más comunes: CBErrorConnectionTimeout (0x04), CBErrorPeripheralDisconnected (0x07), CBErrorOperationNotSupported (0x0A).
Estrategia de recuperación de conexión: al recibir didDisconnectPeripheral, verifique el código de error. Si el error es CBErrorConnectionTimeout o CBErrorPeripheralDisconnected — programe una reconexión automática en 1–5 segundos. Si el error es CBErrorOperationNotSupported — regístrelo y no intente repetir la operación. Para conexiones críticas (dispositivos médicos), use retroceso exponencial con un intervalo máximo de 60 segundos.
// Manejar desconexión con reconexión automática
func centralManager(
_ central: CBCentralManager,
didDisconnectPeripheral peripheral: CBPeripheral,
error: Error?
) {
guard let error = error else {
return // Desconexión esperada
}
print("Disconnected: \(error.localizedDescription)")
// Reconexión automática
if shouldAutoReconnect {
DispatchQueue.main.asyncAfter(
deadline: .now() + reconnectDelay
) {
central.connect(peripheral)
}
}
}
Al desarrollar una aplicación BLE robusta en iOS, tenga en cuenta: Core Bluetooth no garantiza la entrega de todos los paquetes con señal débil. Para una transmisión confiable, use writeType .withResponse (escritura confirmada) y suscríbase a notificaciones (setNotifyValue) para recibir datos del Periférico. Mantenga un registro de errores para diagnosticar problemas de conexión en producción.
Preguntas Frecuentes
Verifique el estado del gestor mediante centralManagerDidUpdateState. Asegúrese de que el permiso NSBluetoothAlwaysUsageDescription esté en Info.plist, Bluetooth esté activado en el dispositivo y el periférico se esté anunciando con el tipo correcto (anuncio conectable, no no conectable).
En dispositivos con BLE 5.0 (iPhone 8 y posteriores) — hasta 7 conexiones simultáneas. En dispositivos más antiguos — hasta 3–5. El número de dispositivos escaneados es ilimitado, pero las conexiones activas tienen un límite estricto establecido por el Controlador Bluetooth.
Se recomienda escanear con filtrado por UUID y detener el escaneo cuando se encuentre el dispositivo. El escaneo continuo agota la batería: 1 hora de escaneo ininterrumpido consume ~10–15% de la carga del iPhone. Use temporizadores y condiciones para detener el escaneo.
CBCentralManager es para escanear y conectarse a dispositivos BLE externos (rol Central). CBPeripheralManager es para que su dispositivo iOS actúe como periférico BLE (anuncie servicios). Una instancia solo puede estar en un rol.
Implemente centralManager(_:didDisconnectPeripheral:error:). Si el error no es nil — programe una reconexión automática con retroceso exponencial (1 s → 2 s → 4 s → 8 s → máx 60 s). Si el error es nil — el dispositivo se desconectó normalmente (por ejemplo, el usuario presionó un botón en el dispositivo).
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también