CBPeripheral: co to je, metody a správa BLE-periferie na iOS

Autor: IT Sectr Publikováno: 2026-07-16 Doba čtení: 10 min

CBPeripheral — třída frameworku Core Bluetooth, která reprezentuje vzdálené BLE zařízení na iOS. Každý objekt CBPeripheral zapouzdřuje UUID, název, RSSI a hierarchii GATT služeb připojeného BLE zařízení. Vývojář interaguje s periferií výhradně prostřednictvím CBPeripheral: discovery služeb (discoverServices:), čtení charakteristik (readValueForCharacteristic:), zápis dat (writeValue:forCharacteristic:type:) a přihlášení k odběru oznámení (setNotifyValue:forCharacteristic:). Podle Apple Developer, 2026 je CBPeripheral — centrální objekt pro všechny operace s BLE-periferií, vrácený CBCentralManagerem při detekci nebo připojení zařízení.

Hlavní body

  • CBPeripheral — třída Core Bluetooth pro práci se vzdáleným BLE zařízením na iOS
  • Hierarchie GATT — Peripheral obsahuje služby (CBService), služby obsahují charakteristiky (CBCharacteristic), charakteristiky obsahují deskriptory (CBDescriptor)
  • Discovery — discoverServices: a discoverCharacteristics:forService: pro získání GATT struktury zařízení
  • Čtení a zápis — readValueForCharacteristic: a writeValue:forCharacteristic:type: s potvrzením (withResponse) nebo bez (withoutResponse)
  • Oznámení — setNotifyValue:forCharacteristic: aktivuje přihlášení ke změnám charakteristik BLE zařízení

Co je CBPeripheral: podstata a účel

CBPeripheral — je objekt, který reprezentuje vzdálené BLE zařízení v iOS aplikaci. Na rozdíl od CBCentralManageru, který spravuje lokální Bluetooth adaptér iPhone, CBPeripheral modeluje externí periferní zařízení: senzor, fitness tracker, maják, lékařské zařízení. Každá instance CBPeripheral obsahuje jedinečný identifikátor (UUID), který je zachován mezi relacemi připojení — Apple spojuje UUID s konkrétním zařízením prostřednictvím systémového Bondingu.

CBPeripheral není vytvářen přímo přes init. Framework Core Bluetooth vrací objekt CBPeripheral ve dvou scénářích: při detekci zařízení pomocí scanForPeripheralsWithServices: (delegát didDiscoverPeripheral) a při připojení k dříve známému zařízení pomocí retrievePeripheralsWithIdentifiers:. Po obdržení objektu vývojář zavolá connectPeripheral: na CBCentralManager, po čemž se CBPeripheral stane dostupným pro GATT operace.

Životní cyklus CBPeripheral zahrnuje šest stavů: disconnected (počáteční), connecting (po volání connect), connected (po didConnectPeripheral), discovering (během volání discoverServices), discovered (po obdržení služeb) a disconnecting (po cancelPeripheralConnection). Každý stav je sledován prostřednictvím delegáta CBPeripheralDelegate — povinný protokol pro každou BLE aplikaci na iOS.

CBPeripheral a hierarchie GATT: služby, charakteristiky, deskriptory

CBPeripheral ukládá hierarchickou GATT strukturu sestávající ze tří úrovní. Kořenová úroveň — pole CBService (služby), každá služba obsahuje pole CBCharacteristic (charakteristiky), každá charakteristika obsahuje pole CBDescriptor (deskriptory). Tento model plně odpovídá specifikaci Bluetooth GATT: služba — funkce zařízení (například „Heart Rate Service"), charakteristika — konkrétní hodnota (puls 72 bpm), deskriptor — metadata charakteristiky (jednotky měření, konfigurace oznámení).

ÚroveňTřída Core BluetoothPopis
SlužbaCBServiceLogická skupina příbuzných charakteristik, identifikovaná UUID (16-bit, 32-bit nebo 128-bit)
CharakteristikaCBCharacteristicKonkrétní hodnota dat, podporuje čtení, zápis, oznámení
DeskriptorCBDescriptorMetadata charakteristiky: konfigurace klienta CCCD, User Description, Presentation Format

Standardní BLE služby jsou registrovány u Bluetooth SIG: Heart Rate Service (UUID 180D), Battery Service (180F), Device Information (180A), Blood Pressure (1810). Pro vlastní služby se používají 128-bit UUID (například E20A39F4-73F5-4BC4-A12F-17D1AD07A961). iOS automaticky rozpoznává standardní UUID a zobrazuje lidsky čitelná jména; vlastní UUID se zobrazují v hex formátu.

Po připojení je hierarchie CBPeripheral prázdná — služby a charakteristiky nejsou načteny. Vývojář musí zavolat discoverServices: pro získání služeb a poté pro každou službu zavolat discoverCharacteristics:forService:. Pokud služba obsahuje zahrnuté služby (includedServices), dodatečně se volá discoverIncludedServices:forService:. Teprve po dokončení discovery hierarchie se CBPeripheral naplní a stane se dostupným pro čtení a zápis.

Discovery služeb a charakteristik: metody a delegáti

Discovery (detekce) GATT struktury CBPeripheral — povinný krok před jakýmikoli operacemi čtení nebo zápisu. Metoda discoverServices: spouští asynchronní vyhledávání všech služeb zařízení. Pokud je předáno nil, jsou detekovány všechny služby; pokud je předáno pole CBUUID — pouze služby s uvedenými UUID (optimalizace času). Výsledek přichází do delegáta peripheral:didDiscoverServices: — objekt CBPeripheral vyplní vlastnost services polem CBService.

Po obdržení služeb je pro každou CBService nutné zavolat discoverCharacteristics:forService:. Podobně, nil — všechny charakteristiky, pole CBUUID — pouze uvedené. Výsledek: peripheral:didDiscoverCharacteristicsForService:error:. V této fázi CBCharacteristic získává vlastnosti (properties: .read, .write, .notify, .indicate), které určují povolené operace.

swift
import CoreBluetooth

extension BLEViewController: CBPeripheralDelegate {

    // 1. Discovery služby
    func peripheral(_ peripheral: CBPeripheral,
                     didDiscoverServices error: Error?) {
        guard let services = peripheral.services else { return }

        for service in services {
            // Požadavek charakteristik pro každou službu
            peripheral.discoverCharacteristics(nil, for: service)
        }
    }

    // 2. Discovery charakteristiky
    func peripheral(_ peripheral: CBPeripheral,
                     didDiscoverCharacteristicsFor service: CBService,
                     error: Error?) {
        guard let characteristics = service.characteristics else { return }

        for characteristic in characteristics {
            if characteristic.properties.contains(.read) {
                peripheral.readValue(for: characteristic)
            }
            if characteristic.properties.contains(.notify) {
                peripheral.setNotifyValue(true, for: characteristic)
            }
        }
    }

    // 3. Čtení hodnoty
    func peripheral(_ peripheral: CBPeripheral,
                     didUpdateValueFor characteristic: CBCharacteristic,
                     error: Error?) {
        guard let data = characteristic.value,
              let value = String(data: data, encoding: .utf8)
        else { return }
        print("Characteristic value: \(value)")
    }
}

V příkladu jsou implementovány tři povinné metody detekce CBPeripheralDelegate. didDiscoverServices prochází všechny nalezené služby a požaduje charakteristiky. didDiscoverCharacteristicsForService kontroluje vlastnosti každé charakteristiky: pro .read volá readValue, pro .notify — setNotifyValue(true). Metoda didUpdateValueForCharacteristic přijímá aktuální hodnotu ve formátu Data.

Čtení a zápis charakteristik: withResponse a withoutResponse

Čtení hodnot CBCharacteristic se provádí metodou readValueForCharacteristic:. Výsledek přichází asynchronně do peripheral:didUpdateValueForCharacteristic:error:. Důležité: zařízení může mít hodnotu v mezipaměti (characteristic.value je dostupný ihned po discovery), ale pro získání aktuální hodnoty je volání readValue povinné. iOS může ukládat hodnoty do mezipaměti kvůli energetické účinnosti — readValue obnovuje mezipaměť.

Zápis hodnot se provádí metodou writeValue:forCharacteristic:type:. Parametr type určuje typ zápisu: .withResponse (CBCharacteristicWriteWithResponse) — zařízení potvrzuje zápis přes didWriteValueForCharacteristic; .withoutResponse (CBCharacteristicWriteWithoutResponse) — zápis bez potvrzení, maximální rychlost, ale bez záruky doručení. Specifikace BLE omezuje MTU (Maximum Transmission Unit): až 23 bajtů pro BLE 4.0, až 251 bajtů pro BLE 5.0+. Pro data větší než MTU je vyžadována fragmentace na úrovni aplikace.

swift
// Čtení a zápis charakteristik CBPeripheral
class BLEService {

    private let peripheral: CBPeripheral
    private let serviceUUID = CBUUID(string: "180D")
    private let charUUID = CBUUID(string: "2A37")

    init(peripheral: CBPeripheral) {
        self.peripheral = peripheral
    }

    // Čtení s potvrzením
    func readHeartRate() {
        guard let service = peripheral.services?.first(where: { $0.uuid == serviceUUID }),
              let characteristic = service.characteristics?.first(where: { $0.uuid == charUUID })
        else { return }
        peripheral.readValue(for: characteristic)
    }

    // Zápis s potvrzením (withResponse)
    func writeWithResponse(data: Data) {
        guard let characteristic = findCharacteristic() else { return }
        peripheral.writeValue(data, for: characteristic,
                             type: .withResponse)
    }

    // Zápis bez potvrzení (withoutResponse)
    // Maximální propustnost, bez záruky doručení
    func writeWithoutResponse(data: Data) {
        guard let characteristic = findCharacteristic() else { return }
        peripheral.writeValue(data, for: characteristic,
                             type: .withoutResponse)
    }

    private func findCharacteristic() -> CBCharacteristic? {
        return peripheral.services?
            .flatMap { $0.characteristics ?? [] }
            .first(where: { $0.uuid == charUUID })
    }
}

Výběr typu zápisu withResponse nebo withoutResponse závisí na požadavcích na spolehlivost. Pro příkazy (rozsvítit světlo, otevřít zámek) použijte withResponse — záruka doručení je kritická. Pro streamovaná data (puls, teplota) použijte withoutResponse — ztráta jednoho paketu je nevýznamná. BLE zařízení může podporovat pouze jeden typ zápisu — zkontrolujte vlastnost characteristic.properties.contains(.write) a .writeWithoutResponse.

Přihlášení k BLE oznámením prostřednictvím setNotifyValue

Oznámení (notifications) — BLE mechanismus, při kterém periferní zařízení odesílá hodnotu charakteristiky centrálnímu zařízení asynchronně, bez neustálého pollingu ze strany centrálního. CBPeripheral aktivuje přihlášení prostřednictvím metody setNotifyValue:forCharacteristic:. Po aktivaci přihlášení iOS automaticky zapisuje CCCD (Client Characteristic Configuration Descriptor) na periferii a zařízení začíná odesílat aktualizace při každé změně hodnoty.

Na rozdíl od indikací (indicate), oznámení nevyžadují potvrzení od centrálního — paket je odeslán a zapomenut. To poskytuje maximální šířku pásma, ale je možná ztráta paketů. Indikace vyžadují potvrzení na úrovni protokolu (L2CAP) — spolehlivější, ale pomalejší. CBCharacteristic prostřednictvím vlastnosti properties přesně určuje, který režim podporuje: .notify, .indicate nebo oba.

Při odpojení CBPeripheral (disconnect, opuštění dosahu) jsou všechna aktivní přihlášení automaticky resetována. Při opětovném připojení je nutné znovu zavolat setNotifyValue:true pro každou charakteristiku. iOS také ztrácí přihlášení při opuštění foreground aplikace (pokud není aktivován režim na pozadí) — pro práci na pozadí je vyžadováno povolení capability „Uses Bluetooth LE accessories“ v Info.plist.

swift
// Správa přihlášení k oznámením CBPeripheral
class NotificationManager: NSObject {

    private var peripheral: CBPeripheral?
    private var subscribedCharacteristics: Set<CBUUID> = []

    // Přihlásit se k oznámením pro všechny .notify charakteristiky
    func subscribeToAllNotifications(peripheral: CBPeripheral) {
        self.peripheral = peripheral

        guard let services = peripheral.services else { return }

        for service in services {
            guard let characteristics = service.characteristics else { continue }

            for characteristic in characteristics {
                if characteristic.properties.contains(.notify)
                    || characteristic.properties.contains(.indicate) {
                    peripheral.setNotifyValue(true, for: characteristic)
                    subscribedCharacteristics.insert(characteristic.uuid)
                }
            }
        }
    }

    // Odhlásit se ze všech oznámení
    func unsubscribeFromAll() {
        guard let peripheral = peripheral else { return }
        guard let services = peripheral.services else { return }

        for service in services {
            guard let characteristics = service.characteristics else { continue }

            for characteristic in characteristics {
                if subscribedCharacteristics.contains(characteristic.uuid) {
                    peripheral.setNotifyValue(false, for: characteristic)
                }
            }
        }
        subscribedCharacteristics.removeAll()
    }

    // Obsluha oznámení
    func peripheral(_ peripheral: CBPeripheral,
                     didUpdateNotificationStateFor characteristic: CBCharacteristic,
                     error: Error?) {
        if characteristic.isNotifying {
            print("Subscription active: \(characteristic.uuid)")
        } else {
            print("Subscription inactive: \(characteristic.uuid)")
        }
    }
}

Správce přihlášení NotificationManager demonstruje správnou práci s oznámeními CBPeripheral. subscribeToAllNotifications prochází všechny služby a charakteristiky, aktivuje .notify a .indicate. subscribedCharacteristics sleduje aktivní přihlášení pro správné odhlášení. didUpdateNotificationStateForCharacteristic potvrzuje úspěšnou změnu stavu přihlášení prostřednictvím vlastnosti characteristic.isNotifying.

Úplný příklad práce s CBPeripheral ve Swift

Úplný pracovní cyklus s CBPeripheral zahrnuje: získání objektu od CBCentralManager, připojení, discovery, čtení/zápis, přihlášení k oznámením a odpojení. V následujícím příkladu je implementována třída BLEConnection, která spravuje úplný životní cyklus BLE-periferie ve Swift pomocí moderního async/await API (iOS 15+).

swift
import CoreBluetooth

// Úplný příklad správy CBPeripheral s async/await
class BLEConnection: NSObject {

    private let centralManager: CBCentralManager
    private var peripheral: CBPeripheral?
    private var continuation: CheckedContinuation<Void, Error>?

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

    // 1. Připojit k periferii
    func connect(to peripheral: CBPeripheral) async throws {
        self.peripheral = peripheral
        peripheral.delegate = self
        centralManager.connect(peripheral, options: nil)

        try await withCheckedThrowingContinuation { continuation in
            self.continuation = continuation
        }
    }

    // 2. Discovery  
    func discoverServices() async throws {
        guard let peripheral = peripheral else {
            throw BLEError.notConnected
        }
        peripheral.discoverServices(nil)
        try await withCheckedThrowingContinuation { continuation in
            self.continuation = continuation
        }
    }
}

// 3. CBCentralManager
extension BLEConnection: CBCentralManagerDelegate {
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        // Zpracování stavu Bluetooth zařízení
    }

    func centralManager(_ central: CBCentralManager,
                        didConnect peripheral: CBPeripheral) {
        continuation?.resume()
        continuation = nil
    }

    func centralManager(_ central: CBCentralManager,
                        didFailToConnect peripheral: CBPeripheral,
                        error: Error?) {
        continuation?.resume(throwing: error ?? BLEError.connectionFailed)
        continuation = nil
    }
}

enum BLEError: Error {
    case notConnected
    case connectionFailed
    case serviceNotFound
    case characteristicNotFound
}

Třída BLEConnection používá Swift Concurrency (async/await) prostřednictvím CheckedContinuation — moderní vzor pro práci s delegátskými API Core Bluetooth. connect(to:) očekává potvrzení připojení přes didConnectPeripheral, discoverServices() — přes didDiscoverServices. Tento přístup eliminuje vnořené delegáty a činí BLE kód lineárním a čitelným. Zpracování chyb prostřednictvím BLEError pokrývá všechny typické scénáře selhání BLE spojení.

Často kladené otázky

Jak získat CBPeripheral bez skenování?

CBPeripheral pro dříve připojené zařízení lze získat prostřednictvím retrievePeripheralsWithIdentifiers: na CBCentralManager. Předejte pole UUID (NSUUID) dříve uložených zařízení — framework vrátí pole CBPeripheral pro zařízení v systémové databázi BLE-bondingu. To funguje pouze pro zařízení, se kterými byl iPhone dříve spárován. Pro nové zařízení je skenování povinné.

Proč CBPeripheral nedetekuje služby?

Hlavní příčiny: zařízení je mimo dosah (RSSI pod prahem), BLE rádio je vypnuto (CBCentralManager.state != .poweredOn), delegát CBPeripheralDelegate není nastaven (peripheral.delegate = self) nebo volání discoverServices bylo provedeno před připojením. Zkontrolujte stav centralManager.state, ujistěte se, že delegát je nastaven před voláním connect a použijte retry s timeoutem 5–10 sekund.

Co dělat, když writeValue neodpovídá?

Příčina — použití .withResponse na charakteristice, která podporuje pouze .writeWithoutResponse nebo naopak. Zkontrolujte characteristic.properties před voláním. Také může být problém s MTU: pokud data > 20 bajtů (BLE 4.0 MTU), je vyžadováno vyjednání MTU prostřednictvím negotiateMTU nebo fragmentace. Použijte peripheral.maximumWriteValueLength(for: .withResponse) pro určení maximální velikosti paketu.

Jak rozlišit CBPeripheral v dosahu od nedostupného?

CBPeripheral mimo dosah se neodpojí okamžitě — iOS jej přepne do stavu .disconnected po timeoutu (obvykle 20–30 sekund). Pro monitorování použijte readRSSI na CBPeripheral — při nedostupnosti vrátí chybu s kódem CBError.connectionTimeout. Také sledujte centralManager:didDisconnectPeripheral:error: pro včasnou detekci přerušení spojení.

Lze použít jeden CBPeripheral z více vláken?

Core Bluetooth není vláknově bezpečný — všechna volání CBPeripheral musí být prováděna z jedné fronty (obvykle main queue nebo sériová serial queue uvedená při inicializaci CBCentralManager). Souběžná volání z různých vláken vedou k race condition a pádu aplikace. Použijte DispatchQueue(label: „com.app.ble“) pro všechny BLE operace a DispatchQueue.main.async pro aktualizaci UI.

Shrnutí

  • CBPeripheral — třída Core Bluetooth pro práci se vzdáleným BLE zařízením na iOS, vrácená CBCentralManagerem
  • Hierarchie GATT se skládá ze služeb (CBService), charakteristik (CBCharacteristic) a deskriptorů (CBDescriptor) s 16-bit nebo 128-bit UUID
  • Discovery se provádí sekvenčně: discoverServices: → discoverCharacteristics:forService: se zpracováním prostřednictvím delegáta
  • Čtení — readValueForCharacteristic:, zápis — writeValue:forCharacteristic:type: (.withResponse nebo .withoutResponse)
  • Oznámení — setNotifyValue:forCharacteristic: aktivuje asynchronní odesílání dat z periferie do centrály
  • MTU BLE 4.0 omezuje paket na 23 bajtů, BLE 5.0+ — až 251 bajtů, data větší než MTU vyžadují fragmentaci
  • Async/await Swift prostřednictvím CheckedContinuation zjednodušuje BLE kód, nahrazuje vnořené delegáty lineárními voláními

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také