Core Bluetooth: что это, архитектура и разработка BLE на iOS

Автор: IT Sectr Опубликовано: 2026-07-16 Время чтения: 10 мин

Core Bluetooth — фреймворк Apple для взаимодействия с Bluetooth Low Energy на iOS, iPadOS и macOS. Фреймворк предоставляет полный набор API для работы в обеих ролях BLE: центральное devicesо (CBCentralManager) для сканирования и подключения к периферии, и периферийное devicesо (CBPeripheralManager) для эмуляции BLE-сервера. Core Bluetooth абстрагирует стек BLE-протокола от физического радио до прикладного GATT-профиля. По данным Apple Developer, 2026, Core Bluetooth — единственный официальный API Apple для BLE-разработки, поддерживающий BLE 4.0–5.4 с extended advertising, 2M PHY и LE Audio.

Главное

  • Core Bluetooth — системный фреймворк Apple для BLE-разработки на iOS, iPadOS и macOS
  • CBCentralManager — класс для сканирования и подключения к BLE-периферии со стороны центрального devicesа
  • CBPeripheralManager — класс для создания BLE-сервера, публикующего сервисы и характеристики
  • GATT-профиль — иерархическая модель сервисов, характеристик и дескрипторов для обмена данными
  • Фоновые режимы — Core Bluetooth поддерживает BLE-связь в background через системные делегаты и state restoration

Что такое Core Bluetooth: архитектура и компоненты

Core Bluetooth разделяет BLE-стек на две логические роли, определённые спецификацией Bluetooth SIG. Роль центрального devicesа (Central) представлена классом CBCentralManager — он инициирует сканирование, устанавливает соединения и управляет списком подключённых CBPeripheral. Роль периферийного devicesа (Peripheral) представлена CBPeripheralManager — он публикует сервисы и характеристики, отвечает на запросы центрального и отправляет уведомления. Одна iOS-сессия может одновременно работать в обеих ролях на разных BLE-радио, но типовое приложение использует одну роль.

Архитектура Core Bluetooth включает пять ключевых абстракций. CBCentralManager управляет состоянием Bluetooth-адаптера devicesа: poweredOn (готов к работе), poweredOff (Bluetooth disabled), unauthorized (нет разрешения), unsupported (BLE недоступен). CBPeripheral представляет удалённое BLE-devicesо с его UUID, именем, RSSI и GATT-иерархией. CBService — логическая группа характеристик. CBCharacteristic — точка данных для чтения/записи/уведомлений. CBPeripheralManager создаёт локальный GATT-сервер для эмуляции периферии.

КлассРольОсновные методы
CBCentralManagerЦентральное devicesоscanForPeripherals, connect, cancelPeripheralConnection, retrievePeripherals
CBPeripheralУдалённая периферияdiscoverServices, discoverCharacteristics, readValue, writeValue, setNotifyValue
CBPeripheralManagerЛокальная периферияaddService, removeService, startAdvertising, respondToRequest, updateValue
CBCentralУдалённый центральныйmaximumUpdateValueLength, identifier, ancsAuthorized

Состояния CBCentralManager управляют всеми BLE-операциями. При старте приложения centralManagerDidUpdateState вызывается с текущим состоянием Bluetooth. Если состояние не .poweredOn, любые BLE-вызовы игнорируются системой. Разработчик обязан проверять state перед каждым сканированием и подключением. Переход из .poweredOff в .poweredOn происходит при включении Bluetooth в iOS Settings — делегат получает повторный вызов, и приложение может возобновить сканирование.

CBCentralManager: сканирование и подключение BLE-devices

CBCentralManager — точка входа для всех BLE-операций со стороны центрального devicesа. Инициализация принимает делегат (CBCentralManagerDelegate) и очередь DispatchQueue — рекомендация Apple использовать main queue для простоты или serial queue для производительности. После инициализации фреймворк автоматически проверяет состояние Bluetooth и вызывает centralManagerDidUpdateState: — первый обязательный делегат для обработки.

Сканирование запускается методом scanForPeripheralsWithServices:options:. Первый параметр — массив CBUUID сервисов для фильтрации: если известны UUID интересующих сервисов, передача их сокращает энергопотребление и время поиска. Если nil, обнаруживаются все BLE-devicesа в зоне действия. Опции включают .allowDuplicatesKey (повторные обнаружения одного devicesа) и .solicitedServiceUUIDsKey (для сервисов, публикуемых на централи).

swift
import CoreBluetooth

class BLECentral: NSObject {

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

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

    // Start BLE scanning
    func startScan() {
        guard centralManager.state == .poweredOn else {
            print("Bluetooth unavailable")
            return
        }
        // Scan all devices (nil = no filter)
        centralManager.scanForPeripherals(withServices: nil,
                                            options: [CBCentralManagerScanOptionAllowDuplicatesKey: true])
    }

    // Stop scanning
    func stopScan() {
        centralManager.stopScan()
    }

    // Connect to selected device
    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)")
    }
}

Класс BLECentral демонстрирует полный цикл сканирования и подключения BLE-devices. centralManagerDidUpdateState запускает сканирование при включённом Bluetooth. didDiscoverPeripheral собирает найденные devicesа в массив discoveredPeripherals с дедупликацией по identifier. После подключения (didConnect) немедленно запускается discovery сервисов — это обязательный шаг перед любыми GATT-операциями.

CBPeripheralManager: создание BLE-сервера на iOS

CBPeripheralManager — класс для эмуляции BLE-периферийного devicesа на iOS. Приложение в роли периферии может публиковать свои сервисы и характеристики, принимать входящие запросы на чтение/запись от центрального devicesа и отправлять уведомления. CBPeripheralManager используется для BLE-аксессуаров, эмулируемых iPhone: пульты, клавиатуры, трекеры, IoT-шлюзы.

Жизненный цикл CBPeripheralManager начинается с инициализации и делегата CBPeripheralManagerDelegate. После подтверждения poweredOn через peripheralManagerDidUpdateState: публикуются сервисы (addService:) и запускается реклама (startAdvertising:). Рекламные данные CBAdvertisementData включают локальное имя (CBAdvertisementDataLocalNameKey), UUID сервисов (CBAdvertisementDataServiceUUIDsKey) и уровень мощности (CBAdvertisementDataTxPowerLevelKey). Максимальный размер рекламного пакета — 31 bytes для BLE 4.0, 251 bytes для extended advertising BLE 5.0+.

swift
// BLE peripheral on iOS via 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)
    }

    // Publish service with characteristic
    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)
    }

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

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

    // Handle write request
    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)
    }
}

Класс BLEPeripheral создаёт BLE-сервер с одной характеристикой, поддерживающей чтение, запись и уведомления. После инициализации peripheralManagerDidUpdateState публикует сервис через addService:, затем запускает рекламу через startAdvertising:. Обработчики didReceiveRead и didReceiveWrite отвечают на входящие GATT-запросы от центрального devicesа. Для отправки уведомлений используется метод updateValue:forCharacteristic:onSubscribedCentrals:.

GATT-операции: чтение, запись и уведомления

GATT-операции (Generic Attribute Profile) — основа обмена данными в Core Bluetooth. После discovery сервисов и характеристик центральное devicesо может выполнять три типа операций: чтение значения характеристики, запись значения и подписка на уведомления/индикации. Каждая операция асинхронна и возвращает результат через соответствующий делегат CBPeripheralDelegate.

Чтение выполняется вызовом readValueForCharacteristic:. Значение приходит в peripheral:didUpdateValueForCharacteristic:error:. Важно: чтение возвращает текущее значение с devicesа, а не кешированное. Если devicesо не поддерживает чтение (свойство .read), вызов вернёт ошибку. Для больших значений (больше MTU) BLE автоматически фрагментирует и собирает данные на уровне GATT.

Запись выполняется writeValue:forCharacteristic:type:. BLE поддерживает две модели записи: withResponse (надежная, с подтверждением) и withoutResponse (быстрая, без подтверждения). Свойство CBCharacteristic.properties определяет доступные типы записи. Максимальный размер одного write-пакета ограничен MTU: 23 bytesа для BLE 4.0 (20 bytes полезных данных + 3 bytesа заголовка), до 247 bytes для BLE 5.0 с extended MTU (MTU 251).

Уведомления активируются вызовом setNotifyValue:true forCharacteristic:. После подписки периферия автоматически отправляет обновления через peripheral:didUpdateValueForCharacteristic: всякий раз, когда значение характеристики меняется. Для отключения уведомлений вызывается setNotifyValue:false forCharacteristic:. Core Bluetooth автоматически управляет CCCD-дескриптором на периферии.

ОперацияМетодДелегатТип передачи
ЧтениеreadValueForCharacteristic:didUpdateValueForCharacteristicPolling (запрос-ответ)
Запись withResponsewriteValue:forCharacteristic:type:withResponsedidWriteValueForCharacteristicС подтверждением
Запись withoutResponsewriteValue:forCharacteristic:type:withoutResponseНет делегатаБез подтверждения
УведомлениеsetNotifyValue:true forCharacteristic:didUpdateNotificationStateForCharacteristic + didUpdateValueForCharacteristicPush от периферии

Фоновый режим Core Bluetooth и State Restoration

Фоновый режим Core Bluetooth позволяет BLE-приложению продолжать сканирование, поддерживать соединения и получать уведомления при нахождении в background. Для активации требуется: включить capability "Uses Bluetooth LE accessories" в Xcode (Info.plist → Required background modes → App communicates using Core Bluetooth) и добавить ключ "bluetooth-central" в UIBackgroundModes. Для периферийной роли — "bluetooth-peripheral".

State Restoration — механизм Core Bluetooth для восстановления состояния BLE-соединений после перезапуска приложения системой iOS. При активации фонового режима и указании restoreIdentifier в инициализации CBCentralManager или CBPeripheralManager, iOS сохраняет состояние BLE-стека при завершении приложения и восстанавливает его при следующем запуске. Делегат centralManager:willRestoreState: получает словарь с сохранёнными CBPeripheral и ожидающими соединениями.

swift
// Core Bluetooth configuration with 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 {

    // Restore state after restart
    func centralManager(_ central: CBCentralManager,
                        willRestoreState dict: [String : Any]) {
        if let peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey]
            as? [CBPeripheral] {
            for peripheral in peripherals {
                peripheral.delegate = self
                // Restore GATT discovery
                peripheral.discoverServices(nil)
            }
        }
    }

    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        if central.state == .poweredOn {
            print("Bluetooth ready after restoration")
        }
    }
}

В конфигурации BLECentralWithRestoration ключ CBCentralManagerOptionRestoreIdentifierKey активирует сохранение состояния. Если приложение было завершено iOS (например, из-за нехватки памяти), при следующем запуске centralManager:willRestoreState: получает список ранее подключённых CBPeripheral. Приложение восстанавливает делегаты и выполняет повторный discovery сервисов — пользователь не замечает разрыва соединения. Без State Restoration все BLE-сессии теряются при завершении приложения.

Пример BLE-приложения на Swift: централь и периферия

Полный пример BLE-приложения на Swift объединяет центральное и периферийное devicesо в одном проекте. Приложение может работать в двух режимах: обнаруживать и подключаться к BLE-devicesам (Central) или эмулировать BLE-аксессуар (Peripheral). Ниже приведена архитектура с общим менеджером BLE, выбирающим роль при старте.

swift
// Universal BLE manager for central and peripheral
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)
        }
    }

    // Central device: scanning
    func scanForDevices() {
        centralManager?.scanForPeripherals(withServices: nil, options: nil)
    }

    // Peripheral device: advertising
    func advertiseService() {
        let data: [String: Any] = [
            CBAdvertisementDataServiceUUIDsKey: [advertisedServiceUUID]
        ]
        peripheralManager?.startAdvertising(data)
    }
}

// Usage on startup
let isCentral = UserDefaults.standard.bool(forKey: "isCentral")
let manager = BLEManager(role: isCentral ? .central : .peripheral)

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

Менеджер BLEManager выбирает роль при инициализации и создаёт соответствующий Manager (CBCentralManager или CBPeripheralManager). Флаг роли может храниться в UserDefaults или передаваться через сервер конфигурации. Такой подход позволяет BLE-приложению адаптироваться под сценарий использования: на точке продажи iPhone работает как централь для сканирования платежных терминалов, на IoT-шлюзе — как периферия для сбора данных с датчиков.

Часто задаваемые вопросы

Что такое Core Bluetooth?

Core Bluetooth — фреймворк Apple для BLE-разработки на iOS, iPadOS и macOS. Предоставляет API для работы центрального (CBCentralManager) и периферийного (CBPeripheralManager) devices. Поддерживает BLE 4.0–5.4, extended advertising, 2M PHY и LE Audio. Core Bluetooth — единственный официальный API Apple для BLE-коммуникации, обязательный для всех iOS-приложений, работающих с Bluetooth Low Energy.

Чем отличается CBCentralManager от CBPeripheralManager?

CBCentralManager — класс для работы в роли центрального devicesа: сканирует BLE-периферию, устанавливает соединения, читает и пишет характеристики. CBPeripheralManager — класс для работы в роли периферии: публикует сервисы, отвечает на запросы чтения/записи и отправляет уведомления. Один iPhone может работать в двух ролях одновременно через разные инстансы менеджеров.

Как настроить Core Bluetooth для фоновой работы?

Для фоновой BLE-работы включите capability "Uses Bluetooth LE accessories" в Xcode и добавьте ключ "bluetooth-central" в UIBackgroundModes. Для периферийной роли — "bluetooth-peripheral". Укажите restoreIdentifier при инициализации менеджера для State Restoration. Без этих настроек приложение в background не получает BLE-события и теряет соединения.

Почему Core Bluetooth не находит devicesа?

Основные причины: CBCentralManager.state != .poweredOn (Bluetooth disabled или не авторизован), делегат не установлен, devicesо вне зоны действия или не отправляет рекламные пакеты. Проверьте разрешение NSBluetoothAlwaysUsageDescription в Info.plist, статус Bluetooth в centralManagerDidUpdateState и убедитесь, что scanForPeripherals вызывается только при .poweredOn.

Можно ли подключить несколько CBPeripheral одновременно?

Да, Core Bluetooth поддерживает одновременное подключение к нескольким BLE-devicesам. Каждый CBPeripheral управляется независимо через собственный делегат. iOS ограничивает количество одновременных BLE-соединений на уровне системы (обычно 5–7 для iPhone). Для 1:N сценариев (например, фитнес-центр с 10 трекерами) требуется постановка в очередь и циклическое обслуживание периферий.

Итоги

  • Core Bluetooth — системный фреймворк Apple для BLE-разработки с классами CBCentralManager и CBPeripheralManager
  • CBCentralManager управляет сканированием, подключением и GATT-операциями с удалёнными BLE-devicesами
  • CBPeripheralManager эмулирует BLE-периферию с публикацией сервисов и обработкой входящих запросов
  • GATT-профиль включает сервисы, характеристики и дескрипторы с операциями чтения, записи и уведомлений
  • Фоновый режим требует UIBackgroundModes и restoreIdentifier для State Restoration
  • MTU BLE ограничивает размер пакета: 23 bytesа для BLE 4.0, до 251 bytesа для BLE 5.0+ с extended MTU
  • Swift async/await через CheckedContinuation упрощает асинхронный BLE-код с делегатами

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также