CBCentralManager in iOS — what it is, BLE management and Core Bluetooth

Author: IT Sectr Published: 2026-07-16 Reading time: 11 min

CBCentralManager is the central class of the Core Bluetooth framework in iOS that manages scanning, connecting, and interacting with BLE peripheral devices. Core Bluetooth (iOS 5+, 2011) provides a high-level abstraction over the BLE stack at the GATT level, hiding the details of Link Layer and HCI from the developer. CBCentralManager implements the Central role: it scans the air via scanForPeripherals, initiates connection via connect, discovers services via discoverServices, and manages data transfer. According to Apple Developer Documentation (2024), CBCentralManager supports up to 7 simultaneous connections to BLE devices on devices with BLE 5.0.

Key Takeaways

  • CBCentralManager is the iOS class for managing BLE scanning, connections, and data transfer in the Central role.
  • Scanning is launched via scanForPeripherals with service UUID filtering to save power.
  • Connection is performed via connect(peripheral:options:) with state tracking through the delegate.
  • iOS supports up to 7 simultaneous BLE connections on devices with BLE 5.0.
  • Background scanning requires enabling bluetooth-central in Background Modes and using CBCentralManagerScanOptionAllowDuplicatesKey.

What is CBCentralManager?

CBCentralManager is the main Core Bluetooth class for implementing the Central role in BLE architecture on iOS. It manages the entire BLE connection lifecycle: from scanning advertising devices to data transfer and disconnection. CBCentralManager works asynchronously through the CBCentralManagerDelegate, notifying the app about events in the Bluetooth stack.

Initializing CBCentralManager starts the state restoration process: the manager checks the Bluetooth state on the device and restores previous connections if the app was closed. The initialization process can take from 50 to 500 ms depending on the Bluetooth state. The app must wait for the centralManagerDidUpdateState callback before starting any BLE operations.

The Core Bluetooth architecture is built on the Delegation pattern: CBCentralManager delegates event handling (device discovery, connection, errors) to the CBCentralManagerDelegate protocol. For working with a specific Peripheral, the CBPeripheralDelegate protocol is used, which notifies about discovered services, characteristics, and received data. This asynchronous model ensures a non-blocking UI.

CBCentralManager States

CBCentralManager goes through several states that determine whether the BLE stack is available. The state is passed through the delegate: centralManagerDidUpdateState(_:). The developer must handle all states — not just poweredOn, but also cases when Bluetooth is turned off or unavailable.

StateValueDeveloper Action
.poweredOnBluetooth is on and readyStart scanning
.poweredOffBluetooth is offShow alert to user
.unauthorizedNo permissionRequest permission in Settings
.unsupportedDevice does not support BLEHide BLE features
.unknownState is undefinedWait for next update
.resettingBluetooth is restartingWait for recovery

Unauthorized state is becoming more common since iOS 13+. Starting with this version, the app must have the NSBluetoothAlwaysUsageDescription permission in Info.plist. Without it, the central manager transitions to the .unauthorized state, and scanning is impossible. The user can change the permission in Settings > Privacy > Bluetooth at any time.

Scanning BLE Devices

scanForPeripherals(withServices:options:) is the main method for starting scanning. The withServices parameter accepts an array of service UUIDs for filtering: if nil is passed, all devices will be discovered, which significantly increases power consumption. It is recommended to always filter by the service UUIDs needed by the app. Scanning options include CBCentralManagerScanOptionAllowDuplicatesKey (repeated notifications about the same device).

swift
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") // Battery Service

        centralManager.scanForPeripherals(
            withServices: [serviceUUID],
            options: [
                CBCentralManagerScanOptionAllowDuplicatesKey: false
            ]
        )
    }
}

When a device is discovered, centralManager(_:didDiscover:advertisementData:rssi:) is called. The advertisementData parameter contains the full dictionary of advertisement data, including the device name (CBAdvertisementDataLocalNameKey), service UUIDs (CBAdvertisementDataServiceUUIDsKey), and manufacturer data (CBAdvertisementDataManufacturerDataKey). RSSI is the signal level in dBm available at the time of discovery.

Connecting to a Peripheral

connect(_:options:) is the method for establishing a BLE connection with a discovered Peripheral. After calling connect, iOS attempts to connect to the device. A successful connection is confirmed by calling centralManager(_:didConnect:), an error — by centralManager(_:didFailToConnect:error:). Connection options include CBConnectPeripheralOptionNotifyOnConnectionKey, CBConnectPeripheralOptionNotifyOnDisconnectionKey, and CBConnectPeripheralOptionNotifyOnNotificationKey for background notifications.

swift
// Connect to BLE device
func connectToPeripheral(
    _ peripheral: CBPeripheral
) {
    centralManager.connect(peripheral, options: nil)

    // Set delegate for Peripheral
    peripheral.delegate = self
}

// Delegate: successful connection
func centralManager(
    _ central: CBCentralManager,
    didConnect peripheral: CBPeripheral
) {
    print("Connected to " +
          "\(peripheral.name ?? "unknown")")

    // Start service discovery
    peripheral.discoverServices(nil)
}

// Delegate: connection error
func centralManager(
    _ central: CBCentralManager,
    didFailToConnect peripheral: CBPeripheral,
    error: Error?
) {
    print("Connection failed: 
          \(error?.localizedDescription ?? "")")
}

Connection timeout on iOS is 30 seconds. If the device has not responded to the connection request within this time, didFailToConnect is called. The timeout is affected by: distance to the device, interference, and whether the device is currently advertising. Before connecting, make sure the device is in connectable advertising mode (ADV_IND, not ADV_NONCONN_IND).

Discovering Services and Characteristics

After connecting, you must discover the services (discoverServices) and characteristics (discoverCharacteristics) of the Peripheral. This is a mandatory step before reading or writing data. The process is asynchronous: discoverServices returns results via peripheral(_:didDiscoverServices:), and discoverCharacteristics — via peripheral(_:didDiscoverCharacteristicsFor:error:).

It is recommended to pass an array of relevant UUIDs to discoverServices rather than nil. Filtering speeds up discovery and saves power. If a service is not found, iOS will report an empty array. After discovering characteristics, you can read their values (readValue), subscribe to notifications (setNotifyValue), or write data (writeValue).

An important nuance: MTU is negotiated automatically after connection. To get the current MTU, use peripheral.maximumWriteValueLength(for: .withResponse) or .withoutResponse. On iOS, the maximum MTU is 512 bytes for BLE 5.0 devices. If you need to transfer data larger than MTU, implement fragmentation at the application level.

Background Scanning and iOS Limitations

Background scanning of BLE devices on iOS requires special configuration. Core Bluetooth supports background execution, but with significant limitations. To work in the background, you need to: enable bluetooth-central in Background Modes in the project Capabilities, initialize CBCentralManager with the CBCentralManagerOptionRestoreIdentifierKey option for state restoration, and handle central manager events when transitioning to the background.

Background BLE limitations on iOS: scanForPeripherals without UUID filtering does not work in the background. The app must specify concrete service UUIDs for scanning. iOS may delay delivery of BLE events indefinitely. Core Bluetooth automatically resumes scanning when a matching device is discovered, even if the app is in the background. Timeout for background scanning: iOS may stop scanning after 10–30 minutes to save power.

State Restoration is a Core Bluetooth mechanism that allows restoring BLE connections after an app restart or iOS reboot. To use it: specify CBCentralManagerOptionRestoreIdentifierKey during initialization, implement centralManager(_:willRestoreState:) in the delegate, and restore the list of connected Peripherals from the passed dictionary. State Restoration is critical functionality for BLE apps working in the background, such as fitness trackers or medical devices.

Error Handling and Connection Recovery

CBCentralManager generates errors in several scenarios: connection failed (didFailToConnect), connection dropped (didDisconnectPeripheral), characteristic is not available for reading/writing (didWriteValue error). All Core Bluetooth errors are returned through the Error object with domain CBErrorDomain. The most common codes: CBErrorConnectionTimeout (0x04), CBErrorPeripheralDisconnected (0x07), CBErrorOperationNotSupported (0x0A).

Connection recovery strategy: when receiving didDisconnectPeripheral, check the error code. If the error is CBErrorConnectionTimeout or CBErrorPeripheralDisconnected — schedule an automatic reconnection in 1–5 seconds. If the error is CBErrorOperationNotSupported — log it and do not retry the operation. For critical connections (medical devices), use exponential backoff with a maximum interval of 60 seconds.

swift
// Handle disconnect with auto-reconnect
func centralManager(
    _ central: CBCentralManager,
    didDisconnectPeripheral peripheral: CBPeripheral,
    error: Error?
) {
    guard let error = error else {
        return // Expected disconnect
    }

    print("Disconnected: \(error.localizedDescription)")

    // Automatic reconnection
    if shouldAutoReconnect {
        DispatchQueue.main.asyncAfter(
            deadline: .now() + reconnectDelay
        ) {
            central.connect(peripheral)
        }
    }
}

When developing a robust BLE app on iOS, keep in mind: Core Bluetooth does not guarantee delivery of all packets on weak signal. For reliable transmission, use writeType .withResponse (acknowledged write) and subscribe to notifications (setNotifyValue) to receive data from the Peripheral. Maintain an error log for diagnosing connection issues in production.

Frequently Asked Questions

Why doesn't CBCentralManager detect devices?

Check the manager state via centralManagerDidUpdateState. Make sure the NSBluetoothAlwaysUsageDescription permission is in Info.plist, Bluetooth is enabled on the device, and the peripheral device is advertising with the correct type (connectable advertising, not non-connectable).

How many BLE devices can be simultaneously connected to iOS?

On devices with BLE 5.0 (iPhone 8 and newer) — up to 7 simultaneous connections. On older devices — up to 3–5. The number of scanned devices is unlimited, but active connections have a hard limit set by the Bluetooth Controller.

How often can I scan BLE on iOS without draining the battery?

It is recommended to scan with UUID filtering and stop scanning when the device is found. Continuous scanning drains the battery: 1 hour of uninterrupted scanning consumes ~10–15% of iPhone charge. Use timers and conditions to stop scanning.

What is the difference between CBCentralManager and CBPeripheralManager?

CBCentralManager is for scanning and connecting to external BLE devices (Central role). CBPeripheralManager is for your iOS device to act as BLE peripheral itself (advertise services). One instance can only be in one role.

How to handle BLE device connection loss?

Implement centralManager(_:didDisconnectPeripheral:error:). If the error is not nil — schedule automatic reconnection with exponential backoff (1 s → 2 s → 4 s → 8 s → max 60 s). If the error is nil — the device disconnected normally (e.g., the user pressed a button on the device).

Summary

  • CBCentralManager is the main Core Bluetooth class for managing BLE scanning, connections, and data transfer in the Central role on iOS.
  • Scanning is launched via scanForPeripherals with optional service UUID filtering to reduce power consumption.
  • Connection is performed via connect, success is confirmed by didConnect, error — by didFailToConnect with a 30-second timeout.
  • After connecting, you must discover services and characteristics via discoverServices and discoverCharacteristics.
  • Background scanning requires bluetooth-central Background Mode and is supported with limitations (UUID filtering, possible delays).
  • iOS supports up to 7 simultaneous BLE connections on devices with BLE 5.0, state restoration for recovery after restart.
  • Error handling and automatic reconnection with exponential backoff is the foundation of a reliable BLE app on iOS.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also