CBPeripheral: vad det är, metoder och hantering av BLE-periferi på iOS

Författare: IT Sectr Publicerad: 2026-07-16 Lästid: 10 min

CBPeripheral — klassen i Core Bluetooth-ramverket som representerar en fjärr-BLE-enhet på iOS. Varje CBPeripheral-objekt inkapslar UUID, namn, RSSI och hierarkin av GATT-tjänster för den anslutna BLE-enheten. Utvecklaren interagerar med periferin uteslutande via CBPeripheral: upptäckt av tjänster (discoverServices:), läsning av egenskaper (readValueForCharacteristic:), skrivning av data (writeValue:forCharacteristic:type:) och prenumeration på notifikationer (setNotifyValue:forCharacteristic:). Enligt Apple Developer, 2026 är CBPeripheral — det centrala objektet för alla operationer med BLE-periferi, returnerat av CBCentralManager vid upptäckt eller anslutning av en enhet.

Huvudpunkter

  • CBPeripheral — Core Bluetooth-klass för arbete med fjärr-BLE-enhet på iOS
  • GATT-hierarki — Peripheral innehåller tjänster (CBService), tjänster innehåller egenskaper (CBCharacteristic), egenskaper innehåller deskriptorer (CBDescriptor)
  • Upptäckt — discoverServices: och discoverCharacteristics:forService: för att få enhetens GATT-struktur
  • Läsning och skrivning — readValueForCharacteristic: och writeValue:forCharacteristic:type: med bekräftelse (withResponse) eller utan (withoutResponse)
  • Notifikationer — setNotifyValue:forCharacteristic: aktiverar prenumeration på ändringar av BLE-enhetens egenskaper

Vad är CBPeripheral: essens och syfte

CBPeripheral — är ett objekt som representerar en fjärr-BLE-enhet i en iOS-applikation. Till skillnad från CBCentralManager, som hanterar iPhone:s lokala Bluetooth-adapter, modellerar CBPeripheral en extern perifer enhet: sensor, fitness-tracker, beacon, medicinsk enhet. Varje CBPeripheral-instans innehåller en unik identifierare (UUID) som bevaras mellan anslutningssessioner — Apple kopplar UUID:t till en specifik enhet via systemets Bonding.

CBPeripheral skapas inte direkt via init. Core Bluetooth-ramverket returnerar ett CBPeripheral-objekt i två scenarier: vid upptäckt av enheten via scanForPeripheralsWithServices: (delegat didDiscoverPeripheral) och vid anslutning till en tidigare känd enhet via retrievePeripheralsWithIdentifiers:. Efter mottagning av objektet anropar utvecklaren connectPeripheral: på CBCentralManager, varefter CBPeripheral blir tillgänglig för GATT-operationer.

Livscykel för CBPeripheral omfattar sex tillstånd: disconnected (initialt), connecting (efter anrop av connect), connected (efter didConnectPeripheral), discovering (under anrop av discoverServices), discovered (efter mottagning av tjänster) och disconnecting (efter cancelPeripheralConnection). Varje tillstånd övervakas via delegaten CBPeripheralDelegate — obligatoriskt protokoll för varje BLE-applikation på iOS.

CBPeripheral och GATT-hierarki: tjänster, egenskaper, deskriptorer

CBPeripheral lagrar en hierarkisk GATT-struktur som består av tre nivåer. Rotnivån — en array av CBService (tjänster), varje tjänst innehåller en array av CBCharacteristic (egenskaper), varje egenskap innehåller en array av CBDescriptor (deskriptorer). Denna modell överensstämmer helt med Bluetooth GATT-specifikationen: tjänst — enhetens funktion (till exempel „Heart Rate Service“), egenskap — konkret värde (puls 72 bpm), deskriptor — metadata för egenskapen (måttenheter, konfiguration av notifikationer).

NivåCore Bluetooth-klassBeskrivning
TjänstCBServiceLogisk grupp av relaterade egenskaper, identifierad av UUID (16-bit, 32-bit eller 128-bit)
EgenskapCBCharacteristicKonkret datavärde, stöder läsning, skrivning, notifikation
DeskriptorCBDescriptorMetadata för egenskapen: klientkonfiguration CCCD, User Description, Presentation Format

Standard BLE-tjänster är registrerade hos Bluetooth SIG: Heart Rate Service (UUID 180D), Battery Service (180F), Device Information (180A), Blood Pressure (1810). För anpassade tjänster används 128-bit UUID (till exempel E20A39F4-73F5-4BC4-A12F-17D1AD07A961). iOS känner automatiskt igen standard UUID och visar läsbara namn; anpassade UUID visas i hex-format.

Efter anslutning är CBPeripherals hierarki tom — tjänster och egenskaper är inte laddade. Utvecklaren måste anropa discoverServices: för att få tjänsterna och sedan för varje tjänst anropa discoverCharacteristics:forService:. Om tjänsten innehåller inkluderade tjänster (includedServices), anropas även discoverIncludedServices:forService:. Först efter slutförd upptäckt av hierarkin fylls CBPeripheral i och blir tillgänglig för läsning och skrivning.

Upptäckt av tjänster och egenskaper: metoder och delegater

Upptäckt (discovery) av CBPeripherals GATT-struktur — ett obligatoriskt steg före alla läs- eller skrivoperationer. Metoden discoverServices: startar en asynkron sökning efter alla enhetens tjänster. Om nil skickas upptäcks alla tjänster; om en array av CBUUID skickas — endast tjänster med angivna UUID (tidsoptimering). Resultatet kommer till delegaten peripheral:didDiscoverServices: — CBPeripheral-objektet fyller egenskapen services med en array av CBService.

Efter mottagning av tjänsterna måste discoverCharacteristics:forService: anropas för varje CBService. På samma sätt, nil — alla egenskaper, array av CBUUID — endast angivna. Resultat: peripheral:didDiscoverCharacteristicsForService:error:. I denna fas får CBCharacteristic egenskaper (properties: .read, .write, .notify, .indicate) som bestämmer tillåtna operationer.

swift
import CoreBluetooth

extension BLEViewController: CBPeripheralDelegate {

    // 1. Tjänstupptäckt
    func peripheral(_ peripheral: CBPeripheral,
                     didDiscoverServices error: Error?) {
        guard let services = peripheral.services else { return }

        for service in services {
            // Begär egenskaper för varje tjänst
            peripheral.discoverCharacteristics(nil, for: service)
        }
    }

    // 2. Egenskapernas upptäckt
    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. Läs värde
    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)")
    }
}

I exemplet implementeras tre obligatoriska upptäcktsmetoder för CBPeripheralDelegate. didDiscoverServices itererar genom alla funna tjänster och begär egenskaper. didDiscoverCharacteristicsForService kontrollerar varje egenskaps egenskaper: för .read anropar det readValue, för .notify — setNotifyValue(true). Metoden didUpdateValueForCharacteristic tar emot aktuellt värde i Data-format.

Läsning och skrivning av egenskaper: withResponse och withoutResponse

Läsning av värden CBCharacteristic utförs med metoden readValueForCharacteristic:. Resultatet kommer asynkront i peripheral:didUpdateValueForCharacteristic:error:. Viktigt: enheten kan ha ett cachat värde (characteristic.value är tillgängligt direkt efter upptäckt), men för att få aktuellt värde är anrop av readValue obligatoriskt. iOS kan cacha värden för energieffektivitet — readValue uppdaterar cachen.

Skrivning av värden utförs med metoden writeValue:forCharacteristic:type:. Parametern type bestämmer skrivningstyp: .withResponse (CBCharacteristicWriteWithResponse) — enheten bekräftar skrivningen via didWriteValueForCharacteristic; .withoutResponse (CBCharacteristicWriteWithoutResponse) — skrivning utan bekräftelse, maximal hastighet, men utan leveransgaranti. BLE-specifikationen begränsar MTU (Maximum Transmission Unit): upp till 23 byte för BLE 4.0, upp till 251 byte för BLE 5.0+. För data större än MTU krävs fragmentering på applikationsnivå.

swift
// CBPeripheral egenskapsläsning och skrivning
class BLEService {

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

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

    // Läs med bekräftelse
    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)
    }

    // Skriv med bekräftelse (withResponse)
    func writeWithResponse(data: Data) {
        guard let characteristic = findCharacteristic() else { return }
        peripheral.writeValue(data, for: characteristic,
                             type: .withResponse)
    }

    // Skriv utan bekräftelse (withoutResponse)
    // Maximal genomströmning, ingen leveransgaranti
    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 })
    }
}

Val av skrivningstyp withResponse eller withoutResponse beror på tillförlitlighetskraven. För kommandon (tänd lampa, öppna lås) använd withResponse — leveransgarantin är kritisk. För strömmande data (puls, temperatur) använd withoutResponse — förlust av ett paket är obetydlig. BLE-enheten kan endast stödja en skrivningstyp — kontrollera egenskapen characteristic.properties.contains(.write) och .writeWithoutResponse.

Prenumeration på BLE-notifikationer via setNotifyValue

Notifikationer (notifications) — BLE-mekanism där den perifera enheten skickar egenskapsvärdet till den centrala enheten asynkront, utan konstant polling från den centrala sidan. CBPeripheral aktiverar prenumeration via metoden setNotifyValue:forCharacteristic:. Efter aktivering av prenumerationen skriver iOS automatiskt CCCD (Client Characteristic Configuration Descriptor) på periferin och enheten börjar skicka uppdateringar vid varje värdeändring.

Till skillnad från indikationer (indicate) kräver notifikationer ingen bekräftelse från den centrala — paketet skickas och glöms. Detta ger maximal bandbredd, men paketförlust är möjlig. Indikationer kräver bekräftelse på protokollnivå (L2CAP) — mer tillförlitliga men långsammare. CBCharacteristic anger via egenskapen properties exakt vilket läge som stöds: .notify, .indicate eller båda.

Vid frånkoppling av CBPeripheral (disconnect, lämna räckvidd) återställs alla aktiva prenumerationer automatiskt. Vid återanslutning måste setNotifyValue:true anropas igen för varje egenskap. iOS förlorar också prenumerationer när applikationen lämnar förgrunden (om bakgrundsläget inte är aktiverat) — för bakgrundsarbete måste funktionen „Uses Bluetooth LE accessories“ aktiveras i Info.plist.

swift
// Hantering av CBPeripheral notifikationsprenumeration
class NotificationManager: NSObject {

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

    // Prenumerera på notifikationer för alla .notify-egenskaper
    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)
                }
            }
        }
    }

    // Avsluta prenumeration på alla notifikationer
    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()
    }

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

Prenumerationshanteraren NotificationManager demonstrerar korrekt arbete med CBPeripheral-notifikationer. subscribeToAllNotifications itererar genom alla tjänster och egenskaper och aktiverar .notify och .indicate. subscribedCharacteristics spårar aktiva prenumerationer för korrekt avprenumeration. didUpdateNotificationStateForCharacteristic bekräftar framgångsrik ändring av prenumerationsstatus via egenskapen characteristic.isNotifying.

Fullständigt exempel på arbete med CBPeripheral i Swift

Fullständig arbetscykel med CBPeripheral omfattar: mottagning av objektet från CBCentralManager, anslutning, upptäckt, läsning/skrivning, prenumeration på notifikationer och frånkoppling. I exemplet nedan implementeras klassen BLEConnection som hanterar den fullständiga livscykeln för BLE-periferi i Swift med hjälp av moderna async/await API (iOS 15+).

swift
import CoreBluetooth

// Fullständigt CBPeripheral-hanteringsexempel med 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. Anslut till periferi
    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. Upptäckt  
    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) {
        // Hantera Bluetooth-enhetens tillstånd
    }

    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
}

Klassen BLEConnection använder Swift Concurrency (async/await) via CheckedContinuation — ett modernt mönster för arbete med Core Bluetooths delegat-API:er. connect(to:) väntar på bekräftelse av anslutning via didConnectPeripheral, discoverServices() — via didDiscoverServices. Detta tillvägagångssätt eliminerar nästlade delegater och gör BLE-koden linjär och läsbar. Felhantering via BLEError täcker alla typiska fellägen för BLE-anslutning.

Vanliga frågor

Hur får jag CBPeripheral utan skanning?

CBPeripheral för en tidigare ansluten enhet kan erhållas via retrievePeripheralsWithIdentifiers: på CBCentralManager. Skicka en array av UUID (NSUUID) för tidigare sparade enheter — ramverket returnerar en array av CBPeripheral för enheter i systemets BLE-bondingdatabas. Detta fungerar endast för enheter som iPhone tidigare har parats med. För en ny enhet är skanning obligatorisk.

Varför upptäcker inte CBPeripheral tjänster?

Huvudorsaker: enheten är utom räckvidd (RSSI under tröskelvärdet), BLE-radion är avstängd (CBCentralManager.state != .poweredOn), delegaten CBPeripheralDelegate är inte inställd (peripheral.delegate = self) eller anropet discoverServices utfördes före anslutning. Kontrollera status centralManager.state, se till att delegaten är inställd före anropet connect och använd försök igen med en timeout på 5–10 sekunder.

Vad gör jag om writeValue inte svarar?

Orsak — användning av .withResponse på en egenskap som endast stöder .writeWithoutResponse eller vice versa. Kontrollera characteristic.properties före anropet. Det kan också vara ett MTU-problem: om data > 20 byte (BLE 4.0 MTU) krävs MTU-förhandling via negotiateMTU eller fragmentering. Använd peripheral.maximumWriteValueLength(for: .withResponse) för att bestämma maximal paketstorlek.

Hur skiljer jag CBPeripheral inom räckvidd från otillgänglig?

CBPeripheral utom räckvidd kopplas inte omedelbart från — iOS överför den till tillståndet .disconnected via en timeout (vanligtvis 20–30 sekunder). För övervakning, använd readRSSI på CBPeripheral — vid otillgänglighet returnerar den ett fel med koden CBError.connectionTimeout. Övervaka även centralManager:didDisconnectPeripheral:error: för snabb upptäckt av anslutningsavbrott.

Kan jag använda en CBPeripheral från flera trådar?

Core Bluetooth är inte trådsäkert — alla CBPeripheral-anrop måste utföras från en kö (vanligtvis main queue eller en seriell serial queue som anges vid initialisering av CBCentralManager). Samtidiga anrop från olika trådar leder till race condition och applikationskrasch. Använd DispatchQueue(label: „com.app.ble“) för alla BLE-operationer och DispatchQueue.main.async för UI-uppdateringar.

Sammanfattning

  • CBPeripheral — Core Bluetooth-klass för arbete med fjärr-BLE-enhet på iOS, returnerad av CBCentralManager
  • GATT-hierarki består av tjänster (CBService), egenskaper (CBCharacteristic) och deskriptorer (CBDescriptor) med 16-bit eller 128-bit UUID
  • Upptäckt utförs sekventiellt: discoverServices: → discoverCharacteristics:forService: med bearbetning via delegat
  • Läsning — readValueForCharacteristic:, skrivning — writeValue:forCharacteristic:type: (.withResponse eller .withoutResponse)
  • Notifikationer — setNotifyValue:forCharacteristic: aktiverar asynkron datasändning från periferi till central
  • MTU BLE 4.0 begränsar paket till 23 byte, BLE 5.0+ — upp till 251 byte, data större än MTU kräver fragmentering
  • Async/await Swift via CheckedContinuation förenklar BLE-kod genom att ersätta nästlade delegater med linjära anrop

Vi utvecklar en mobil applikation nyckelfärdigt

IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.

Diskutera projektet

Läs också