core-bluetooth
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCore Bluetooth
Core Bluetooth
Scan for, connect to, and exchange data with Bluetooth Low Energy (BLE) devices.
Covers the central role (scanning and connecting to peripherals), the peripheral
role (advertising services), background modes, and state restoration.
Targets Swift 6.2 / iOS 26+.
扫描、连接低功耗蓝牙(BLE)设备并与之交换数据。涵盖中心角色(扫描和连接外设)、外设角色(广播服务)、后台模式以及状态恢复功能。适配Swift 6.2 / iOS 26+版本。
Contents
目录
Setup
配置
Info.plist Keys
Info.plist 键值
| Key | Purpose |
|---|---|
| Required. Explains why the app uses Bluetooth |
| Background scanning and connecting |
| Background advertising |
| 键 | 用途 |
|---|---|
| 必填项,说明应用使用蓝牙的原因 |
包含 | 后台扫描与连接 |
包含 | 后台广播 |
Bluetooth Authorization
蓝牙授权
iOS prompts for Bluetooth permission automatically when you create a
or . The usage description from
is shown in the permission dialog.
CBCentralManagerCBPeripheralManagerNSBluetoothAlwaysUsageDescription当你创建或时,iOS会自动请求蓝牙权限。权限弹窗中会显示设置的使用说明。
CBCentralManagerCBPeripheralManagerNSBluetoothAlwaysUsageDescriptionCentral Role: Scanning
中心角色:扫描
Creating the Central Manager
创建中心管理器
Always wait for the state before scanning.
poweredOnswift
import CoreBluetooth
final class BluetoothManager: NSObject, CBCentralManagerDelegate {
private var centralManager: CBCentralManager!
private var discoveredPeripheral: CBPeripheral?
override init() {
super.init()
centralManager = CBCentralManager(delegate: self, queue: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
startScanning()
case .poweredOff:
// Bluetooth is off -- prompt user to enable
break
case .unauthorized:
// App not authorized for Bluetooth
break
case .unsupported:
// Device does not support BLE
break
case .resetting, .unknown:
break
@unknown default:
break
}
}
}始终等待状态后再开始扫描。
poweredOnswift
import CoreBluetooth
final class BluetoothManager: NSObject, CBCentralManagerDelegate {
private var centralManager: CBCentralManager!
private var discoveredPeripheral: CBPeripheral?
override init() {
super.init()
centralManager = CBCentralManager(delegate: self, queue: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
startScanning()
case .poweredOff:
// Bluetooth is off -- prompt user to enable
break
case .unauthorized:
// App not authorized for Bluetooth
break
case .unsupported:
// Device does not support BLE
break
case .resetting, .unknown:
break
@unknown default:
break
}
}
}Scanning for Peripherals
扫描外设
Scan for specific service UUIDs to save power. Pass to discover all
peripherals (not recommended in production).
nilswift
let heartRateServiceUUID = CBUUID(string: "180D")
func startScanning() {
centralManager.scanForPeripherals(
withServices: [heartRateServiceUUID],
options: [CBCentralManagerScanOptionAllowDuplicatesKey: false]
)
}
func centralManager(
_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral,
advertisementData: [String: Any],
rssi RSSI: NSNumber
) {
guard RSSI.intValue > -70 else { return } // Filter weak signals
// IMPORTANT: Retain the peripheral -- it will be deallocated otherwise
discoveredPeripheral = peripheral
centralManager.stopScan()
centralManager.connect(peripheral, options: nil)
}扫描特定服务UUID以节省电量。传入会发现所有外设(生产环境不推荐)。
nilswift
let heartRateServiceUUID = CBUUID(string: "180D")
func startScanning() {
centralManager.scanForPeripherals(
withServices: [heartRateServiceUUID],
options: [CBCentralManagerScanOptionAllowDuplicatesKey: false]
)
}
func centralManager(
_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral,
advertisementData: [String: Any],
rssi RSSI: NSNumber
) {
guard RSSI.intValue > -70 else { return } // Filter weak signals
// IMPORTANT: Retain the peripheral -- it will be deallocated otherwise
discoveredPeripheral = peripheral
centralManager.stopScan()
centralManager.connect(peripheral, options: nil)
}Central Role: Connecting
中心角色:连接
swift
func centralManager(
_ central: CBCentralManager,
didConnect peripheral: CBPeripheral
) {
peripheral.delegate = self
peripheral.discoverServices([heartRateServiceUUID])
}
func centralManager(
_ central: CBCentralManager,
didFailToConnect peripheral: CBPeripheral,
error: Error?
) {
// Handle connection failure -- retry or inform user
discoveredPeripheral = nil
}
func centralManager(
_ central: CBCentralManager,
didDisconnectPeripheral peripheral: CBPeripheral,
timestamp: CFAbsoluteTime,
isReconnecting: Bool,
error: Error?
) {
if isReconnecting {
// System is automatically reconnecting
return
}
// Handle disconnection -- optionally reconnect
discoveredPeripheral = nil
}swift
func centralManager(
_ central: CBCentralManager,
didConnect peripheral: CBPeripheral
) {
peripheral.delegate = self
peripheral.discoverServices([heartRateServiceUUID])
}
func centralManager(
_ central: CBCentralManager,
didFailToConnect peripheral: CBPeripheral,
error: Error?
) {
// Handle connection failure -- retry or inform user
discoveredPeripheral = nil
}
func centralManager(
_ central: CBCentralManager,
didDisconnectPeripheral peripheral: CBPeripheral,
timestamp: CFAbsoluteTime,
isReconnecting: Bool,
error: Error?
) {
if isReconnecting {
// System is automatically reconnecting
return
}
// Handle disconnection -- optionally reconnect
discoveredPeripheral = nil
}Discovering Services and Characteristics
发现服务与特征
Implement to walk the service/characteristic tree.
CBPeripheralDelegateswift
extension BluetoothManager: 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(.notify) {
peripheral.setNotifyValue(true, for: characteristic)
}
if characteristic.properties.contains(.read) {
peripheral.readValue(for: characteristic)
}
}
}
}实现来遍历服务/特征树。
CBPeripheralDelegateswift
extension BluetoothManager: 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(.notify) {
peripheral.setNotifyValue(true, for: characteristic)
}
if characteristic.properties.contains(.read) {
peripheral.readValue(for: characteristic)
}
}
}
}Common Service and Characteristic UUIDs
常见服务与特征UUID
| Service | UUID | Characteristics |
|---|---|---|
| Heart Rate | | Heart Rate Measurement ( |
| Battery | | Battery Level ( |
| Device Information | | Manufacturer Name ( |
| Generic Access | | Device Name ( |
swift
let heartRateMeasurementUUID = CBUUID(string: "2A37")
let batteryLevelUUID = CBUUID(string: "2A19")| 服务 | UUID | 特征 |
|---|---|---|
| 心率 | | 心率测量( |
| 电池 | | 电池电量( |
| 设备信息 | | 制造商名称( |
| 通用访问 | | 设备名称( |
swift
let heartRateMeasurementUUID = CBUUID(string: "2A37")
let batteryLevelUUID = CBUUID(string: "2A19")Reading, Writing, and Notifications
读取、写入与通知
Reading a Value
读取值
swift
func peripheral(
_ peripheral: CBPeripheral,
didUpdateValueFor characteristic: CBCharacteristic,
error: Error?
) {
guard let data = characteristic.value else { return }
switch characteristic.uuid {
case CBUUID(string: "2A37"):
let heartRate = parseHeartRate(data)
print("Heart rate: \(heartRate) bpm")
case CBUUID(string: "2A19"):
let batteryLevel = data.first.map { Int($0) } ?? 0
print("Battery: \(batteryLevel)%")
default:
break
}
}
private func parseHeartRate(_ data: Data) -> Int {
let flags = data[0]
let is16Bit = (flags & 0x01) != 0
if is16Bit {
return Int(data[1]) | (Int(data[2]) << 8)
} else {
return Int(data[1])
}
}swift
func peripheral(
_ peripheral: CBPeripheral,
didUpdateValueFor characteristic: CBCharacteristic,
error: Error?
) {
guard let data = characteristic.value else { return }
switch characteristic.uuid {
case CBUUID(string: "2A37"):
let heartRate = parseHeartRate(data)
print("Heart rate: \(heartRate) bpm")
case CBUUID(string: "2A19"):
let batteryLevel = data.first.map { Int($0) } ?? 0
print("Battery: \(batteryLevel)%")
default:
break
}
}
private func parseHeartRate(_ data: Data) -> Int {
let flags = data[0]
let is16Bit = (flags & 0x01) != 0
if is16Bit {
return Int(data[1]) | (Int(data[2]) << 8)
} else {
return Int(data[1])
}
}Writing a Value
写入值
swift
func writeValue(_ data: Data, to characteristic: CBCharacteristic,
on peripheral: CBPeripheral) {
if characteristic.properties.contains(.writeWithoutResponse) {
peripheral.writeValue(data, for: characteristic, type: .withoutResponse)
} else if characteristic.properties.contains(.write) {
peripheral.writeValue(data, for: characteristic, type: .withResponse)
}
}
// Confirmation callback for .withResponse writes
func peripheral(
_ peripheral: CBPeripheral,
didWriteValueFor characteristic: CBCharacteristic,
error: Error?
) {
if let error {
print("Write failed: \(error.localizedDescription)")
}
}swift
func writeValue(_ data: Data, to characteristic: CBCharacteristic,
on peripheral: CBPeripheral) {
if characteristic.properties.contains(.writeWithoutResponse) {
peripheral.writeValue(data, for: characteristic, type: .withoutResponse)
} else if characteristic.properties.contains(.write) {
peripheral.writeValue(data, for: characteristic, type: .withResponse)
}
}
// Confirmation callback for .withResponse writes
func peripheral(
_ peripheral: CBPeripheral,
didWriteValueFor characteristic: CBCharacteristic,
error: Error?
) {
if let error {
print("Write failed: \(error.localizedDescription)")
}
}Subscribing to Notifications
订阅通知
swift
// Subscribe
peripheral.setNotifyValue(true, for: characteristic)
// Unsubscribe
peripheral.setNotifyValue(false, for: characteristic)
// Confirmation
func peripheral(
_ peripheral: CBPeripheral,
didUpdateNotificationStateFor characteristic: CBCharacteristic,
error: Error?
) {
if characteristic.isNotifying {
print("Now receiving notifications for \(characteristic.uuid)")
}
}swift
// Subscribe
peripheral.setNotifyValue(true, for: characteristic)
// Unsubscribe
peripheral.setNotifyValue(false, for: characteristic)
// Confirmation
func peripheral(
_ peripheral: CBPeripheral,
didUpdateNotificationStateFor characteristic: CBCharacteristic,
error: Error?
) {
if characteristic.isNotifying {
print("Now receiving notifications for \(characteristic.uuid)")
}
}Peripheral Role: Advertising
外设角色:广播
Publish services from the local device using .
CBPeripheralManagerswift
final class BLEPeripheralManager: NSObject, CBPeripheralManagerDelegate {
private var peripheralManager: CBPeripheralManager!
private let serviceUUID = CBUUID(string: "12345678-1234-1234-1234-123456789ABC")
private let charUUID = CBUUID(string: "12345678-1234-1234-1234-123456789ABD")
override init() {
super.init()
peripheralManager = CBPeripheralManager(delegate: self, queue: nil)
}
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
guard peripheral.state == .poweredOn else { return }
setupService()
}
private func setupService() {
let characteristic = CBMutableCharacteristic(
type: charUUID,
properties: [.read, .notify],
value: nil,
permissions: [.readable]
)
let service = CBMutableService(type: serviceUUID, primary: true)
service.characteristics = [characteristic]
peripheralManager.add(service)
}
func peripheralManager(
_ peripheral: CBPeripheralManager,
didAdd service: CBService,
error: Error?
) {
guard error == nil else { return }
peripheralManager.startAdvertising([
CBAdvertisementDataServiceUUIDsKey: [serviceUUID],
CBAdvertisementDataLocalNameKey: "MyDevice"
])
}
}使用从本地设备发布服务。
CBPeripheralManagerswift
final class BLEPeripheralManager: NSObject, CBPeripheralManagerDelegate {
private var peripheralManager: CBPeripheralManager!
private let serviceUUID = CBUUID(string: "12345678-1234-1234-1234-123456789ABC")
private let charUUID = CBUUID(string: "12345678-1234-1234-1234-123456789ABD")
override init() {
super.init()
peripheralManager = CBPeripheralManager(delegate: self, queue: nil)
}
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
guard peripheral.state == .poweredOn else { return }
setupService()
}
private func setupService() {
let characteristic = CBMutableCharacteristic(
type: charUUID,
properties: [.read, .notify],
value: nil,
permissions: [.readable]
)
let service = CBMutableService(type: serviceUUID, primary: true)
service.characteristics = [characteristic]
peripheralManager.add(service)
}
func peripheralManager(
_ peripheral: CBPeripheralManager,
didAdd service: CBService,
error: Error?
) {
guard error == nil else { return }
peripheralManager.startAdvertising([
CBAdvertisementDataServiceUUIDsKey: [serviceUUID],
CBAdvertisementDataLocalNameKey: "MyDevice"
])
}
}Background BLE
BLE后台模式
Background Central Mode
后台中心模式
Add to . In the background:
bluetooth-centralUIBackgroundModes- Scanning continues but only for specific service UUIDs
- is ignored (always
CBCentralManagerScanOptionAllowDuplicatesKey)false - Discovery callbacks are coalesced and delivered in batches
在中添加。后台状态下:
UIBackgroundModesbluetooth-central- 扫描继续执行,但仅针对特定服务UUID
- 会被忽略(始终为
CBCentralManagerScanOptionAllowDuplicatesKey)false - 发现回调会被合并并批量交付
Background Peripheral Mode
后台外设模式
Add to . In the background:
bluetooth-peripheralUIBackgroundModes- Advertising continues but data is reduced to service UUIDs only
- The local name is not included in background advertisements
在中添加。后台状态下:
UIBackgroundModesbluetooth-peripheral- 广播继续执行,但数据仅保留服务UUID
- 本地名称不会包含在后台广播中
State Restoration
状态恢复
State restoration allows the system to re-create your central or peripheral
manager after your app is terminated and relaunched for a BLE event.
状态恢复允许系统在应用因BLE事件被终止并重新启动后,重新创建你的中心或外设管理器。
Central Manager State Restoration
中心管理器状态恢复
swift
// 1. Create with a restoration identifier
centralManager = CBCentralManager(
delegate: self,
queue: nil,
options: [CBCentralManagerOptionRestoreIdentifierKey: "myCentral"]
)
// 2. Implement the restoration delegate method
func centralManager(
_ central: CBCentralManager,
willRestoreState dict: [String: Any]
) {
if let peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey]
as? [CBPeripheral] {
for peripheral in peripherals {
// Re-assign delegate and retain
peripheral.delegate = self
discoveredPeripheral = peripheral
}
}
}swift
// 1. 使用恢复标识符创建
centralManager = CBCentralManager(
delegate: self,
queue: nil,
options: [CBCentralManagerOptionRestoreIdentifierKey: "myCentral"]
)
// 2. 实现恢复代理方法
func centralManager(
_ central: CBCentralManager,
willRestoreState dict: [String: Any]
) {
if let peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey]
as? [CBPeripheral] {
for peripheral in peripherals {
// Re-assign delegate and retain
peripheral.delegate = self
discoveredPeripheral = peripheral
}
}
}Peripheral Manager State Restoration
外设管理器状态恢复
swift
peripheralManager = CBPeripheralManager(
delegate: self,
queue: nil,
options: [CBPeripheralManagerOptionRestoreIdentifierKey: "myPeripheral"]
)
func peripheralManager(
_ peripheral: CBPeripheralManager,
willRestoreState dict: [String: Any]
) {
// Restore published services, advertising state, etc.
}swift
peripheralManager = CBPeripheralManager(
delegate: self,
queue: nil,
options: [CBPeripheralManagerOptionRestoreIdentifierKey: "myPeripheral"]
)
func peripheralManager(
_ peripheral: CBPeripheralManager,
willRestoreState dict: [String: Any]
) {
// Restore published services, advertising state, etc.
}Common Mistakes
常见错误
DON'T: Scan or connect before poweredOn
请勿:在poweredOn前扫描或连接
swift
// WRONG: Scanning immediately -- manager may not be ready
let manager = CBCentralManager(delegate: self, queue: nil)
manager.scanForPeripherals(withServices: nil) // May silently fail
// CORRECT: Wait for poweredOn in the delegate
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
central.scanForPeripherals(withServices: [serviceUUID])
}
}swift
// WRONG: Scanning immediately -- manager may not be ready
let manager = CBCentralManager(delegate: self, queue: nil)
manager.scanForPeripherals(withServices: nil) // May silently fail
// CORRECT: Wait for poweredOn in the delegate
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
central.scanForPeripherals(withServices: [serviceUUID])
}
}DON'T: Lose the peripheral reference
请勿:丢失外设引用
Core Bluetooth does not retain discovered peripherals. If you don't hold a
strong reference, the peripheral is deallocated and the connection fails silently.
swift
// WRONG: No strong reference kept
func centralManager(_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral, ...) {
central.connect(peripheral) // peripheral may be deallocated
}
// CORRECT: Retain the peripheral
func centralManager(_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral, ...) {
self.discoveredPeripheral = peripheral // Strong reference
central.connect(peripheral)
}Core Bluetooth不会保留已发现的外设。如果不持有强引用,外设会被释放,连接将静默失败。
swift
// WRONG: No strong reference kept
func centralManager(_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral, ...) {
central.connect(peripheral) // peripheral may be deallocated
}
// CORRECT: Retain the peripheral
func centralManager(_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral, ...) {
self.discoveredPeripheral = peripheral // Strong reference
central.connect(peripheral)
}DON'T: Scan for nil services in production
请勿:生产环境中扫描nil服务
swift
// WRONG: Discovers every BLE device in range -- drains battery
centralManager.scanForPeripherals(withServices: nil)
// CORRECT: Specify the service UUIDs you need
centralManager.scanForPeripherals(withServices: [targetServiceUUID])swift
// WRONG: Discovers every BLE device in range -- drains battery
centralManager.scanForPeripherals(withServices: nil)
// CORRECT: Specify the service UUIDs you need
centralManager.scanForPeripherals(withServices: [targetServiceUUID])DON'T: Assume connection order or timing
请勿:假设连接顺序或时机
swift
// WRONG: Assuming immediate connection
centralManager.connect(peripheral)
discoverServicesNow() // Peripheral not connected yet
// CORRECT: Discover services in the didConnect callback
func centralManager(_ central: CBCentralManager,
didConnect peripheral: CBPeripheral) {
peripheral.delegate = self
peripheral.discoverServices([serviceUUID])
}swift
// WRONG: Assuming immediate connection
centralManager.connect(peripheral)
discoverServicesNow() // Peripheral not connected yet
// CORRECT: Discover services in the didConnect callback
func centralManager(_ central: CBCentralManager,
didConnect peripheral: CBPeripheral) {
peripheral.delegate = self
peripheral.discoverServices([serviceUUID])
}DON'T: Write to a characteristic without checking properties
请勿:未检查属性就写入特征
swift
// WRONG: Crashes or silently fails if write is unsupported
peripheral.writeValue(data, for: characteristic, type: .withResponse)
// CORRECT: Check properties first
if characteristic.properties.contains(.write) {
peripheral.writeValue(data, for: characteristic, type: .withResponse)
} else if characteristic.properties.contains(.writeWithoutResponse) {
peripheral.writeValue(data, for: characteristic, type: .withoutResponse)
}swift
// WRONG: Crashes or silently fails if write is unsupported
peripheral.writeValue(data, for: characteristic, type: .withResponse)
// CORRECT: Check properties first
if characteristic.properties.contains(.write) {
peripheral.writeValue(data, for: characteristic, type: .withResponse)
} else if characteristic.properties.contains(.writeWithoutResponse) {
peripheral.writeValue(data, for: characteristic, type: .withoutResponse)
}Review Checklist
检查清单
- added to Info.plist
NSBluetoothAlwaysUsageDescription - All BLE operations gated on returning
centralManagerDidUpdateState.poweredOn - Discovered peripherals retained with a strong reference
- Scanning uses specific service UUIDs (not ) in production
nil - set before calling
CBPeripheralDelegatediscoverServices - Characteristic properties checked before read/write/notify
- Background mode (or
bluetooth-central) added if neededbluetooth-peripheral - State restoration identifier set if app needs relaunch-on-BLE-event support
- delegate method implemented when using state restoration
willRestoreState - Scanning stopped after discovering the target peripheral
- Disconnection handled with optional automatic reconnect logic
- Write type matches characteristic properties (vs
.withResponse).withoutResponse
- 已在Info.plist中添加
NSBluetoothAlwaysUsageDescription - 所有BLE操作均在返回
centralManagerDidUpdateState后执行.poweredOn - 已通过强引用保留已发现的外设
- 生产环境中扫描使用特定服务UUID(而非)
nil - 在调用前已设置
discoverServicesCBPeripheralDelegate - 读取/写入/通知前已检查特征属性
- 按需添加了后台模式(或
bluetooth-central)bluetooth-peripheral - 若应用需要支持BLE事件重启,已设置状态恢复标识符
- 使用状态恢复时已实现代理方法
willRestoreState - 发现目标外设后已停止扫描
- 已处理断开连接逻辑,可选自动重连
- 写入类型与特征属性匹配(vs
.withResponse).withoutResponse
References
参考资料
- Extended patterns (reconnection strategies, data parsing, SwiftUI integration):
references/ble-patterns.md - Core Bluetooth framework
- CBCentralManager
- CBPeripheral
- CBPeripheralManager
- CBService
- CBCharacteristic
- CBUUID
- CBCentralManagerDelegate
- CBPeripheralDelegate
- 扩展模式(重连策略、数据解析、SwiftUI集成):
references/ble-patterns.md - Core Bluetooth框架
- CBCentralManager
- CBPeripheral
- CBPeripheralManager
- CBService
- CBCharacteristic
- CBUUID
- CBCentralManagerDelegate
- CBPeripheralDelegate