CBPeripheral — de klasse van het Core Bluetooth-framework die een extern BLE-apparaat op iOS vertegenwoordigt. Elk CBPeripheral-object kapselt UUID, naam, RSSI en de GATT-servicehiërarchie van het aangesloten BLE-apparaat in. De ontwikkelaar communiceert met de periferie uitsluitend via CBPeripheral: discovery van services (discoverServices:), lezen van characteristics (readValueForCharacteristic:), schrijven van gegevens (writeValue:forCharacteristic:type:) en abonneren op meldingen (setNotifyValue:forCharacteristic:). Volgens Apple Developer, 2026 is CBPeripheral — het centrale object voor alle bewerkingen met BLE-periferie, geretourneerd door CBCentralManager bij detectie of verbinding van een apparaat.
Belangrijkste punten
CBPeripheral — is een object dat een extern BLE-apparaat in een iOS-applicatie vertegenwoordigt. In tegenstelling tot CBCentralManager, die de lokale Bluetooth-adapter van de iPhone beheert, modelleert CBPeripheral een extern randapparaat: sensor, fitnesstracker, beacon, medisch apparaat. Elk CBPeripheral-exemplaar bevat een unieke identificatie (UUID) die tussen verbindingssessies wordt bewaard — Apple koppelt de UUID aan een specifiek apparaat via systeem-Bonding.
CBPeripheral wordt niet rechtstreeks via init gemaakt. Het Core Bluetooth-framework retourneert een CBPeripheral-object in twee scenario's: bij detectie van het apparaat via scanForPeripheralsWithServices: (delegate didDiscoverPeripheral) en bij verbinding met een eerder bekend apparaat via retrievePeripheralsWithIdentifiers:. Na ontvangst van het object roept de ontwikkelaar connectPeripheral: aan op CBCentralManager, waarna CBPeripheral beschikbaar wordt voor GATT-bewerkingen.
Levenscyclus van CBPeripheral omvat zes toestanden: disconnected (initieel), connecting (na aanroep van connect), connected (na didConnectPeripheral), discovering (tijdens aanroep van discoverServices), discovered (na ontvangst van services) en disconnecting (na cancelPeripheralConnection). Elke toestand wordt gevolgd via de delegate CBPeripheralDelegate — een verplicht protocol voor elke BLE-applicatie op iOS.
CBPeripheral slaat een hiërarchische GATT-structuur op die uit drie niveaus bestaat. Het hoofdniveau — een array van CBService (services), elke service bevat een array van CBCharacteristic (characteristics), elke characteristic bevat een array van CBDescriptor (descriptoren). Dit model komt volledig overeen met de Bluetooth GATT-specificatie: service — functie van het apparaat (bijvoorbeeld „Heart Rate Service“), characteristic — concrete waarde (hartslag 72 bpm), descriptor — metadata van de characteristic (meeteenheden, configuratie van meldingen).
| Niveau | Core Bluetooth-klasse | Beschrijving |
|---|---|---|
| Service | CBService | Logische groep van gerelateerde characteristics, geïdentificeerd door UUID (16-bit, 32-bit of 128-bit) |
| Characteristic | CBCharacteristic | Concrete gegevenswaarde, ondersteunt lezen, schrijven, melding |
| Descriptor | CBDescriptor | Metadata van de characteristic: clientconfiguratie CCCD, User Description, Presentation Format |
Standaard BLE-services zijn geregistreerd bij Bluetooth SIG: Heart Rate Service (UUID 180D), Battery Service (180F), Device Information (180A), Blood Pressure (1810). Voor aangepaste services worden 128-bit UUID's gebruikt (bijvoorbeeld E20A39F4-73F5-4BC4-A12F-17D1AD07A961). iOS herkent standaard UUID's automatisch en toont leesbare namen; aangepaste UUID's worden in hex-formaat weergegeven.
Na verbinding is de hiërarchie van CBPeripheral leeg — services en characteristics zijn niet geladen. De ontwikkelaar moet discoverServices: aanroepen om de services te verkrijgen en vervolgens voor elke service discoverCharacteristics:forService: aanroepen. Als de service opgenomen services (includedServices) bevat, wordt ook discoverIncludedServices:forService: aangeroepen. Pas na voltooiing van de discovery van de hiërarchie wordt CBPeripheral gevuld en beschikbaar voor lezen en schrijven.
Discovery (detectie) van de GATT-structuur van CBPeripheral — een verplichte stap voor elke lees- of schrijfbewerking. De methode discoverServices: start een asynchrone zoekopdracht naar alle services van het apparaat. Als nil wordt doorgegeven, worden alle services gedetecteerd; als een array van CBUUID wordt doorgegeven — alleen services met de opgegeven UUID's (tijdoptimalisatie). Het resultaat komt in de delegate peripheral:didDiscoverServices: — het CBPeripheral-object vult de eigenschap services met een array van CBService.
Na ontvangst van de services moet voor elke CBService discoverCharacteristics:forService: worden aangeroepen. Analoog, nil — alle characteristics, array van CBUUID — alleen de opgegeven. Resultaat: peripheral:didDiscoverCharacteristicsForService:error:. In deze fase krijgt CBCharacteristic eigenschappen (properties: .read, .write, .notify, .indicate) die de toegestane bewerkingen bepalen.
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 {
// Characteristics aanvragen voor elke 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. Waarde lezen
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)")
}
}
In het voorbeeld zijn drie verplichte detectiemethoden van CBPeripheralDelegate geïmplementeerd. didDiscoverServices doorloopt alle gevonden services en vraagt characteristics aan. didDiscoverCharacteristicsForService controleert de eigenschappen van elke characteristic: voor .read roept het readValue aan, voor .notify — setNotifyValue(true). De methode didUpdateValueForCharacteristic ontvangt de actuele waarde in Data-formaat.
Lezen van waarden CBCharacteristic wordt uitgevoerd met de methode readValueForCharacteristic:. Het resultaat komt asynchroon binnen in peripheral:didUpdateValueForCharacteristic:error:. Belangrijk: het apparaat kan een gecachede waarde hebben (characteristic.value is direct na discovery beschikbaar), maar voor het verkrijgen van de actuele waarde is readValue verplicht. iOS kan waarden cachen voor energie-efficiëntie — readValue vernieuwt de cache.
Schrijven van waarden wordt uitgevoerd met de methode writeValue:forCharacteristic:type:. De parameter type bepaalt het schrijftype: .withResponse (CBCharacteristicWriteWithResponse) — het apparaat bevestigt het schrijven via didWriteValueForCharacteristic; .withoutResponse (CBCharacteristicWriteWithoutResponse) — schrijven zonder bevestiging, maximale snelheid, maar zonder leveringsgarantie. De BLE-specificatie beperkt MTU (Maximum Transmission Unit): tot 23 bytes voor BLE 4.0, tot 251 bytes voor BLE 5.0+. Voor gegevens groter dan MTU is fragmentatie op applicatieniveau vereist.
// CBPeripheral characteristic lezen en schrijven
class BLEService {
private let peripheral: CBPeripheral
private let serviceUUID = CBUUID(string: "180D")
private let charUUID = CBUUID(string: "2A37")
init(peripheral: CBPeripheral) {
self.peripheral = peripheral
}
// Lezen met bevestiging
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)
}
// Schrijven met bevestiging (withResponse)
func writeWithResponse(data: Data) {
guard let characteristic = findCharacteristic() else { return }
peripheral.writeValue(data, for: characteristic,
type: .withResponse)
}
// Schrijven zonder bevestiging (withoutResponse)
// Maximale doorvoer, geen leveringsgarantie
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 })
}
}
De keuze van het schrijftype withResponse of withoutResponse hangt af van de betrouwbaarheidseisen. Voor commando's (licht aanzetten, slot openen) gebruikt u withResponse — leveringsgarantie is cruciaal. Voor streaminggegevens (hartslag, temperatuur) gebruikt u withoutResponse — verlies van één pakket is niet significant. Het BLE-apparaat kan slechts één schrijftype ondersteunen — controleer de eigenschap characteristic.properties.contains(.write) en .writeWithoutResponse.
Meldingen (notifications) — een BLE-mechanisme waarbij het randapparaat de waarde van een characteristic asynchroon naar het centrale apparaat stuurt, zonder constante polling van de centrale kant. CBPeripheral activeert het abonnement via de methode setNotifyValue:forCharacteristic:. Na activering van het abonnement schrijft iOS automatisch CCCD (Client Characteristic Configuration Descriptor) op de periferie en begint het apparaat updates te sturen bij elke waardeverandering.
In tegenstelling tot indicaties (indicate), vereisen meldingen geen bevestiging van de centrale kant — het pakket wordt verzonden en vergeten. Dit biedt maximale bandbreedte, maar pakketverlies is mogelijk. Indicaties vereisen bevestiging op protocolniveau (L2CAP) — betrouwbaarder maar langzamer. CBCharacteristic geeft via de eigenschap properties precies aan welke modus wordt ondersteund: .notify, .indicate of beide.
Bij verbreking van CBPeripheral (disconnect, buiten bereik) worden alle actieve abonnementen automatisch gereset. Bij heraansluiting moet setNotifyValue:true opnieuw worden aangeroepen voor elke characteristic. iOS verliest ook abonnementen wanneer de applicatie de foreground verlaat (als de background-modus niet is ingeschakeld) — voor achtergrondwerk moet de capability „Uses Bluetooth LE accessories“ in Info.plist worden ingeschakeld.
// CBPeripheral meldingsabonnement beheren
class NotificationManager: NSObject {
private var peripheral: CBPeripheral?
private var subscribedCharacteristics: Set<CBUUID> = []
// Abonneren op meldingen voor alle .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)
}
}
}
}
// Afmelden voor alle meldingen
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()
}
// Meldingshandler
func peripheral(_ peripheral: CBPeripheral,
didUpdateNotificationStateFor characteristic: CBCharacteristic,
error: Error?) {
if characteristic.isNotifying {
print("Subscription active: \(characteristic.uuid)")
} else {
print("Subscription inactive: \(characteristic.uuid)")
}
}
}
De abonnementsbeheerder NotificationManager demonstreert correct werken met CBPeripheral-meldingen. subscribeToAllNotifications doorloopt alle services en characteristics en activeert .notify en .indicate. subscribedCharacteristics volgt actieve abonnementen voor correct afmelden. didUpdateNotificationStateForCharacteristic bevestigt de succesvolle wijziging van de abonnementsstatus via de eigenschap characteristic.isNotifying.
Volledige werkcyclus met CBPeripheral omvat: het object van CBCentralManager ontvangen, verbinden, discovery, lezen/schrijven, abonneren op meldingen en verbreken. In het onderstaande voorbeeld is de klasse BLEConnection geïmplementeerd die de volledige levenscyclus van BLE-periferie in Swift beheert met behulp van de moderne async/await API (iOS 15+).
import CoreBluetooth
// Volledig CBPeripheral-beheervoorbeeld met 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. Verbinden met periferie
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) {
// BLE-apparaatstatus beheren
}
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
}
De klasse BLEConnection gebruikt Swift Concurrency (async/await) via CheckedContinuation — een modern patroon voor het werken met delegate-API's van Core Bluetooth. connect(to:) wacht op bevestiging van de verbinding via didConnectPeripheral, discoverServices() — via didDiscoverServices. Deze benadering elimineert geneste delegaten en maakt de BLE-code lineair en leesbaar. Foutafhandeling via BLEError dekt alle typische faalscenario's van BLE-verbindingen.
Veelgestelde vragen
CBPeripheral voor een eerder verbonden apparaat kan worden verkregen via retrievePeripheralsWithIdentifiers: op CBCentralManager. Geef een array van UUID's (NSUUID) van eerder opgeslagen apparaten door — het framework retourneert een array van CBPeripheral voor apparaten in de systeem-BLE-bondingdatabase. Dit werkt alleen voor apparaten waarmee de iPhone eerder was gekoppeld. Voor een nieuw apparaat is scannen verplicht.
Belangrijkste oorzaken: het apparaat is buiten bereik (RSSI onder drempel), de BLE-radio is uitgeschakeld (CBCentralManager.state != .poweredOn), de delegate CBPeripheralDelegate is niet ingesteld (peripheral.delegate = self) of de aanroep discoverServices is uitgevoerd vóór verbinding. Controleer de status centralManager.state, zorg ervoor dat de delegate is ingesteld vóór de connect-aanroep en gebruik retry met een time-out van 5–10 seconden.
Oorzaak — gebruik van .withResponse op een characteristic die alleen .writeWithoutResponse ondersteunt of omgekeerd. Controleer characteristic.properties vóór de aanroep. Ook kan er een MTU-probleem zijn: als gegevens > 20 bytes (BLE 4.0 MTU), is MTU-onderhandeling via negotiateMTU of fragmentatie vereist. Gebruik peripheral.maximumWriteValueLength(for: .withResponse) om de maximale pakketgrootte te bepalen.
CBPeripheral buiten bereik wordt niet onmiddellijk verbroken — iOS zet het via een time-out (meestal 20–30 seconden) over naar de toestand .disconnected. Voor monitoring gebruikt u readRSSI op CBPeripheral — bij onbereikbaarheid wordt een fout met code CBError.connectionTimeout geretourneerd. Volg ook centralManager:didDisconnectPeripheral:error: voor tijdige detectie van verbindingsverbreking.
Core Bluetooth is niet threadveilig — alle CBPeripheral-aanroepen moeten vanuit één wachtrij worden uitgevoerd (meestal main queue of een seriële serial queue gespecificeerd bij initialisatie van CBCentralManager). Gelijktijdige aanroepen vanuit verschillende threads leiden tot race conditions en applicatiecrashes. Gebruik DispatchQueue(label: „com.app.ble“) voor alle BLE-bewerkingen en DispatchQueue.main.async voor UI-updates.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook