如何用Objective-C/Swift实现iOS后台BLE外设指令交互功能
iOS后台持续BLE交互原生实现方案
一、先搞定后台权限配置
这是基础中的基础,没配置的话后台直接歇菜:
- 打开项目的
Info.plist,添加UIBackgroundModes数组,加入bluetooth-central(如果你是作为中心设备连接外设);如果APP还要作为外设被连接,再加bluetooth-peripheral。 - iOS 13及以上必须添加
NSBluetoothAlwaysUsageDescription字段,填写蓝牙使用原因(比如"需要与BLE外设实时交互"),否则会被系统拒绝蓝牙访问。
二、Swift核心实现代码
下面是完整的BLE管理器示例,包含后台恢复、连接、数据交互逻辑:
import CoreBluetooth import UIKit class BLECentralManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { private var centralManager: CBCentralManager! private var targetPeripheral: CBPeripheral? private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid override init() { super.init() // 必须设置恢复标识,否则APP被系统回收后无法恢复BLE连接 let initOptions: [String: Any] = [ CBCentralManagerOptionRestoreIdentifierKey: "com.yourapp.ble.restore", CBCentralManagerOptionShowPowerAlertKey: true ] // 用主队列处理代理回调,避免线程问题 centralManager = CBCentralManager(delegate: self, queue: DispatchQueue.main, options: initOptions) } // MARK: - CBCentralManagerDelegate // 系统恢复APP时的回调,重新绑定外设和代理 func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) { guard let restoredPeripherals = dict[CBCentralManagerRestoredStatePeripheralsKey] as? [CBPeripheral] else { return } for peripheral in restoredPeripherals { if peripheral.identifier == targetPeripheral?.identifier { targetPeripheral = peripheral peripheral.delegate = self peripheral.discoverServices(nil) } } } // BLE状态更新,只有poweredOn时才能操作 func centralManagerDidUpdateState(_ central: CBCentralManager) { switch central.state { case .poweredOn: // 扫描指定服务UUID的外设,减少无效扫描 central.scanForPeripherals(withServices: [CBUUID(string: "YOUR_PERIPHERAL_SERVICE_UUID")], options: [CBCentralManagerScanOptionAllowDuplicatesKey: false]) case .poweredOff, .unauthorized, .unknown, .unsupported: print("BLE不可用: \(central.state.rawValue)") default: break } } // 发现外设后连接 func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { // 匹配目标外设(可以用名称或UUID) if peripheral.name == "YOUR_PERIPHERAL_NAME" { central.stopScan() targetPeripheral = peripheral peripheral.delegate = self // 连接时开启断开通知,后台能收到断开事件 central.connect(peripheral, options: [CBConnectPeripheralOptionNotifyOnDisconnectionKey: true]) } } // 连接成功后发现服务 func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { peripheral.discoverServices([CBUUID(string: "YOUR_SERVICE_UUID")]) } // MARK: - CBPeripheralDelegate // 发现服务后发现特征 func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { guard let services = peripheral.services else { return } for service in services { peripheral.discoverCharacteristics([CBUUID(string: "YOUR_CHARACTERISTIC_UUID")], for: service) } } // 发现特征后配置通知/读写 func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { guard let characteristics = service.characteristics else { return } for characteristic in characteristics { // 开启特征通知,外设数据更新时会触发didUpdateValueFor回调(后台也会触发) if characteristic.properties.contains(.notify) { peripheral.setNotifyValue(true, for: characteristic) } // 写入数据示例 if characteristic.properties.contains(.write) { let data = "Hello BLE".data(using: .utf8)! peripheral.writeValue(data, for: characteristic, type: .withResponse) } } } // 收到外设数据更新(后台活跃时会触发) func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { guard let data = characteristic.value, let content = String(data: data, encoding: .utf8) else { return } print("收到外设数据: \(content)") // 开启后台任务延长处理时间(最多几分钟,不要滥用) startBackgroundTask() // 这里写你的后台处理逻辑,比如解析数据、发送本地通知等 DispatchQueue.global().asyncAfter(deadline: .now() + 5) { [weak self] in self?.endBackgroundTask() } } // MARK: - 后台任务管理 private func startBackgroundTask() { backgroundTaskID = UIApplication.shared.beginBackgroundTask(withName: "BLEDataProcessing") { [weak self] in self?.endBackgroundTask() } } private func endBackgroundTask() { if backgroundTaskID != .invalid { UIApplication.shared.endBackgroundTask(backgroundTaskID) backgroundTaskID = .invalid } } }
三、Objective-C核心片段
如果用OC开发,核心逻辑和Swift一致,关键代码如下:
#import <CoreBluetooth/CoreBluetooth.h> #import <UIKit/UIKit.h> @interface BLECentralManager : NSObject <CBCentralManagerDelegate, CBPeripheralDelegate> @property (nonatomic, strong) CBCentralManager *centralManager; @property (nonatomic, strong) CBPeripheral *targetPeripheral; @property (nonatomic, assign) UIBackgroundTaskIdentifier backgroundTaskID; @end @implementation BLECentralManager - (instancetype)init { self = [super init]; if (self) { NSDictionary *initOptions = @{ CBCentralManagerOptionRestoreIdentifierKey: @"com.yourapp.ble.restore", CBCentralManagerOptionShowPowerAlertKey: @YES }; self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue() options:initOptions]; self.backgroundTaskID = UIBackgroundTaskInvalid; } return self; } - (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary<NSString *,id> *)dict { NSArray *restoredPeripherals = dict[CBCentralManagerRestoredStatePeripheralsKey]; for (CBPeripheral *peripheral in restoredPeripherals) { if ([peripheral.identifier isEqual:self.targetPeripheral.identifier]) { self.targetPeripheral = peripheral; peripheral.delegate = self; [peripheral discoverServices:nil]; } } } - (void)centralManagerDidUpdateState:(CBCentralManager *)central { switch (central.state) { case CBCentralManagerStatePoweredOn: [central scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:@"YOUR_SERVICE_UUID"]] options:@{CBCentralManagerScanOptionAllowDuplicatesKey: @NO}]; break; default: NSLog(@"BLE不可用: %ld", (long)central.state); break; } } // 其他代理方法(连接、发现服务特征、数据更新)和Swift逻辑一致,此处省略 @end
四、关键注意点
- 后台唤醒逻辑:iOS不会让APP一直后台运行,只有当BLE有事件(外设断开、特征值更新、连接成功)时,系统才会给APP一段活跃时间(通常10秒左右)处理。如果需要持续交互,必须让外设定期发送特征更新来触发系统唤醒。
- 恢复标识必设:
CBCentralManagerOptionRestoreIdentifierKey是后台恢复的核心,没有这个,APP被系统内存回收后,无法恢复之前的BLE连接状态。 - 后台任务慎用:
beginBackgroundTask只能延长有限的活跃时间(最多几分钟),不要用来做无限循环,否则会被系统强制终止。 - 测试注意事项:测试后台模式时,不要用Xcode直接运行(Xcode会保持APP活跃),应该把APP安装到真机,断开Xcode连接后测试屏幕关闭/后台状态。
常见问题排查
- 后台停止响应:检查
UIBackgroundModes是否添加bluetooth-central,是否设置了恢复标识,外设是否主动发送了特征更新。 - 无法恢复连接:确认
willRestoreState代理里重新绑定了外设代理并调用了discoverServices。 - 权限报错:检查
NSBluetoothAlwaysUsageDescription是否添加,用户是否授权蓝牙访问。
内容的提问来源于stack exchange,提问作者Ludeesha Nanayakkara
相关产品推荐
相关产品推荐

