CBPeripheral — η κλάση του πλαισίου Core Bluetooth που αντιπροσωπεύει μια απομακρυσμένη συσκευή BLE στο iOS. Κάθε αντικείμενο CBPeripheral ενσωματώνει UUID, όνομα, RSSI και την ιεραρχία υπηρεσιών GATT της συνδεδεμένης συσκευής BLE. Ο προγραμματιστής αλληλεπιδρά με το περιφερειακό αποκλειστικά μέσω του CBPeripheral: ανακάλυψη υπηρεσιών (discoverServices:), ανάγνωση χαρακτηριστικών (readValueForCharacteristic:), εγγραφή δεδομένων (writeValue:forCharacteristic:type:) και εγγραφή σε ειδοποιήσεις (setNotifyValue:forCharacteristic:). Σύμφωνα με το Apple Developer, 2026, το CBPeripheral — το κεντρικό αντικείμενο για όλες τις λειτουργίες με BLE-περιφερειακό, που επιστρέφεται από το CBCentralManager κατά τον εντοπισμό ή τη σύνδεση της συσκευής.
Κύρια σημεία
CBPeripheral — είναι ένα αντικείμενο που αντιπροσωπεύει μια απομακρυσμένη συσκευή BLE σε μια εφαρμογή iOS. Σε αντίθεση με το CBCentralManager, το οποίο διαχειρίζεται τον τοπικό προσαρμογέα Bluetooth του iPhone, το CBPeripheral μοντελοποιεί μια εξωτερική περιφερειακή συσκευή: αισθητήρα, ιχνηλάτη γυμναστικής, φάρο, ιατρική συσκευή. Κάθε στιγμιότυπο CBPeripheral περιέχει ένα μοναδικό αναγνωριστικό (UUID) που διατηρείται μεταξύ των συνεδριών σύνδεσης — η Apple συνδέει το UUID με μια συγκεκριμένη συσκευή μέσω του συστήματος Bonding.
Το CBPeripheral δεν δημιουργείται άμεσα μέσω init. Το πλαίσιο Core Bluetooth επιστρέφει ένα αντικείμενο CBPeripheral σε δύο σενάρια: κατά τον εντοπισμό της συσκευής μέσω scanForPeripheralsWithServices: (εκπρόσωπος didDiscoverPeripheral) και κατά τη σύνδεση σε μια προηγουμένως γνωστή συσκευή μέσω retrievePeripheralsWithIdentifiers:. Μετά τη λήψη του αντικειμένου, ο προγραμματιστής καλεί το connectPeripheral: στο CBCentralManager, μετά το οποίο το CBPeripheral γίνεται διαθέσιμο για λειτουργίες GATT.
Ο κύκλος ζωής του CBPeripheral περιλαμβάνει έξι καταστάσεις: disconnected (αρχική), connecting (μετά την κλήση connect), connected (μετά το didConnectPeripheral), discovering (κατά τη διάρκεια της κλήσης discoverServices), discovered (μετά τη λήψη υπηρεσιών) και disconnecting (μετά το cancelPeripheralConnection). Κάθε κατάσταση παρακολουθείται μέσω του εκπροσώπου CBPeripheralDelegate — υποχρεωτικό πρωτόκολλο για κάθε εφαρμογή BLE στο iOS.
CBPeripheral αποθηκεύει μια ιεραρχική δομή GATT που αποτελείται από τρία επίπεδα. Το επίπεδο ρίζας — ένας πίνακας CBService (υπηρεσίες), κάθε υπηρεσία περιέχει έναν πίνακα CBCharacteristic (χαρακτηριστικά), κάθε χαρακτηριστικό περιέχει έναν πίνακα CBDescriptor (περιγραφείς). Αυτό το μοντέλο αντιστοιχεί πλήρως στην προδιαγραφή Bluetooth GATT: υπηρεσία — λειτουργία της συσκευής (για παράδειγμα, «Heart Rate Service»), χαρακτηριστικό — συγκεκριμένη τιμή (σφυγμός 72 bpm), περιγραφέας — μεταδεδομένα του χαρακτηριστικού (μονάδες μέτρησης, διαμόρφωση ειδοποιήσεων).
| Επίπεδο | Κλάση Core Bluetooth | Περιγραφή |
|---|---|---|
| Υπηρεσία | CBService | Λογική ομάδα συναφών χαρακτηριστικών, αναγνωρίζεται από UUID (16-bit, 32-bit ή 128-bit) |
| Χαρακτηριστικό | CBCharacteristic | Συγκεκριμένη τιμή δεδομένων, υποστηρίζει ανάγνωση, εγγραφή, ειδοποίηση |
| Περιγραφέας | CBDescriptor | Μεταδεδομένα χαρακτηριστικού: διαμόρφωση πελάτη CCCD, User Description, Presentation Format |
Οι τυπικές BLE-υπηρεσίες είναι καταχωρημένες στο Bluetooth SIG: Heart Rate Service (UUID 180D), Battery Service (180F), Device Information (180A), Blood Pressure (1810). Για προσαρμοσμένες υπηρεσίες χρησιμοποιούνται 128-bit UUID (για παράδειγμα, E20A39F4-73F5-4BC4-A12F-17D1AD07A961). Το iOS αναγνωρίζει αυτόματα τα τυπικά UUID και εμφανίζει αναγνώσιμα ονόματα· τα προσαρμοσμένα UUID εμφανίζονται σε μορφή hex.
Μετά τη σύνδεση, η ιεραρχία του CBPeripheral είναι κενή — οι υπηρεσίες και τα χαρακτηριστικά δεν έχουν φορτωθεί. Ο προγραμματιστής πρέπει να καλέσει discoverServices: για να λάβει τις υπηρεσίες και στη συνέχεια για κάθε υπηρεσία να καλέσει discoverCharacteristics:forService:. Εάν η υπηρεσία περιέχει ενσωματωμένες υπηρεσίες (includedServices), καλείται επιπλέον το discoverIncludedServices:forService:. Μόνο μετά την ολοκλήρωση της ανακάλυψης της ιεραρχίας, το CBPeripheral συμπληρώνεται και γίνεται διαθέσιμο για ανάγνωση και εγγραφή.
Ανακάλυψη (discovery) της δομής GATT του CBPeripheral — υποχρεωτικό βήμα πριν από οποιεσδήποτε λειτουργίες ανάγνωσης ή εγγραφής. Η μέθοδος discoverServices: ξεκινά μια ασύγχρονη αναζήτηση όλων των υπηρεσιών της συσκευής. Εάν μεταβιβαστεί nil, ανακαλύπτονται όλες οι υπηρεσίες· εάν μεταβιβαστεί ένας πίνακας CBUUID — μόνο οι υπηρεσίες με τα καθορισμένα UUID (βελτιστοποίηση χρόνου). Το αποτέλεσμα φτάνει στον εκπρόσωπο peripheral:didDiscoverServices: — το αντικείμενο CBPeripheral συμπληρώνει την ιδιότητα services με έναν πίνακα CBService.
Μετά τη λήψη των υπηρεσιών, για κάθε CBService πρέπει να κληθεί discoverCharacteristics:forService:. Παρόμοια, nil — όλα τα χαρακτηριστικά, πίνακας CBUUID — μόνο τα καθορισμένα. Αποτέλεσμα: peripheral:didDiscoverCharacteristicsForService:error:. Σε αυτό το στάδιο, το CBCharacteristic λαμβάνει ιδιότητες (properties: .read, .write, .notify, .indicate) που καθορίζουν τις επιτρεπόμενες λειτουργίες.
import CoreBluetooth
extension BLEViewController: CBPeripheralDelegate {
// 1. Ανακάλυψη υπηρεσίας
func peripheral(_ peripheral: CBPeripheral,
didDiscoverServices error: Error?) {
guard let services = peripheral.services else { return }
for service in services {
// Αίτημα χαρακτηριστικών για κάθε υπηρεσία
peripheral.discoverCharacteristics(nil, for: service)
}
}
// 2. Ανακάλυψη χαρακτηριστικού
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. Ανάγνωση τιμής
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)")
}
}
Στο παράδειγμα, τρεις υποχρεωτικές μέθοδοι ανακάλυψης του CBPeripheralDelegate έχουν υλοποιηθεί. Το didDiscoverServices διατρέχει όλες τις υπηρεσίες που βρέθηκαν και ζητά χαρακτηριστικά. Το didDiscoverCharacteristicsForService ελέγχει τις ιδιότητες κάθε χαρακτηριστικού: για .read καλεί readValue, για .notify — setNotifyValue(true). Η μέθοδος didUpdateValueForCharacteristic λαμβάνει την τρέχουσα τιμή σε μορφή Data.
Ανάγνωση τιμών CBCharacteristic γίνεται με τη μέθοδο readValueForCharacteristic:. Το αποτέλεσμα φτάνει ασύγχρονα στο peripheral:didUpdateValueForCharacteristic:error:. Σημαντικό: η συσκευή μπορεί να έχει προσωρινά αποθηκευμένη τιμή (το characteristic.value είναι διαθέσιμο αμέσως μετά την ανακάλυψη), αλλά για τη λήψη της τρέχουσας τιμής, η κλήση readValue είναι υποχρεωτική. Το iOS μπορεί να αποθηκεύει προσωρινά τιμές για ενεργειακή απόδοση — το readValue ανανεώνει την προσωρινή μνήμη.
Εγγραφή τιμών γίνεται με τη μέθοδο writeValue:forCharacteristic:type:. Η παράμετρος type καθορίζει τον τύπο εγγραφής: .withResponse (CBCharacteristicWriteWithResponse) — η συσκευή επιβεβαιώνει την εγγραφή μέσω didWriteValueForCharacteristic; .withoutResponse (CBCharacteristicWriteWithoutResponse) — εγγραφή χωρίς επιβεβαίωση, μέγιστη ταχύτητα, αλλά χωρίς εγγύηση παράδοσης. Η προδιαγραφή BLE περιορίζει το MTU (Maximum Transmission Unit): έως 23 byte για BLE 4.0, έως 251 byte για BLE 5.0+. Για δεδομένα μεγαλύτερα από το MTU, απαιτείται κατακερματισμός σε επίπεδο εφαρμογής.
// Ανάγνωση και εγγραφή χαρακτηριστικού 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
}
// Ανάγνωση με επιβεβαίωση
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)
}
// Εγγραφή με επιβεβαίωση (withResponse)
func writeWithResponse(data: Data) {
guard let characteristic = findCharacteristic() else { return }
peripheral.writeValue(data, for: characteristic,
type: .withResponse)
}
// Εγγραφή χωρίς επιβεβαίωση (withoutResponse)
// Μέγιστη απόδοση, χωρίς εγγύηση παράδοσης
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 })
}
}
Η επιλογή του τύπου εγγραφής withResponse ή withoutResponse εξαρτάται από τις απαιτήσεις αξιοπιστίας. Για εντολές (άναμμα φωτός, άνοιγμα κλειδαριάς) χρησιμοποιήστε withResponse — η εγγύηση παράδοσης είναι κρίσιμη. Για δεδομένα ροής (σφυγμός, θερμοκρασία) χρησιμοποιήστε withoutResponse — η απώλεια ενός πακέτου είναι ασήμαντη. Η συσκευή BLE μπορεί να υποστηρίζει μόνο έναν τύπο εγγραφής — ελέγξτε την ιδιότητα characteristic.properties.contains(.write) και .writeWithoutResponse.
Ειδοποιήσεις (notifications) — μηχανισμός BLE κατά τον οποίο η περιφερειακή συσκευή στέλνει την τιμή του χαρακτηριστικού στην κεντρική συσκευή ασύγχρονα, χωρίς συνεχή polling από την κεντρική πλευρά. Το CBPeripheral ενεργοποιεί την εγγραφή μέσω της μεθόδου setNotifyValue:forCharacteristic:. Μετά την ενεργοποίηση της εγγραφής, το iOS γράφει αυτόματα το CCCD (Client Characteristic Configuration Descriptor) στο περιφερειακό και η συσκευή αρχίζει να στέλνει ενημερώσεις σε κάθε αλλαγή τιμής.
Σε αντίθεση με τις ενδείξεις (indicate), οι ειδοποιήσεις δεν απαιτούν επιβεβαίωση από το κεντρικό — το πακέτο αποστέλλεται και ξεχνιέται. Αυτό παρέχει μέγιστο εύρος ζώνης, αλλά είναι πιθανή η απώλεια πακέτων. Οι ενδείξεις απαιτούν επιβεβαίωση σε επίπεδο πρωτοκόλλου (L2CAP) — πιο αξιόπιστες αλλά πιο αργές. Το CBCharacteristic μέσω της ιδιότητας properties υποδεικνύει ακριβώς ποια λειτουργία υποστηρίζει: .notify, .indicate ή και τα δύο.
Κατά την αποσύνδεση του CBPeripheral (disconnect, έξοδος από την εμβέλεια), όλες οι ενεργές εγγραφές επαναφέρονται αυτόματα. Κατά την επανασύνδεση, πρέπει να κληθεί ξανά setNotifyValue:true για κάθε χαρακτηριστικό. Το iOS χάνει επίσης τις εγγραφές όταν η εφαρμογή βγαίνει από το foreground (εάν η λειτουργία παρασκηνίου δεν είναι ενεργοποιημένη) — για εργασία στο παρασκήνιο απαιτείται ενεργοποίηση της δυνατότητας «Uses Bluetooth LE accessories» στο Info.plist.
// Διαχείριση εγγραφής ειδοποιήσεων CBPeripheral
class NotificationManager: NSObject {
private var peripheral: CBPeripheral?
private var subscribedCharacteristics: Set<CBUUID> = []
// Εγγραφή σε ειδοποιήσεις για όλα τα χαρακτηριστικά .notify
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)
}
}
}
}
// Απεγγραφή από όλες τις ειδοποιήσεις
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()
}
// Χειριστής ειδοποιήσεων
func peripheral(_ peripheral: CBPeripheral,
didUpdateNotificationStateFor characteristic: CBCharacteristic,
error: Error?) {
if characteristic.isNotifying {
print("Subscription active: \(characteristic.uuid)")
} else {
print("Subscription inactive: \(characteristic.uuid)")
}
}
}
Ο διαχειριστής εγγραφών NotificationManager δείχνει τη σωστή εργασία με τις ειδοποιήσεις CBPeripheral. Το subscribeToAllNotifications διατρέχει όλες τις υπηρεσίες και τα χαρακτηριστικά, ενεργοποιώντας .notify και .indicate. Το subscribedCharacteristics παρακολουθεί τις ενεργές εγγραφές για σωστή απεγγραφή. Το didUpdateNotificationStateForCharacteristic επιβεβαιώνει την επιτυχή αλλαγή κατάστασης εγγραφής μέσω της ιδιότητας characteristic.isNotifying.
Πλήρης κύκλος εργασίας με το CBPeripheral περιλαμβάνει: λήψη του αντικειμένου από το CBCentralManager, σύνδεση, ανακάλυψη, ανάγνωση/εγγραφή, εγγραφή σε ειδοποιήσεις και αποσύνδεση. Στο παρακάτω παράδειγμα, υλοποιείται η κλάση BLEConnection, η οποία διαχειρίζεται τον πλήρη κύκλο ζωής του BLE-περιφερειακού σε Swift χρησιμοποιώντας το σύγχρονο async/await API (iOS 15+).
import CoreBluetooth
// Πλήρες παράδειγμα διαχείρισης CBPeripheral με 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. Σύνδεση στο περιφερειακό
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. Ανακάλυψη
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) {
// Χειρισμός κατάστασης συσκευής Bluetooth
}
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
}
Η κλάση BLEConnection χρησιμοποιεί Swift Concurrency (async/await) μέσω CheckedContinuation — ένα σύγχρονο μοτίβο για εργασία με τα delegate API του Core Bluetooth. Το connect(to:) περιμένει επιβεβαίωση σύνδεσης μέσω didConnectPeripheral, το discoverServices() — μέσω didDiscoverServices. Αυτή η προσέγγιση εξαλείφει τους ένθετους εκπροσώπους και καθιστά τον κώδικα BLE γραμμικό και ευανάγνωστο. Ο χειρισμός σφαλμάτων μέσω BLEError καλύπτει όλα τα τυπικά σενάρια αποτυχίας σύνδεσης BLE.
Συχνές Ερωτήσεις
Το CBPeripheral για μια προηγουμένως συνδεδεμένη συσκευή μπορεί να ληφθεί μέσω retrievePeripheralsWithIdentifiers: στο CBCentralManager. Περάστε έναν πίνακα UUID (NSUUID) προηγουμένως αποθηκευμένων συσκευών — το πλαίσιο θα επιστρέψει έναν πίνακα CBPeripheral για συσκευές στη βάση δεδομένων συστήματος BLE-bonding. Αυτό λειτουργεί μόνο για συσκευές με τις οποίες το iPhone είχε προηγουμένως αντιστοιχιστεί. Για μια νέα συσκευή, η σάρωση είναι υποχρεωτική.
Κύριες αιτίες: η συσκευή είναι εκτός εμβέλειας (RSSI κάτω από το όριο), το ραδιόφωνο BLE είναι απενεργοποιημένο (CBCentralManager.state != .poweredOn), ο εκπρόσωπος CBPeripheralDelegate δεν έχει οριστεί (peripheral.delegate = self) ή η κλήση discoverServices πραγματοποιήθηκε πριν από τη σύνδεση. Ελέγξτε την κατάσταση centralManager.state, βεβαιωθείτε ότι ο εκπρόσωπος έχει οριστεί πριν από την κλήση connect και χρησιμοποιήστε retry με χρονικό όριο 5–10 δευτερόλεπτα.
Αιτία — χρήση .withResponse σε χαρακτηριστικό που υποστηρίζει μόνο .writeWithoutResponse ή αντίστροφα. Ελέγξτε το characteristic.properties πριν από την κλήση. Επίσης, μπορεί να υπάρχει πρόβλημα MTU: εάν τα δεδομένα > 20 byte (BLE 4.0 MTU), απαιτείται διαπραγμάτευση MTU μέσω negotiateMTU ή κατακερματισμός. Χρησιμοποιήστε το peripheral.maximumWriteValueLength(for: .withResponse) για να προσδιορίσετε το μέγιστο μέγεθος πακέτου.
Το CBPeripheral εκτός εμβέλειας δεν αποσυνδέεται αμέσως — το iOS το μεταφέρει στην κατάσταση .disconnected μέσω χρονικού ορίου (συνήθως 20–30 δευτερόλεπτα). Για παρακολούθηση, χρησιμοποιήστε readRSSI στο CBPeripheral — όταν δεν είναι διαθέσιμο, θα επιστρέψει σφάλμα με κωδικό CBError.connectionTimeout. Επίσης, παρακολουθήστε το centralManager:didDisconnectPeripheral:error: για έγκαιρο εντοπισμό διακοπής σύνδεσης.
Το Core Bluetooth δεν είναι ασφαλές για νήματα — όλες οι κλήσεις CBPeripheral πρέπει να εκτελούνται από μία ουρά (συνήθως main queue ή σειριακή serial queue που καθορίζεται κατά την αρχικοποίηση του CBCentralManager). Ταυτόχρονες κλήσεις από διαφορετικά νήματα οδηγούν σε race condition και κατάρρευση της εφαρμογής. Χρησιμοποιήστε DispatchQueue(label: «com.app.ble») για όλες τις λειτουργίες BLE και DispatchQueue.main.async για ενημερώσεις UI.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης