Core Bluetooth: arquitectura y desarrollo BLE en iOS

Autor: IT Sectr Publicado: 2026-07-16 Tiempo de lectura: 10 min

Core Bluetooth es el framework de Apple para interactuar con Bluetooth Low Energy en iOS, iPadOS y macOS. El framework proporciona un conjunto completo de API para operar en ambos roles BLE: dispositivo central (CBCentralManager) para escanear y conectarse a periféricos, y dispositivo periférico (CBPeripheralManager) para emular un servidor BLE. Core Bluetooth abstrae la pila del protocolo BLE desde la radio física hasta el perfil GATT de aplicación. Según Apple Developer, 2026, Core Bluetooth es la única API oficial de Apple para desarrollo BLE, compatible con BLE 4.0–5.4, extended advertising, 2M PHY y LE Audio.

Lo más importante

  • Core Bluetooth — framework de sistema de Apple para desarrollo BLE en iOS, iPadOS y macOS
  • CBCentralManager — clase para escanear y conectarse a periféricos BLE desde el dispositivo central
  • CBPeripheralManager — clase para crear un servidor BLE que publica servicios y características
  • Perfil GATT — modelo jerárquico de servicios, características y descriptores para el intercambio de datos
  • Modos en segundo plano — Core Bluetooth admite comunicación BLE en segundo plano mediante delegados del sistema y restauración de estado

Qué es Core Bluetooth: arquitectura y componentes

Core Bluetooth divide la pila BLE en dos roles lógicos definidos por la especificación Bluetooth SIG. El rol de dispositivo central (Central) está representado por la clase CBCentralManager — inicia el escaneo, establece conexiones y gestiona la lista de CBPeripheral conectados. El rol de dispositivo periférico (Peripheral) está representado por CBPeripheralManager — publica servicios y características, responde a solicitudes centrales y envía notificaciones. Una misma sesión de iOS puede operar simultáneamente en ambos roles en diferentes radios BLE, pero una aplicación típica usa un solo rol.

La arquitectura de Core Bluetooth incluye cinco abstracciones clave. CBCentralManager gestiona el estado del adaptador Bluetooth del dispositivo: poweredOn (listo para funcionar), poweredOff (Bluetooth desactivado), unauthorized (sin permiso), unsupported (BLE no disponible). CBPeripheral representa un dispositivo BLE remoto con su UUID, nombre, RSSI y jerarquía GATT. CBService — un grupo lógico de características. CBCharacteristic — un punto de datos para lectura/escritura/notificaciones. CBPeripheralManager crea un servidor GATT local para emular un periférico.

ClaseRolMétodos principales
CBCentralManagerDispositivo centralscanForPeripherals, connect, cancelPeripheralConnection, retrievePeripherals
CBPeripheralPeriférico remotodiscoverServices, discoverCharacteristics, readValue, writeValue, setNotifyValue
CBPeripheralManagerPeriférico localaddService, removeService, startAdvertising, respondToRequest, updateValue
CBCentralCentral remotomaximumUpdateValueLength, identifier, ancsAuthorized

Los estados de CBCentralManager controlan todas las operaciones BLE. Cuando la aplicación se inicia, centralManagerDidUpdateState se llama con el estado actual de Bluetooth. Si el estado no es .poweredOn, el sistema ignora cualquier llamada BLE. El desarrollador debe verificar el estado antes de cada escaneo y conexión. La transición de .poweredOff a .poweredOn ocurre cuando se activa Bluetooth en la configuración de iOS — el delegado recibe una llamada repetida y la aplicación puede reanudar el escaneo.

CBCentralManager: escaneo y conexión de dispositivos BLE

CBCentralManager es el punto de entrada para todas las operaciones BLE del dispositivo central. La inicialización acepta un delegado (CBCentralManagerDelegate) y una DispatchQueue — Apple recomienda usar la cola principal para simplicidad o una cola serial para rendimiento. Después de la inicialización, el framework verifica automáticamente el estado de Bluetooth y llama a centralManagerDidUpdateState: — el primer delegado obligatorio a manejar.

El escaneo se inicia con el método scanForPeripheralsWithServices:options:. El primer parámetro es un array de CBUUID de servicios para filtrar: si se conocen los UUID de los servicios deseados, pasarlos reduce el consumo de energía y el tiempo de búsqueda. Si es nil, se descubren todos los dispositivos BLE en el alcance. Las opciones incluyen .allowDuplicatesKey (descubrimientos repetidos del mismo dispositivo) y .solicitedServiceUUIDsKey (para servicios publicados en el central).

swift
import CoreBluetooth

class BLECentral: NSObject {

    private var centralManager: CBCentralManager!
    private var discoveredPeripherals: [CBPeripheral] = []

    override init() {
        super.init()
        centralManager = CBCentralManager(delegate: self, queue: .main)
    }

    // Iniciar escaneo BLE
    func startScan() {
        guard centralManager.state == .poweredOn else {
            print("Bluetooth no disponible")
            return
        }
        // Escanear todos los dispositivos (nil = sin filtro)
        centralManager.scanForPeripherals(withServices: nil,
                                            options: [CBCentralManagerScanOptionAllowDuplicatesKey: true])
    }

    // Detener escaneo
    func stopScan() {
        centralManager.stopScan()
    }

    // Conectar al dispositivo seleccionado
    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 clase BLECentral demuestra el ciclo completo de escaneo y conexión de dispositivos BLE. centralManagerDidUpdateState inicia el escaneo cuando Bluetooth está activado. didDiscoverPeripheral recopila los dispositivos encontrados en el array discoveredPeripherals con deduplicación por identifier. Después de la conexión (didConnect), el descubrimiento de servicios se inicia inmediatamente — este es un paso obligatorio antes de cualquier operación GATT.

CBPeripheralManager: creación de un servidor BLE en iOS

CBPeripheralManager es la clase para emular un dispositivo periférico BLE en iOS. Una aplicación en el rol periférico puede publicar sus servicios y características, aceptar solicitudes entrantes de lectura/escritura desde un dispositivo central y enviar notificaciones. CBPeripheralManager se usa para accesorios BLE emulados por iPhone: controles remotos, teclados, rastreadores, puertas de enlace IoT.

El ciclo de vida de CBPeripheralManager comienza con la inicialización y el delegado CBPeripheralManagerDelegate. Después de recibir la confirmación de poweredOn a través de peripheralManagerDidUpdateState:, se publican los servicios (addService:) y se inicia la publicidad (startAdvertising:). Los datos publicitarios CBAdvertisementData incluyen el nombre local (CBAdvertisementDataLocalNameKey), UUID de servicios (CBAdvertisementDataServiceUUIDsKey) y nivel de potencia de transmisión (CBAdvertisementDataTxPowerLevelKey). El tamaño máximo del paquete publicitario es de 31 bytes para BLE 4.0 y 251 bytes para extended advertising BLE 5.0+.

swift
// Periférico BLE en iOS mediante 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)
    }

    // Publicar servicio con característica
    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)
    }

    // Iniciar publicidad
    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()
        }
    }

    // Manejar solicitud de lectura
    func peripheralManager(_ peripheral: CBPeripheralManager,
                        didReceiveRead request: CBATTRequest) {
        let data = "CurrentValue".data(using: .utf8)!
        request.value = data
        peripheralManager.respond(to: request, withResult: .success)
    }

    // Manejar solicitud de escritura
    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 clase BLEPeripheral crea un servidor BLE con una sola característica que admite lectura, escritura y notificaciones. Después de la inicialización, peripheralManagerDidUpdateState publica el servicio mediante addService:, luego inicia la publicidad con startAdvertising:. Los manejadores didReceiveRead y didReceiveWrite responden a las solicitudes GATT entrantes del dispositivo central. El método updateValue:forCharacteristic:onSubscribedCentrals: se usa para enviar notificaciones.

Operaciones GATT: lectura, escritura y notificaciones

Las operaciones GATT (Generic Attribute Profile) son la base del intercambio de datos en Core Bluetooth. Después del descubrimiento de servicios y características, el dispositivo central puede realizar tres tipos de operaciones: leer un valor de característica, escribir un valor y suscribirse a notificaciones/indicaciones. Cada operación es asíncrona y devuelve el resultado a través del delegado CBPeripheralDelegate correspondiente.

Lectura: se realiza llamando a readValueForCharacteristic:. El valor llega en peripheral:didUpdateValueForCharacteristic:error:. Importante: la lectura devuelve el valor actual del dispositivo, no uno en caché. Si el dispositivo no admite lectura (propiedad .read), la llamada devolverá un error. Para valores grandes (mayores que MTU), BLE fragmenta y reensambla automáticamente los datos a nivel GATT.

Escritura: se realiza llamando a writeValue:forCharacteristic:type:. BLE admite dos modelos de escritura: withResponse (confiable, con confirmación) y withoutResponse (rápido, sin confirmación). La propiedad CBCharacteristic.properties define los tipos de escritura disponibles. El tamaño máximo de un solo paquete de escritura está limitado por MTU: 23 bytes para BLE 4.0 (20 bytes de datos útiles + 3 bytes de encabezado), hasta 247 bytes para BLE 5.0 con MTU extendido (MTU 251).

Notificaciones: se activan llamando a setNotifyValue:true forCharacteristic:. Después de la suscripción, el periférico envía actualizaciones automáticamente mediante peripheral:didUpdateValueForCharacteristic: cada vez que cambia el valor de la característica. Para desactivar las notificaciones, se llama a setNotifyValue:false forCharacteristic:. Core Bluetooth gestiona automáticamente el descriptor CCCD en el periférico.

OperaciónMétodoDelegadoTipo de transferencia
LecturareadValueForCharacteristic:didUpdateValueForCharacteristicPolling (solicitud-respuesta)
Escritura withResponsewriteValue:forCharacteristic:type:withResponsedidWriteValueForCharacteristicCon confirmación
Escritura withoutResponsewriteValue:forCharacteristic:type:withoutResponseSin delegadoSin confirmación
NotificaciónsetNotifyValue:true forCharacteristic:didUpdateNotificationStateForCharacteristic + didUpdateValueForCharacteristicPush desde el periférico

Modo en segundo plano de Core Bluetooth y State Restoration

El modo en segundo plano de Core Bluetooth permite que las aplicaciones BLE continúen escaneando, mantengan conexiones y reciban notificaciones mientras están en segundo plano. Para activarlo, es necesario habilitar la capacidad “Uses Bluetooth LE accessories” en Xcode (Info.plist → Required background modes → App communicates using Core Bluetooth) y agregar la clave “bluetooth-central” en UIBackgroundModes. Para el rol periférico — “bluetooth-peripheral”.

State Restoration es un mecanismo de Core Bluetooth para restaurar el estado de las conexiones BLE después de un reinicio de la aplicación por iOS. Cuando el modo en segundo plano está activo y se especifica un restoreIdentifier en la inicialización de CBCentralManager o CBPeripheralManager, iOS guarda el estado de la pila BLE cuando la aplicación termina y lo restaura en el próximo inicio. El delegado centralManager:willRestoreState: recibe un diccionario con los CBPeripheral guardados y las conexiones pendientes.

swift
// Configuración de 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 {

    // Restaurar estado después del reinicio
    func centralManager(_ central: CBCentralManager,
                        willRestoreState dict: [String : Any]) {
        if let peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey]
            as? [CBPeripheral] {
            for peripheral in peripherals {
                peripheral.delegate = self
                // Restaurar descubrimiento GATT
                peripheral.discoverServices(nil)
            }
        }
    }

    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        if central.state == .poweredOn {
            print("Bluetooth listo después de la restauración")
        }
    }
}

En la configuración BLECentralWithRestoration, la clave CBCentralManagerOptionRestoreIdentifierKey activa la conservación del estado. Si iOS terminó la aplicación (por ejemplo, por falta de memoria), en el próximo inicio centralManager:willRestoreState: recibe una lista de los CBPeripheral conectados anteriormente. La aplicación restaura los delegados y realiza un redescubrimiento de servicios — el usuario no nota la interrupción de la conexión. Sin State Restoration, todas las sesiones BLE se pierden cuando la aplicación termina.

Ejemplo de aplicación BLE en Swift: central y periférico

Un ejemplo completo de una aplicación BLE en Swift combina ambos dispositivos central y periférico en un solo proyecto. La aplicación puede funcionar en dos modos: descubrir y conectarse a dispositivos BLE (Central) o emular un accesorio BLE (Peripheral). A continuación se muestra una arquitectura con un administrador BLE común que selecciona el rol al iniciar.

swift
// Administrador BLE universal para central y periférico
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 central: escaneo
    func scanForDevices() {
        centralManager?.scanForPeripherals(withServices: nil, options: nil)
    }

    // Dispositivo periférico: publicidad
    func advertiseService() {
        let data: [String: Any] = [
            CBAdvertisementDataServiceUUIDsKey: [advertisedServiceUUID]
        ]
        peripheralManager?.startAdvertising(data)
    }
}

// Uso al iniciar
let isCentral = UserDefaults.standard.bool(forKey: "isCentral")
let manager = BLEManager(role: isCentral ? .central : .peripheral)

if isCentral {
    manager.scanForDevices()
} else {
    manager.advertiseService()
}

El administrador BLEManager selecciona el rol en la inicialización y crea el Manager correspondiente (CBCentralManager o CBPeripheralManager). El indicador de rol puede almacenarse en UserDefaults o pasarse a través de un servidor de configuración. Este enfoque permite que la aplicación BLE se adapte al caso de uso: en un punto de venta, un iPhone funciona como central para escanear terminales de pago; en una puerta de enlace IoT, como periférico para recopilar datos de sensores.

Preguntas frecuentes

¿Qué es Core Bluetooth?

Core Bluetooth es el framework de Apple para desarrollo BLE en iOS, iPadOS y macOS. Proporciona API tanto para dispositivos centrales (CBCentralManager) como periféricos (CBPeripheralManager). Es compatible con BLE 4.0–5.4, extended advertising, 2M PHY y LE Audio. Core Bluetooth es la única API oficial de Apple para comunicación BLE, necesaria para todas las aplicaciones iOS que trabajan con Bluetooth Low Energy.

¿Cuál es la diferencia entre CBCentralManager y CBPeripheralManager?

CBCentralManager es una clase para operar en el rol de dispositivo central: escanea periféricos BLE, establece conexiones, lee y escribe características. CBPeripheralManager es una clase para operar en el rol periférico: publica servicios, responde a solicitudes de lectura/escritura y envía notificaciones. Un mismo iPhone puede funcionar en ambos roles simultáneamente a través de diferentes instancias de administradores.

¿Cómo configurar Core Bluetooth para funcionar en segundo plano?

Para el funcionamiento BLE en segundo plano, habilite la capacidad “Uses Bluetooth LE accessories” en Xcode y agregue la clave “bluetooth-central” en UIBackgroundModes. Para el rol periférico — “bluetooth-peripheral”. Especifique un restoreIdentifier al inicializar el administrador para State Restoration. Sin estos ajustes, la aplicación en segundo plano no recibirá eventos BLE y perderá las conexiones.

¿Por qué Core Bluetooth no encuentra dispositivos?

Razones comunes: CBCentralManager.state != .poweredOn (Bluetooth desactivado o no autorizado), delegado no establecido, dispositivo fuera del alcance o no envía paquetes publicitarios. Verifique el permiso NSBluetoothAlwaysUsageDescription en Info.plist, el estado de Bluetooth en centralManagerDidUpdateState y asegúrese de que scanForPeripherals se llame solo cuando .poweredOn.

¿Se pueden conectar varios CBPeripheral simultáneamente?

Sí, Core Bluetooth admite conexiones simultáneas a múltiples dispositivos BLE. Cada CBPeripheral se gestiona de forma independiente a través de su propio delegado. iOS limita la cantidad de conexiones BLE simultáneas a nivel del sistema (generalmente 5–7 para iPhone). Para escenarios 1:N (por ejemplo, un centro de fitness con 10 rastreadores), se requiere poner en cola y atender periféricos de forma cíclica.

Resumen

  • Core Bluetooth — framework de sistema de Apple para desarrollo BLE con las clases CBCentralManager y CBPeripheralManager
  • CBCentralManager gestiona el escaneo, la conexión y las operaciones GATT con dispositivos BLE remotos
  • CBPeripheralManager emula periféricos BLE con publicación de servicios y manejo de solicitudes entrantes
  • Perfil GATT incluye servicios, características y descriptores con operaciones de lectura, escritura y notificación
  • Modo en segundo plano requiere UIBackgroundModes y restoreIdentifier para State Restoration
  • MTU limita el tamaño del paquete BLE: 23 bytes para BLE 4.0, hasta 251 bytes para BLE 5.0+ con MTU extendido
  • Swift async/await mediante CheckedContinuation simplifica el código BLE asíncrono con delegados

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.

Discutir el proyecto

Lea también