CBPeripheral — Core Bluetooth框架的类,代表iOS上的远程BLE设备。每个CBPeripheral对象封装了所连接BLE设备的UUID、名称、RSSI和GATT服务层次结构。开发人员完全通过CBPeripheral与外设交互:发现服务(discoverServices:)、读取特征(readValueForCharacteristic:)、写入数据(writeValue:forCharacteristic:type:)和订阅通知(setNotifyValue:forCharacteristic:)。根据Apple Developer, 2026,CBPeripheral是所有BLE外设操作的核心对象,由CBCentralManager在检测或连接设备时返回。
要点
CBPeripheral — 是一个对象,代表iOS应用程序中的远程BLE设备。与管理iPhone本地蓝牙适配器的CBCentralManager不同,CBPeripheral对外部外围设备进行建模:传感器、健身追踪器、信标、医疗设备。每个CBPeripheral实例都包含一个唯一标识符(UUID),该标识符在连接会话之间保留 — Apple通过系统Bonding将UUID与特定设备关联。
CBPeripheral不是直接通过init创建的。Core Bluetooth框架在两种情况下返回CBPeripheral对象:通过scanForPeripheralsWithServices:检测设备时(委托didDiscoverPeripheral)和通过retrievePeripheralsWithIdentifiers:连接到以前已知的设备时。收到对象后,开发人员在CBCentralManager上调用connectPeripheral:,之后CBPeripheral可用于GATT操作。
CBPeripheral的生命周期包括六种状态:disconnected(初始)、connecting(调用connect后)、connected(didConnectPeripheral后)、discovering(调用discoverServices期间)、discovered(收到服务后)和disconnecting(cancelPeripheralConnection后)。每种状态都通过CBPeripheralDelegate委托跟踪 — iOS上每个BLE应用程序的必需协议。
CBPeripheral存储由三个级别组成的层次化GATT结构。根级别 — CBService数组(服务),每个服务包含CBCharacteristic数组(特征),每个特征包含CBDescriptor数组(描述符)。此模型完全符合Bluetooth GATT规范:服务 — 设备的功能(例如“Heart Rate Service”),特征 — 具体值(脉搏72 bpm),描述符 — 特征的元数据(测量单位、通知配置)。
| 级别 | Core Bluetooth类 | 描述 |
|---|---|---|
| 服务 | CBService | 相关特征的逻辑组,由UUID(16位、32位或128位)标识 |
| 特征 | 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位UUID(例如E20A39F4-73F5-4BC4-A12F-17D1AD07A961)。iOS自动识别标准UUID并显示人类可读的名称;自定义UUID以十六进制格式显示。
连接后,CBPeripheral的层次结构为空 — 服务和特征未加载。开发人员必须调用discoverServices:来获取服务,然后为每个服务调用discoverCharacteristics:forService:。如果服务包含includeServices,则还需要调用discoverIncludedServices:forService:。只有在层次结构发现完成后,CBPeripheral才会被填充并可用于读取和写入。
发现CBPeripheral的GATT结构 — 在进行任何读取或写入操作之前的必要步骤。discoverServices:方法启动设备所有服务的异步搜索。如果传递nil,则发现所有服务;如果传递CBUUID数组 — 仅发现具有指定UUID的服务(时间优化)。结果到达委托peripheral:didDiscoverServices: — CBPeripheral对象用CBService数组填充services属性。
收到服务后,必须为每个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(最大传输单元):BLE 4.0可达23字节,BLE 5.0+可达251字节。对于大于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属性。
通知 — BLE机制,外围设备异步将特征值发送到中央设备,无需中央设备进行持续轮询。CBPeripheral通过setNotifyValue:forCharacteristic:方法启用订阅。激活订阅后,iOS自动在外设上写入CCCD(客户端特征配置描述符),并且设备在每次值更改时开始发送更新。
与指示不同,通知不需要中央的确认 — 数据包发送后即被遗忘。这提供了最大带宽,但可能丢失数据包。指示需要在协议级别(L2CAP)进行确认 — 更可靠但更慢。CBCharacteristic通过properties属性精确指示支持哪种模式:.notify、.indicate或两者都支持。
当CBPeripheral断开连接(disconnect、离开范围)时,所有活动订阅将自动重置。重新连接时,需要为每个特征重新调用setNotifyValue:true。当应用程序离开前台时(如果未启用后台模式),iOS也会丢失订阅 — 对于后台工作,需要在Info.plist中启用“Uses Bluetooth LE accessories”功能。
// 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类,该类使用现代async/await API(iOS 15+)在Swift中管理BLE外设的完整生命周期。
import CoreBluetooth
// 使用async/await的完整CBPeripheral管理示例
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) {
// 处理蓝牙设备状态
}
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类通过CheckedContinuation使用Swift Concurrency(async/await)— 这是与Core Bluetooth委托API一起使用的现代模式。connect(to:)通过didConnectPeripheral等待连接确认,discoverServices()通过didDiscoverServices等待。这种方法消除了嵌套委托,使BLE代码线性且可读。通过BLEError进行的错误处理涵盖了BLE连接的所有典型故障场景。
常见问题
CBPeripheral用于之前连接的设备可以通过retrievePeripheralsWithIdentifiers:在CBCentralManager上获取。传递之前保存的设备的UUID(NSUUID)数组 — 框架将返回系统BLE-bonding数据库中的设备的CBPeripheral数组。这仅适用于iPhone之前配对过的设备。对于新设备,扫描是必需的。
主要原因:设备在范围外(RSSI低于阈值)、BLE无线电关闭(CBCentralManager.state != .poweredOn)、CBPeripheralDelegate委托未设置(peripheral.delegate = self)或在连接之前调用了discoverServices。检查centralManager.state状态,确保委托在调用connect之前已设置,并使用5-10秒超时的重试。
原因 — 在仅支持.writeWithoutResponse的特征上使用.withResponse,反之亦然。在调用之前检查characteristic.properties。也可能是MTU问题:如果数据>20字节(BLE 4.0 MTU),则需要通过negotiateMTU协商MTU或进行分段。使用peripheral.maximumWriteValueLength(for: .withResponse)确定最大数据包大小。
范围外的CBPeripheral不会立即断开连接 — iOS通过超时(通常20-30秒)将其切换到.disconnected状态。对于监控,在CBPeripheral上使用readRSSI — 当不可访问时将返回带有CBError.connectionTimeout代码的错误。同时监控centralManager:didDisconnectPeripheral:error:以及时检测连接断开。
Core Bluetooth不是线程安全的 — 所有CBPeripheral调用必须从一个队列执行(通常是main queue或在CBCentralManager初始化时指定的串行serial queue)。来自不同线程的并发调用会导致竞争条件和应用程序崩溃。对所有BLE操作使用DispatchQueue(label: “com.app.ble”),对UI更新使用DispatchQueue.main.async。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。