You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Objective-C/Swift实现iOS后台BLE外设指令交互功能

iOS后台持续BLE交互原生实现方案

一、先搞定后台权限配置

这是基础中的基础,没配置的话后台直接歇菜:

  1. 打开项目的Info.plist,添加UIBackgroundModes数组,加入bluetooth-central(如果你是作为中心设备连接外设);如果APP还要作为外设被连接,再加bluetooth-peripheral。
  2. 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

四、关键注意点

  1. 后台唤醒逻辑:iOS不会让APP一直后台运行,只有当BLE有事件(外设断开、特征值更新、连接成功)时,系统才会给APP一段活跃时间(通常10秒左右)处理。如果需要持续交互,必须让外设定期发送特征更新来触发系统唤醒。
  2. 恢复标识必设:CBCentralManagerOptionRestoreIdentifierKey是后台恢复的核心,没有这个,APP被系统内存回收后,无法恢复之前的BLE连接状态。
  3. 后台任务慎用:beginBackgroundTask只能延长有限的活跃时间(最多几分钟),不要用来做无限循环,否则会被系统强制终止。
  4. 测试注意事项:测试后台模式时,不要用Xcode直接运行(Xcode会保持APP活跃),应该把APP安装到真机,断开Xcode连接后测试屏幕关闭/后台状态。

常见问题排查

  • 后台停止响应:检查UIBackgroundModes是否添加bluetooth-central,是否设置了恢复标识,外设是否主动发送了特征更新。
  • 无法恢复连接:确认willRestoreState代理里重新绑定了外设代理并调用了discoverServices。
  • 权限报错:检查NSBluetoothAlwaysUsageDescription是否添加,用户是否授权蓝牙访问。

内容的提问来源于stack exchange,提问作者Ludeesha Nanayakkara

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 19:34:55