CBPeripheral — کلاسی از فریمورک Core Bluetooth که یک دستگاه BLE راه دور را در iOS نمایش میدهد. هر شیء CBPeripheral شامل UUID، نام، RSSI و سلسلهمراتب سرویسهای GATT دستگاه BLE متصل شده است. توسعهدهنده منحصراً از طریق CBPeripheral با پیرامونی تعامل میکند: discovery سرویسها (discoverServices:)، خواندن ویژگیها (readValueForCharacteristic:)، نوشتن دادهها (writeValue:forCharacteristic:type:) و اشتراک در اعلانها (setNotifyValue:forCharacteristic:). بر اساس Apple Developer, 2026، CBPeripheral — شیء مرکزی برای تمام عملیات با BLE-پیرامونی است که توسط CBCentralManager هنگام کشف یا اتصال دستگاه بازگردانده میشود.
نکات اصلی
CBPeripheral — شیئی است که یک دستگاه BLE راه دور را در برنامه iOS نمایش میدهد. برخلاف CBCentralManager که آداپتور بلوتوث محلی 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). برای سرویسهای سفارشی از UUID 128-bit استفاده میشود (مثلاً E20A39F4-73F5-4BC4-A12F-17D1AD07A961). iOS بهطور خودکار UUIDهای استاندارد را تشخیص داده و نامهای قابل خواندن توسط انسان را نمایش میدهد؛ UUIDهای سفارشی در قالب hex نمایش داده میشوند.
پس از اتصال، سلسلهمراتب CBPeripheral خالی است — سرویسها و ویژگیها بارگذاری نشدهاند. توسعهدهنده باید discoverServices: را برای دریافت سرویسها و سپس برای هر سرویس discoverCharacteristics:forService: را فراخوانی کند. اگر سرویس شامل سرویسهای توکار (includedServices) باشد، discoverIncludedServices:forService: نیز فراخوانی میشود. تنها پس از تکمیل discovery سلسلهمراتب، 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 {
// ۱. کشف سرویس
func peripheral(_ peripheral: CBPeripheral,
didDiscoverServices error: Error?) {
guard let services = peripheral.services else { return }
for service in services {
// درخواست ویژگیها برای هر سرویس
peripheral.discoverCharacteristics(nil, for: service)
}
}
// ۲. کشف ویژگی
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)
}
}
}
// ۳. خواندن مقدار
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 بلافاصله پس از discovery در دسترس است)، اما برای دریافت مقدار فعلی، فراخوانی readValue الزامی است. iOS ممکن است مقادیر را برای بهرهوری انرژی ذخیره کند — readValue حافظه پنهان را بهروز میکند.
نوشتن مقادیر با روش writeValue:forCharacteristic:type: انجام میشود. پارامتر type نوع نوشتن را تعیین میکند: .withResponse (CBCharacteristicWriteWithResponse) — دستگاه نوشتن را از طریق didWriteValueForCharacteristic تأیید میکند؛ .withoutResponse (CBCharacteristicWriteWithoutResponse) — نوشتن بدون تأیید، حداکثر سرعت، اما بدون تضمین تحویل. مشخصات BLE MTU (واحد حداکثر انتقال) را محدود میکند: تا 23 بایت برای BLE 4.0، تا 251 بایت برای 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 که در آن دستگاه پیرامونی مقدار ویژگی را به صورت ناهمزمان به دستگاه مرکزی ارسال میکند، بدون نظرسنجی مداوم از طرف مرکزی. CBPeripheral اشتراک را از طریق روش setNotifyValue:forCharacteristic: فعال میکند. پس از فعالسازی اشتراک، iOS به طور خودکار CCCD (توصیفگر پیکربندی ویژگی مشتری) را در پیرامونی مینویسد و دستگاه شروع به ارسال بهروزرسانیها در هر تغییر مقدار میکند.
برخلاف نشانهها (indicate)، اعلانها نیاز به تأیید از مرکزی ندارند — بسته ارسال و فراموش میشود. این حداکثر پهنای باند را فراهم میکند، اما از دست رفتن بسته ممکن است. نشانهها نیاز به تأیید در سطح پروتکل (L2CAP) دارند — قابل اعتمادتر اما کندتر. CBCharacteristic از طریق ویژگی properties دقیقاً مشخص میکند که کدام حالت را پشتیبانی میکند: .notify، .indicate یا هر دو.
هنگام قطع اتصال CBPeripheral (قطع اتصال، خروج از محدوده)، همه اشتراکهای فعال به طور خودکار بازنشانی میشوند. هنگام اتصال مجدد، باید دوباره setNotifyValue:true را برای هر ویژگی فراخوانی کرد. iOS همچنین هنگام خروج برنامه از foreground اشتراکها را از دست میدهد (اگر حالت background فعال نباشد) — برای کار در پسزمینه باید capability "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، اتصال، discovery، خواندن/نوشتن، اشتراک در اعلانها و قطع اتصال است. در مثال زیر کلاس BLEConnection پیادهسازی شده است که چرخه حیات کامل BLE-پیرامونی را در Swift با استفاده از API مدرن async/await (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
}
// ۱. اتصال به پیرامونی
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
}
}
// ۲. کشف
func discoverServices() async throws {
guard let peripheral = peripheral else {
throw BLEError.notConnected
}
peripheral.discoverServices(nil)
try await withCheckedThrowingContinuation { continuation in
self.continuation = continuation
}
}
}
// ۳. CBCentralManager
extension BLEConnection: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
// مدیریت وضعیت دستگاه بلوتوث
}
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 استفاده میکند — الگوی مدرن برای کار با 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 با تایماوت ۵–۱۰ ثانیه استفاده کنید.
علت — استفاده از .withResponse روی ویژگیای که فقط .writeWithoutResponse را پشتیبانی میکند یا برعکس. characteristic.properties را قبل از فراخوانی بررسی کنید. همچنین ممکن است مشکل MTU وجود داشته باشد: اگر داده > ۲۰ بایت (BLE 4.0 MTU) باشد، توافق MTU از طریق negotiateMTU یا تکهتکهکردن لازم است. از peripheral.maximumWriteValueLength(for: .withResponse) برای تعیین حداکثر اندازه بسته استفاده کنید.
CBPeripheral خارج از محدوده فوراً قطع نمیشود — iOS آن را از طریق تایماوت (معمولاً ۲۰–۳۰ ثانیه) به حالت .disconnected منتقل میکند. برای نظارت از readRSSI در CBPeripheral استفاده کنید — در صورت عدم دسترسی، خطایی با کد CBError.connectionTimeout برمیگرداند. همچنین centralManager:didDisconnectPeripheral:error: را برای تشخیص بهموقع قطع اتصال ردیابی کنید.
Core Bluetooth ایمن از نظر رشته نیست — همه فراخوانیهای CBPeripheral باید از یک صف اجرا شوند (معمولاً main queue یا serial queue متوالی مشخص شده در مقداردهی CBCentralManager). فراخوانیهای همزمان از رشتههای مختلف منجر به race condition و crash برنامه میشود. از DispatchQueue(label: "com.app.ble") برای تمام عملیات BLE و DispatchQueue.main.async برای بهروزرسانی UI استفاده کنید.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید