CBCentralManager — це центральний клас фреймворку Core Bluetooth в iOS, який керує скануванням, підключенням та взаємодією з BLE-периферійними пристроями. Core Bluetooth (iOS 5+, 2011) надає високорівневу абстракцію над BLE-стеком на рівні GATT, приховуючи від розробника деталі Link Layer та HCI. CBCentralManager реалізує роль Central: він сканує ефір через scanForPeripherals, ініціює підключення через connect, виявляє сервіси через discoverServices та керує передачею даних. За даними Apple Developer Documentation (2024), CBCentralManager підтримує до 7 одночасних підключень до BLE-пристроїв на пристроях з BLE 5.0.
Головне
CBCentralManager — це основний клас Core Bluetooth для реалізації ролі Central в BLE-архітектурі на iOS. Він керує всім життєвим циклом BLE-з'єднання: від сканування пристроїв, що рекламуються, до передачі даних та відключення. CBCentralManager працює асинхронно через делегат CBCentralManagerDelegate, сповіщаючи додаток про події в Bluetooth-стеку.
Ініціалізація CBCentralManager запускає процес state restoration: менеджер перевіряє стан Bluetooth на пристрої та відновлює попередні з'єднання, якщо додаток було закрито. Процес ініціалізації може тривати від 50 до 500 мс залежно від стану Bluetooth. Додаток має дочекатися виклику centralManagerDidUpdateState перед початком будь-яких BLE-операцій.
Архітектура Core Bluetooth побудована на шаблоні Delegation: CBCentralManager делегує обробку подій (виявлення пристроїв, підключення, помилки) протоколу CBCentralManagerDelegate. Для роботи з конкретним Peripheral використовується протокол CBPeripheralDelegate, який сповіщає про виявлені сервіси, характеристики та отримані дані. Така асинхронна модель забезпечує неблокуючий інтерфейс користувача.
CBCentralManager проходить через кілька станів, які визначають, чи доступний BLE-стек для роботи. Стан передається через делегат: centralManagerDidUpdateState(_:). Розробник зобов'язаний обробити всі стани — не тільки poweredOn, але й випадки, коли Bluetooth вимкнено або недоступний.
| Стан | Значення | Дія розробника |
|---|---|---|
| .poweredOn | Bluetooth увімкнено та готовий | Почати сканування |
| .poweredOff | Bluetooth вимкнено | Показати сповіщення користувачеві |
| .unauthorized | Немає дозволу | Запросити дозвіл у Налаштуваннях |
| .unsupported | Пристрій не підтримує BLE | Приховати BLE-функції |
| .unknown | Стан не визначено | Очікувати наступного оновлення |
| .resetting | Bluetooth перезавантажується | Очікувати відновлення |
Unauthorized state стає все більш поширеним з iOS 13+. Починаючи з цієї версії, додаток повинен мати дозвіл NSBluetoothAlwaysUsageDescription у Info.plist. Без нього центральний менеджер переходить у стан .unauthorized, і сканування неможливе. Користувач може змінити дозвіл у Налаштуваннях > Конфіденційність > Bluetooth у будь-який момент.
scanForPeripherals(withServices:options:) — основний метод для запуску сканування. Параметр withServices приймає масив UUID сервісів для фільтрації: якщо передати nil, будуть виявлені всі пристрої, що значно збільшує енергоспоживання. Рекомендується завжди фільтрувати за UUID сервісів, які потрібні додатку. Опції сканування включають CBCentralManagerScanOptionAllowDuplicatesKey (повторні сповіщення про один пристрій).
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") // Сервіс батареї
centralManager.scanForPeripherals(
withServices: [serviceUUID],
options: [
CBCentralManagerScanOptionAllowDuplicatesKey: false
]
)
}
}
При виявленні пристрою викликається centralManager(_:didDiscover:advertisementData:rssi:). Параметр advertisementData містить повний словник даних рекламного пакета, включаючи ім'я пристрою (CBAdvertisementDataLocalNameKey), UUID сервісів (CBAdvertisementDataServiceUUIDsKey) та дані виробника (CBAdvertisementDataManufacturerDataKey). RSSI — рівень сигналу в dBm, доступний у момент виявлення.
connect(_:options:) — метод для встановлення BLE-з'єднання з виявленим Peripheral. Після виклику connect iOS намагається підключитися до пристрою. Успішне підключення підтверджується викликом centralManager(_:didConnect:), помилка — centralManager(_:didFailToConnect:error:). Опції підключення включають CBConnectPeripheralOptionNotifyOnConnectionKey, CBConnectPeripheralOptionNotifyOnDisconnectionKey та CBConnectPeripheralOptionNotifyOnNotificationKey для фонових сповіщень.
// Підключитися до BLE пристрою
func connectToPeripheral(
_ peripheral: CBPeripheral
) {
centralManager.connect(peripheral, options: nil)
// Встановити делегат для Peripheral
peripheral.delegate = self
}
// Делегат: успішне підключення
func centralManager(
_ central: CBCentralManager,
didConnect peripheral: CBPeripheral
) {
print("Підключено до " +
"\(peripheral.name ?? "unknown")")
// Почати виявлення сервісів
peripheral.discoverServices(nil)
}
// Делегат: помилка підключення
func centralManager(
_ central: CBCentralManager,
didFailToConnect peripheral: CBPeripheral,
error: Error?
) {
print("Connection failed:
\(error?.localizedDescription ?? "")")
}
Таймаут підключення на iOS становить 30 секунд. Якщо за цей час пристрій не відповів на connection request, викликається didFailToConnect. На таймаут впливають: відстань до пристрою, перешкоди, чи рекламується пристрій у даний момент. Перед підключенням переконайтеся, що пристрій знаходиться в режимі connectable advertising (ADV_IND, а не ADV_NONCONN_IND).
Після підключення необхідно виявити сервіси (discoverServices) та характеристики (discoverCharacteristics) Peripheral. Це обов'язковий крок перед читанням або записом даних. Процес асинхронний: discoverServices повертає результат через peripheral(_:didDiscoverServices:), а discoverCharacteristics — через peripheral(_:didDiscoverCharacteristicsFor:error:).
Рекомендується передавати в discoverServices масив потрібних UUID, а не nil. Фільтрація прискорює виявлення та економить енергію. Якщо сервіс не буде знайдено, iOS повідомить порожній масив. Після виявлення характеристик можна читати їх значення (readValue), підписуватися на сповіщення (setNotifyValue) або записувати дані (writeValue).
Важливий нюанс: MTU узгоджується автоматично після підключення. Для отримання поточного MTU використовуйте peripheral.maximumWriteValueLength(for: .withResponse) або .withoutResponse. В iOS максимальний MTU — 512 байт для BLE 5.0 пристроїв. Якщо потрібно передати дані більше MTU, реалізуйте фрагментацію на рівні додатка.
Фонове сканування BLE-пристроїв на iOS потребує спеціального налаштування. Core Bluetooth підтримує background execution, але з суттєвими обмеженнями. Для роботи в фоні необхідно: включити bluetooth-central у Background Modes в Capabilities проекту, ініціалізувати CBCentralManager з опцією CBCentralManagerOptionRestoreIdentifierKey для state restoration та обробляти події центрального менеджера при переході в фон.
Обмеження фонового BLE в iOS: scanForPeripherals без фільтрації за UUID не працює в фоні. Додаток повинен вказувати конкретні UUID сервісів для сканування. iOS може затримувати доставку BLE-подій на невизначений час. Core Bluetooth автоматично відновлює сканування при виявленні matching-пристрою, навіть якщо додаток у фоні. Таймаут на фонове сканування: iOS може зупинити сканування через 10–30 хвилин для економії енергії.
State Restoration — механізм Core Bluetooth, який дозволяє відновити BLE-з'єднання після перезапуску додатка або перезавантаження iOS. Для його використання: вкажіть CBCentralManagerOptionRestoreIdentifierKey при ініціалізації, реалізуйте centralManager(_:willRestoreState:) у делегаті та відновіть список підключених Peripheral з переданого словника. State Restoration — критичний функціонал для BLE-додатків, що працюють у фоні, наприклад фітнес-трекерів або медичних пристроїв.
CBCentralManager генерує помилки в кількох сценаріях: підключення не вдалося (didFailToConnect), з'єднання розірвано (didDisconnectPeripheral), характеристика недоступна для читання/запису (didWriteValue error). Всі помилки Core Bluetooth повертаються через об'єкт Error з доменом CBErrorDomain. Найчастіші коди: CBErrorConnectionTimeout (0x04), CBErrorPeripheralDisconnected (0x07), CBErrorOperationNotSupported (0x0A).
Стратегія відновлення з'єднання: при отриманні didDisconnectPeripheral перевірте код помилки. Якщо помилка CBErrorConnectionTimeout або CBErrorPeripheralDisconnected — заплануйте автоматичне перепідключення через 1–5 секунд. Якщо помилка CBErrorOperationNotSupported — логуйте і не намагайтеся повторити операцію. Для критично важливих з'єднань (медичні пристрої) використовуйте exponential backoff з максимальним інтервалом 60 секунд.
// Обробка відключення з автоматичним перепідключенням
func centralManager(
_ central: CBCentralManager,
didDisconnectPeripheral peripheral: CBPeripheral,
error: Error?
) {
guard let error = error else {
return // Очікуване відключення
}
print("Disconnected: \(error.localizedDescription)")
// Автоматичне перепідключення
if shouldAutoReconnect {
DispatchQueue.main.asyncAfter(
deadline: .now() + reconnectDelay
) {
central.connect(peripheral)
}
}
}
При розробці надійного BLE-додатка на iOS враховуйте: Core Bluetooth не гарантує доставку всіх пакетів при слабкому сигналі. Для надійної передачі використовуйте writeType .withResponse (підтверджений запис) та підписуйтеся на сповіщення (setNotifyValue) для отримання даних від Peripheral. Ведіть лог помилок для діагностики проблем з'єднання в продакшені.
Часті запитання
Перевірте стан менеджера через centralManagerDidUpdateState. Переконайтеся, що дозвіл NSBluetoothAlwaysUsageDescription є в Info.plist, Bluetooth увімкнено на пристрої та периферійний пристрій рекламується з правильним типом (connectable advertising, а не non-connectable).
На пристроях з BLE 5.0 (iPhone 8 та новіші) — до 7 одночасних підключень. На старіших пристроях — до 3–5. Кількість пристроїв, що скануються, не обмежена, але активні підключення мають жорсткий ліміт, встановлений Bluetooth Controller.
Рекомендується сканувати з фільтром за UUID і вимикати сканування, коли пристрій знайдено. Постійне сканування розряджає батарею: 1 година безперервного сканування споживає ~10–15% заряду iPhone. Використовуйте таймери та умови для зупинки сканування.
CBCentralManager — для сканування та підключення до зовнішніх BLE-пристроїв (роль Central). CBPeripheralManager — щоб ваш iOS-пристрій сам виступав у ролі BLE-периферії (рекламував сервіси). Один екземпляр може бути тільки в одній ролі.
Реалізуйте centralManager(_:didDisconnectPeripheral:error:). Якщо помилка не nil — заплануйте автоматичне перепідключення з exponential backoff (1 с → 2 с → 4 с → 8 с → макс 60 с). Якщо помилка nil — пристрій відключився штатно (наприклад, користувач натиснув кнопку на пристрої).
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також