.h
#import <Foundation/Foundation.h>
#import <CoreBluetooth/CoreBluetooth.h>
NS_ASSUME_NONNULL_BEGIN
typedef void(^ScanDevicesBlock)(NSArray <CBPeripheral *> *devices);
typedef void(^ConnectResultBlock)(CBPeripheral * _Nullable peripheral);
typedef void(^ReceiveDataBlock)(NSDictionary *data);
typedef void(^ErrorMsgBlock)(NSString *errorMsg);
@interface BleManager : NSObject
/// 检索回调
@property (copy, nonatomic) ScanDevicesBlock scanDevicesBlock;
/// 接续回调
@property (copy, nonatomic) ConnectResultBlock connectResultBlock;
/// 接收回调
@property (copy, nonatomic) ReceiveDataBlock receiveDataBlock;
/// 错误回调
@property (copy, nonatomic) ErrorMsgBlock errorMsgBlock;
/// 最长接续时间,默认 5.0s
@property (assign, nonatomic) NSTimeInterval connectionDuration;
/// 初期化
+ (instancetype)sharedInstance;
- (instancetype)init __attribute__((unavailable("Use BleManager.sharedInstance")));
+ (instancetype)new __attribute__((unavailable("Use BleManager.sharedInstance")));
/// 检索设备
- (void)scanPeripherals;
/// 停止检索
- (void)stopScan;
/// 连接设备
/// @param peripheral 蓝牙设备
- (void)connectPeripheral:(CBPeripheral *)peripheral;
/// 断开连接
/// @param peripheral 蓝牙设备
- (void)cancelPeripheralConnection:(CBPeripheral *)peripheral;
/// 写入数据
/// @param data 待写入的数据
- (void)writeValue:(NSData *)data;
@end
NS_ASSUME_NONNULL_END
.m
#import "BleManager.h"
@interface BleManager() <CBCentralManagerDelegate,CBPeripheralDelegate>
@property (strong, nonatomic) CBCentralManager *centralManager; /// 中央管理者
@property (strong, nonatomic) CBPeripheral *connectingPeripheral; /// 处于连接状态的蓝牙设备
@property (strong, nonatomic) CBCharacteristic *writeValueCharacteristic; /// 可写入特征值
@property (strong, nonatomic) NSTimer *connectionTimer; /// 设备接续计时器
@property (strong, nonatomic) NSArray <CBUUID *> *targetPeripheralUUIDs; /// 筛选蓝牙设备的 UUID 集合
@property (strong, nonatomic) NSMutableArray <NSData *> *waitingToAppendDatas; /// 存储待拼接的数据
@property (strong, nonatomic) NSMutableArray <CBPeripheral *> *discoverdDevices; /// 存储已检索到的蓝牙设备
@end
@implementation BleManager
/// 初期化 BleManager 单例对象
static BleManager *manager = nil;
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[BleManager alloc] init];
[manager initObjects];
});
return manager;
}
/// 检索设备
- (void)scanPeripherals {
if (self.centralManager.state != CBManagerStatePoweredOn) {
[self errorMsgCallBack:@"蓝牙不可用"];
return;
}
[self.discoverdDevices removeAllObjects];
if (self.scanDevicesBlock) {
self.scanDevicesBlock(self.discoverdDevices);
}
[self.centralManager scanForPeripheralsWithServices:self.targetPeripheralUUIDs options:nil];
}
/// 停止检索
- (void)stopScan {
[self.centralManager stopScan];
}
/// 连接设备
/// @param peripheral 蓝牙设备
- (void)connectPeripheral:(CBPeripheral *)peripheral {
if (!peripheral) {
[self errorMsgCallBack:@"请检查与蓝牙设备的连接状态"];
return;
}
NSLog(@"BleManager --> 即将建立与 %@ 的连接", peripheral);
[self.centralManager connectPeripheral:peripheral options:nil];
/// 开启接续计时器
if (!self.connectionTimer) {
__weak typeof(self) weakSelf = self;
self.connectionTimer = [NSTimer scheduledTimerWithTimeInterval:self.connectionDuration repeats:NO block:^(NSTimer * _Nonnull timer) {
if (!weakSelf.connectingPeripheral) {
[weakSelf cancelPeripheralConnection:peripheral];
[weakSelf errorMsgCallBack:@"连接失败,请重试"];
}
[weakSelf invalidateConnectionTimer];
}];
[[NSRunLoop mainRunLoop] addTimer:self.connectionTimer forMode:NSRunLoopCommonModes];
}
}
/// 断开连接
/// @param peripheral 蓝牙设备
- (void)cancelPeripheralConnection:(CBPeripheral *)peripheral {
if (!peripheral) {
[self errorMsgCallBack:@"没有已连接的设备"];
return;
}
NSLog(@"BleManager --> 即将断开与 %@ 的连接", peripheral);
[self.centralManager cancelPeripheralConnection:peripheral];
}
/// 写入数据
/// @param data 待写入的数据
- (void)writeValue:(NSData *)data {
NSLog(@"BleManager --> 准备发送数据 %@", [self dataToDictionary:data]);
if (!data.length) {
NSLog(@"BleManager --> 数据发送失败,待发送数据不能为空");
[self errorMsgCallBack:@"待发送的数据为空"];
return;
}
if (!self.connectingPeripheral) {
NSLog(@"BleManager --> 数据发送失败,没有正在连接的设备");
[self errorMsgCallBack:@"没有已连接的设备"];
return;
}
if (!self.writeValueCharacteristic) {
NSLog(@"BleManager --> 数据发送失败,没有可写入的特征值");
[self errorMsgCallBack:@"无法向蓝牙设备写入数据"];
return;
}
[self.connectingPeripheral writeValue:data forCharacteristic:self.writeValueCharacteristic type:CBCharacteristicWriteWithResponse];
}
#pragma mark -
- (void)centralManagerDidUpdateState:(nonnull CBCentralManager *)central {
switch (central.state) {
case CBManagerStateResetting:
[self errorMsgCallBack:@"蓝牙重置中"];
[self stopScan];
break;
case CBManagerStateUnsupported:
[self errorMsgCallBack:@"设备不支持蓝牙"];
break;
case CBManagerStateUnauthorized:
NSLog(@"BleManager --> 未授权...");
[self errorMsgCallBack:@"请开启蓝牙权限"];
break;
case CBManagerStatePoweredOff:
NSLog(@"BleManager --> 蓝牙未开启...");
[self errorMsgCallBack:@"请开启蓝牙"];
break;
case CBManagerStatePoweredOn:
NSLog(@"BleManager --> 蓝牙已开启...");
break;
default:
// CBManagerStateUnknown
NSLog(@"BleManager --> 未知错误...");
[self errorMsgCallBack:@"未知错误,请重新开启蓝牙"];
break;
}
}
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI {
// 检索到设备
if (peripheral.name.length && ![self.discoverdDevices containsObject:peripheral]) {
[self.discoverdDevices addObject:peripheral];
if (self.scanDevicesBlock) {
self.scanDevicesBlock(self.discoverdDevices);
}
}
}
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(@"BleManager --> didConnectPeripheral -> %@", peripheral);
// 已连接
self.connectingPeripheral = peripheral;
// start to discover services
self.connectingPeripheral.delegate = self;
[self.connectingPeripheral discoverServices:nil];
}
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@"BleManager --> didFailToConnectPeripheral -> %@", peripheral);
// 连接失败
self.connectingPeripheral = nil;
self.writeValueCharacteristic = nil;
if (self.connectResultBlock) {
self.connectResultBlock(nil);
[self invalidateConnectionTimer];
}
}
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@"BleManager --> didDisconnectPeripheral -> %@", peripheral);
// 已断开连接
self.connectingPeripheral = nil;
self.writeValueCharacteristic = nil;
}
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
// 发现服务
for (CBService *service in peripheral.services) {
// start to discover characteristics
NSLog(@"BleManager --> didDiscoverServices -> %@", service);
[self.connectingPeripheral discoverCharacteristics:nil forService:service];
}
}
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
// 发现特征值
for (CBCharacteristic *characteristic in service.characteristics) {
NSLog(@"BleManager --> didDiscoverCharacteristicsForService -> %@", characteristic);
// 写数据特征值
if ([characteristic.UUID.UUIDString isEqualToString:@"***"]) {
self.writeValueCharacteristic = characteristic;
// 扫描到可写入的特征值后认为设备连接成功
if (self.connectResultBlock) {
[self invalidateConnectionTimer];
self.connectResultBlock(peripheral);
}
NSLog(@"BleManager --> 已发现可写入的特征值 %@", self.writeValueCharacteristic);
}
// 监听数据特征值
if ([characteristic.UUID.UUIDString isEqualToString:@"***"]) {
[self.connectingPeripheral setNotifyValue:YES forCharacteristic:characteristic];
[self.connectingPeripheral readValueForCharacteristic:characteristic];
}
}
}
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
// 向某一特征值写指令
NSLog(@"BleManager --> didWriteValueForCharacteristic -> %@ %@", characteristic, [self dataToDictionary:characteristic.value]);
if (error) {
[self errorMsgCallBack:@"数据写入失败"];
}
}
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if (!characteristic.value.length) { return; }
// 接收到某一特征值返回的数据
NSLog(@"BleManager --> didUpdateValueForCharacteristic -> %@ %@", characteristic, [self dataToDictionary:characteristic.value]);
// 数据校验失败
if (![self checkData:characteristic.value]) {
NSLog(@"BleManager --> 接收到的数据不完整,等待拼接");
[self.waitingToAppendDatas addObject:characteristic.value];
return;
}
NSMutableData *fullData = [[NSMutableData alloc] init];
if (self.waitingToAppendDatas.count) {
// 若存在不完整数据,则拼接数据
for (NSData *waitToAppendData in self.waitingToAppendDatas) {
[fullData appendData:waitToAppendData];
}
}
[fullData appendData:characteristic.value];
if (self.receiveDataBlock) {
// 传递完整数据后,清空待拼接数据
[self.waitingToAppendDatas removeAllObjects];
self.receiveDataBlock([self dataToDictionary:fullData]);
}
}
#pragma mark -
/// 验证是否接收到数据尾
/// @param data 待验证的数据
- (BOOL)checkData:(NSData *)data {
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"BleManager --> checkDataIntegrity %@", jsonString);
return [jsonString hasSuffix:@"}}"];
}
/// NSData 转 NSDictionary
/// @param data 待转换数据
- (NSDictionary *)dataToDictionary:(NSData *)data {
if (!data.length) { return nil; }
NSString *receiveString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"dataToDictionary --> %@", receiveString);
if (!receiveString) {
return nil;
}
NSData *encodingData = [receiveString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:encodingData options:NSJSONReadingMutableLeaves error:nil];
return dictionary;
}
#pragma mark -
/// 初期化
- (void)initObjects {
self.connectionDuration = 5.0;
self.discoverdDevices = [[NSMutableArray alloc] init];
self.waitingToAppendDatas = [[NSMutableArray alloc] init];
self.targetPeripheralUUIDs = @[[CBUUID UUIDWithString:@"***"], [CBUUID UUIDWithString:@"***"]];
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue() options:nil];
}
/// 设置最长接续时间
/// @param connectionDuration 最长接续时间
- (void)setConnectionDuration:(NSTimeInterval)connectionDuration {
_connectionDuration = connectionDuration;
}
/// 注销接续计时器
- (void)invalidateConnectionTimer {
[self.connectionTimer invalidate];
self.connectionTimer = nil;
}
/// 错误回调
/// @param error 错误信息
- (void)errorMsgCallBack:(NSString *)error {
if (!error.length) { return; }
if (self.errorMsgBlock) {
self.errorMsgBlock(error);
}
}
@end