CBPeripheral: Was es ist, Methoden und Verwaltung von BLE-Peripheriegeräten unter iOS

Autor: IT Sectr Veröffentlicht: 2026-07-16 Lesezeit: 10 Min.

CBPeripheral ist eine Klasse des Core Bluetooth Frameworks, die ein entferntes BLE-Gerät unter iOS repräsentiert. Jedes CBPeripheral-Objekt kapselt die UUID, den Namen, den RSSI und die GATT-Diensthierarchie eines verbundenen BLE-Geräts. Der Entwickler interagiert mit dem Peripheriegerät ausschließlich über CBPeripheral: Service-Discovery (discoverServices:), Characteristic-Lesen (readValueForCharacteristic:), Datenschreiben (writeValue:forCharacteristic:type:) und Benachrichtigungsabonnement (setNotifyValue:forCharacteristic:). Laut Apple Developer, 2026 ist CBPeripheral das zentrale Objekt für alle BLE-Peripherieoperationen, das von CBCentralManager bei Geräteerkennung oder -verbindung zurückgegeben wird.

Wichtige Punkte

  • CBPeripheral ist eine Core Bluetooth-Klasse für die Arbeit mit einem entfernten BLE-Gerät unter iOS
  • GATT-Hierarchie — Peripheral enthält Dienste (CBService), Dienste enthalten Characteristics (CBCharacteristic), Characteristics enthalten Deskriptoren (CBDescriptor)
  • Discovery — discoverServices: und discoverCharacteristics:forService: zum Abrufen der GATT-Struktur eines Geräts
  • Lesen und Schreiben — readValueForCharacteristic: und writeValue:forCharacteristic:type: mit Bestätigung (withResponse) oder ohne (withoutResponse)
  • Benachrichtigungen — setNotifyValue:forCharacteristic: aktiviert das Abonnement von BLE-Gerätecharacteristic-Änderungen

Was ist CBPeripheral: Wesen und Zweck

CBPeripheral ist ein Objekt, das ein entferntes BLE-Gerät in einer iOS-Anwendung repräsentiert. Im Gegensatz zu CBCentralManager, der den lokalen Bluetooth-Adapter des iPhone verwaltet, modelliert CBPeripheral ein externes Peripheriegerät: einen Sensor, Fitness-Tracker, Beacon oder medizinisches Instrument. Jede CBPeripheral-Instanz enthält eine eindeutige Kennung (UUID), die über Verbindungssitzungen hinweg bestehen bleibt — Apple verknüpft die UUID über das systemeigene Bonding mit einem bestimmten Gerät.

CBPeripheral wird nicht direkt über init erstellt. Das Core Bluetooth Framework gibt ein CBPeripheral-Objekt in zwei Szenarien zurück: wenn ein Gerät über scanForPeripheralsWithServices: entdeckt wird (Delegat didDiscoverPeripheral) und beim Verbinden mit einem zuvor bekannten Gerät über retrievePeripheralsWithIdentifiers:. Nach Erhalt des Objekts ruft der Entwickler connectPeripheral: auf CBCentralManager auf, wonach CBPeripheral für GATT-Operationen verfügbar wird.

CBPeripheral-Lebenszyklus umfasst sechs Zustände: getrennt (initial), verbindend (nach connect-Aufruf), verbunden (nach didConnectPeripheral), entdeckend (während discoverServices-Aufruf), entdeckt (nach Erhalt der Dienste) und trennend (nach cancelPeripheralConnection). Jeder Zustand wird über das CBPeripheralDelegate-Protokoll verfolgt — ein Muss für jede BLE-Anwendung unter iOS.

CBPeripheral und GATT-Hierarchie: Dienste, Characteristics, Deskriptoren

CBPeripheral speichert eine hierarchische GATT-Struktur, die aus drei Ebenen besteht. Die Root-Ebene ist ein Array von CBService (Diensten), jeder Dienst enthält ein Array von CBCharacteristic (Characteristics), jede Characteristic enthält ein Array von CBDescriptor (Deskriptoren). Dieses Modell entspricht vollständig der Bluetooth GATT-Spezifikation: Ein Dienst ist eine Gerätefunktion (z.B. „Herzfrequenzdienst“), eine Characteristic ist ein spezifischer Wert (Puls 72 bpm), ein Deskriptor sind Metadaten der Characteristic (Maßeinheiten, Benachrichtigungskonfiguration).

EbeneCore Bluetooth-KlasseBeschreibung
DienstCBServiceLogische Gruppe verwandter Characteristics, identifiziert durch UUID (16-Bit, 32-Bit oder 128-Bit)
CharacteristicCBCharacteristicSpezifischer Datenwert, unterstützt Lesen, Schreiben und Benachrichtigungen
DeskriptorCBDescriptorMetadaten der Characteristic: Client-Konfiguration CCCD, Benutzerbeschreibung, Präsentationsformat

Standardmäßige BLE-Dienste sind von der Bluetooth SIG registriert: Heart Rate Service (UUID 180D), Battery Service (180F), Device Information (180A), Blood Pressure (1810). Für benutzerdefinierte Dienste werden 128-Bit-UUIDs verwendet (z.B. E20A39F4-73F5-4BC4-A12F-17D1AD07A961). iOS erkennt standardmäßige UUIDs automatisch und zeigt menschenlesbare Namen an; benutzerdefinierte UUIDs werden im Hex-Format angezeigt.

Nach der Verbindung ist die CBPeripheral-Hierarchie leer — Dienste und Characteristics sind nicht geladen. Der Entwickler muss discoverServices: aufrufen, um Dienste zu erhalten, und dann für jeden Dienst discoverCharacteristics:forService: aufrufen. Wenn der Dienst eingeschlossene Dienste enthält, wird zusätzlich discoverIncludedServices:forService: aufgerufen. Erst nach Abschluss der Hierarchie-Erkennung wird CBPeripheral befüllt und steht zum Lesen und Schreiben zur Verfügung.

Service- und Characteristic-Discovery: Methoden und Delegaten

Discovery der GATT-Struktur von CBPeripheral ist ein obligatorischer Schritt vor allen Lese- oder Schreiboperationen. Die Methode discoverServices: startet eine asynchrone Suche nach allen Diensten des Geräts. Wenn nil übergeben wird, werden alle Dienste entdeckt; wenn ein Array von CBUUID übergeben wird — nur Dienste mit den angegebenen UUIDs (Zeitoptimierung). Das Ergebnis kommt im Delegaten peripheral:didDiscoverServices: an — das CBPeripheral-Objekt füllt seine Eigenschaft services mit einem Array von CBService.

Nach Erhalt der Dienste muss für jeden CBService discoverCharacteristics:forService: aufgerufen werden. Analog dazu: nil — alle Characteristics, Array von CBUUID — nur die angegebenen. Das Ergebnis: peripheral:didDiscoverCharacteristicsForService:error:. In diesem Stadium erhält CBCharacteristic Eigenschaften (properties: .read, .write, .notify, .indicate), die die erlaubten Operationen definieren.

swift
import CoreBluetooth

extension BLEViewController: CBPeripheralDelegate {

    // 1. Service discovery
    func peripheral(_ peripheral: CBPeripheral,
                     didDiscoverServices error: Error?) {
        guard let services = peripheral.services else { return }

        for service in services {
            // Request characteristics for each service
            peripheral.discoverCharacteristics(nil, for: service)
        }
    }

    // 2. Characteristic discovery
    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. Read value
    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)")
    }
}

Im Beispiel implementiert CBPeripheralDelegate drei obligatorische Discovery-Methoden. didDiscoverServices iteriert über alle gefundenen Dienste und fordert Characteristics an. didDiscoverCharacteristicsForService prüft die Eigenschaften jeder Characteristic: für .read ruft es readValue auf, für .notify ruft es setNotifyValue(true) auf. Die Methode didUpdateValueForCharacteristic erhält den tatsächlichen Wert im Data-Format.

Lesen und Schreiben von Characteristics: withResponse und withoutResponse

Lesen von Werten der CBCharacteristic erfolgt mit der Methode readValueForCharacteristic:. Das Ergebnis kommt asynchron in peripheral:didUpdateValueForCharacteristic:error: an. Wichtig: Das Gerät kann einen zwischengespeicherten Wert haben (characteristic.value ist sofort nach der Discovery verfügbar), aber um die aktuellen Daten zu erhalten, ist der Aufruf von readValue obligatorisch. iOS kann Werte aus Energieeffizienzgründen zwischenspeichern — readValue aktualisiert den Cache.

Schreiben von Werten erfolgt mit der Methode writeValue:forCharacteristic:type:. Der Parameter type bestimmt den Schreibtyp: .withResponse (CBCharacteristicWriteWithResponse) — das Gerät bestätigt das Schreiben über didWriteValueForCharacteristic; .withoutResponse (CBCharacteristicWriteWithoutResponse) — Schreiben ohne Bestätigung, maximale Geschwindigkeit aber ohne Liefergarantie. Die BLE-Spezifikation begrenzt die MTU (Maximum Transmission Unit): bis zu 23 Bytes für BLE 4.0, bis zu 251 Bytes für BLE 5.0+. Für Daten, die größer als die MTU sind, ist eine Fragmentierung auf Anwendungsebene erforderlich.

swift
// CBPeripheral characteristic read and write
class BLEService {

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

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

    // Read with response
    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)
    }

    // Write with response (withResponse)
    func writeWithResponse(data: Data) {
        guard let characteristic = findCharacteristic() else { return }
        peripheral.writeValue(data, for: characteristic,
                             type: .withResponse)
    }

    // Write without response (withoutResponse)
    // Max throughput, no delivery guarantee
    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 })
    }
}

Die Wahl des Schreibtyps withResponse oder withoutResponse hängt von den Zuverlässigkeitsanforderungen ab. Für Befehle (Licht einschalten, Tür öffnen) verwenden Sie withResponse — die Liefergarantie ist kritisch. Für Streaming-Daten (Puls, Temperatur) verwenden Sie withoutResponse — der Verlust eines Pakets ist unbedeutend. Ein BLE-Gerät unterstützt möglicherweise nur einen Schreibtyp — überprüfen Sie die Eigenschaften characteristic.properties.contains(.write) und .writeWithoutResponse.

Abonnieren von BLE-Benachrichtigungen über setNotifyValue

Benachrichtigungen sind ein BLE-Mechanismus, bei dem das Peripheriegerät Characteristic-Werte asynchron an das zentrale Gerät sendet, ohne ständiges Polling durch die Zentrale. CBPeripheral aktiviert das Abonnement über die Methode setNotifyValue:forCharacteristic:. Nach Aktivierung des Abonnements schreibt iOS automatisch in das CCCD (Client Characteristic Configuration Descriptor) des Peripheriegeräts, und das Gerät beginnt mit dem Senden von Aktualisierungen, sobald sich der Wert ändert.

Im Gegensatz zu Indications erfordern Benachrichtigungen keine Bestätigung durch das zentrale Gerät — das Paket wird gesendet und vergessen. Dies bietet maximalen Durchsatz, aber Pakete können verloren gehen. Indications erfordern eine Bestätigung auf Protokollebene (L2CAP) — zuverlässiger, aber langsamer. Die properties-Eigenschaft von CBCharacteristic gibt genau an, welcher Modus unterstützt wird: .notify, .indicate oder beide.

Wenn CBPeripheral die Verbindung trennt (Trennung, außerhalb der Reichweite), werden alle aktiven Abonnements automatisch zurückgesetzt. Nach der Wiederverbindung muss setNotifyValue:true erneut für jede Characteristic aufgerufen werden. iOS verliert auch Abonnements, wenn die Anwendung den Vordergrund verlässt (wenn der Hintergrundmodus nicht aktiviert ist) — für den Hintergrundbetrieb muss die Fähigkeit „Uses Bluetooth LE accessories“ in Info.plist aktiviert werden.

swift
// CBPeripheral notification subscription management
class NotificationManager: NSObject {

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

    // Subscribe to notifications for all .notify characteristics
    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)
                }
            }
        }
    }

    // Unsubscribe from all notifications
    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()
    }

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

Der NotificationManager demonstriert die korrekte Handhabung von CBPeripheral-Benachrichtigungen. subscribeToAllNotifications iteriert über alle Dienste und Characteristics und aktiviert .notify und .indicate. subscribedCharacteristics verfolgt aktive Abonnements für die korrekte Kündigung. didUpdateNotificationStateForCharacteristic bestätigt die erfolgreiche Änderung des Abonnementstatus über die Eigenschaft characteristic.isNotifying.

Vollständiges CBPeripheral-Beispiel in Swift

Vollständiger Workflow mit CBPeripheral umfasst: Abrufen des Objekts von CBCentralManager, Verbindung, Discovery, Lesen/Schreiben, Benachrichtigungsabonnement und Trennung. Das folgende Beispiel implementiert eine BLEConnection-Klasse, die den vollständigen BLE-Peripherie-Lebenszyklus in Swift mit der modernen async/await-API (iOS 15+) verwaltet.

swift
import CoreBluetooth

// Full CBPeripheral management example with 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. Connect to peripheral
    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) {
        // Handle Bluetooth device state
    }

    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
}

Die BLEConnection-Klasse verwendet Swift Concurrency (async/await) über CheckedContinuation — ein modernes Muster für die Arbeit mit delegatbasierten Core Bluetooth-APIs. connect(to:) wartet auf die Verbindungsbestätigung über didConnectPeripheral, discoverServices() — über didDiscoverServices. Dieser Ansatz eliminiert verschachtelte Delegaten und macht BLE-Code linear und lesbar. Die Fehlerbehandlung über BLEError deckt alle typischen BLE-Verbindungsfehlerszenarien ab.

Häufig gestellte Fragen

Wie erhalte ich CBPeripheral ohne Scannen?

CBPeripheral für ein zuvor verbundenes Gerät kann über retrievePeripheralsWithIdentifiers: auf CBCentralManager abgerufen werden. Übergeben Sie ein Array von UUIDs (NSUUID) zuvor gespeicherter Geräte — das Framework gibt ein Array von CBPeripheral für Geräte in der systemeigenen BLE-Bonding-Datenbank zurück. Dies funktioniert nur für Geräte, mit denen das iPhone zuvor gekoppelt wurde. Für ein neues Gerät ist das Scannen erforderlich.

Warum erkennt CBPeripheral keine Dienste?

Häufige Ursachen: Das Gerät ist außer Reichweite (RSSI unterhalb des Schwellenwerts), BLE-Funk ist ausgeschaltet (CBCentralManager.state != .poweredOn), der CBPeripheralDelegate ist nicht gesetzt (peripheral.delegate = self), oder discoverServices wurde vor der Verbindung aufgerufen. Überprüfen Sie centralManager.state, stellen Sie sicher, dass der Delegat vor dem Aufruf von connect gesetzt ist, und verwenden Sie einen Wiederholungsversuch mit einem Timeout von 5–10 Sekunden.

Was tun, wenn writeValue nicht antwortet?

Die Ursache ist die Verwendung von .withResponse bei einer Characteristic, die nur .writeWithoutResponse unterstützt, oder umgekehrt. Überprüfen Sie characteristic.properties vor dem Aufruf. Ein weiteres mögliches Problem ist die MTU: Wenn die Daten 20 Bytes (BLE 4.0 MTU) überschreiten, ist eine MTU-Aushandlung über negotiateMTU oder Fragmentierung erforderlich. Verwenden Sie peripheral.maximumWriteValueLength(for: .withResponse), um die maximale Paketgröße zu bestimmen.

Wie unterscheide ich einen CBPeripheral in Reichweite von einem nicht verfügbaren?

CBPeripheral außerhalb der Reichweite trennt nicht sofort — iOS versetzt es nach einem Timeout (normalerweise 20–30 Sekunden) in den Zustand .disconnected. Zur Überwachung verwenden Sie readRSSI auf CBPeripheral — wenn nicht verfügbar, wird ein Fehler mit dem Code CBError.connectionTimeout zurückgegeben. Überwachen Sie auch centralManager:didDisconnectPeripheral:error: zur rechtzeitigen Erkennung von Verbindungsabbrüchen.

Kann ein einzelner CBPeripheral von mehreren Threads verwendet werden?

Core Bluetooth ist nicht threadsicher — alle CBPeripheral-Aufrufe müssen von derselben Warteschlange aus erfolgen (normalerweise der Hauptwarteschlange oder einer seriellen Warteschlange, die bei der Initialisierung von CBCentralManager angegeben wurde). Gleichzeitige Aufrufe aus verschiedenen Threads führen zu Wettlaufsituationen und Anwendungsabstürzen. Verwenden Sie DispatchQueue(label: „com.app.ble“) für alle BLE-Operationen und DispatchQueue.main.async für UI-Updates.

Zusammenfassung

  • CBPeripheral ist eine Core Bluetooth-Klasse für die Arbeit mit einem entfernten BLE-Gerät unter iOS, zurückgegeben von CBCentralManager
  • GATT-Hierarchie besteht aus Diensten (CBService), Characteristics (CBCharacteristic) und Deskriptoren (CBDescriptor) mit 16-Bit- oder 128-Bit-UUIDs
  • Discovery erfolgt sequenziell: discoverServices: → discoverCharacteristics:forService: mit Delegatenbehandlung
  • Lesen — readValueForCharacteristic:, Schreiben — writeValue:forCharacteristic:type: (.withResponse oder .withoutResponse)
  • Benachrichtigungen — setNotifyValue:forCharacteristic: aktiviert die asynchrone Datenübertragung vom Peripheriegerät zur Zentrale
  • MTU für BLE 4.0 begrenzt Pakete auf 23 Bytes, BLE 5.0+ — bis zu 251 Bytes, Daten über MTU erfordern Fragmentierung
  • Swift async/await über CheckedContinuation vereinfacht BLE-Code und ersetzt verschachtelte Delegaten durch lineare Aufrufe

Wir entwickeln eine mobile Applikation schlüsselfertig

IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.

Projekt besprechen

Lesen Sie auch