iOS 蓝牙开发实现文件传输
Posted JackLee18
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS 蓝牙开发实现文件传输相关的知识,希望对你有一定的参考价值。
这是一篇旧文,三年前就写过了,一直没有时间分享出来,最近简单整理了下,希望能帮到有需要的人。
由于我这里没有相关的蓝牙设备,主要用了两个手机,一个作为主设备,一个做为从设备。另外进行蓝牙开发有一个调试利器。
主设备和从设备我分别创建了一个管理类。
主设备主要进行的操作如下:
- 开始扫描设备
- 停止扫描设备
- 连接设备
- 断开连接设备
- 发送数据
具体源码如下:
#import <Foundation/Foundation.h>
#import <CoreBluetooth/CoreBluetooth.h>
NS_ASSUME_NONNULL_BEGIN
@interface JKBlueToothCenterHelper : NSObject
/// 设备管理者状态block
@property (nonatomic, copy) void(^btStatusBlock)(NSInteger status,NSString *message);
/// 设备连接状态的block
@property (nonatomic, copy) void(^btConnectStatusBlock)(CBPeripheral *peripheral, NSError * _Nullable error);
/// 断开链接block
@property (nonatomic, copy) void(^btDisconnectStatusBlock)(CBPeripheral *peripheral, NSError * _Nullable error);
/// 扫描到的设备列表block
@property (nonatomic,copy) void(^btScanDevicesBlock)(NSMutableArray <CBPeripheral *>*devices);
/// 接收到数据的block
@property (nonatomic,copy) void(^receivedDataBlock)(NSData *data, NSError *error);
/**
开始扫描设备
@param services 扫描的服务
@param options 扫描的选项配置
@param containDefaultService 是否包含默认的服务
@return JKBlueToothCenterHelper 对象
*/
- (instancetype)initWithScanServices:(nullable NSArray<CBUUID *> *)services options:(nullable NSDictionary <NSString *, id> *)options containDefaultService:(BOOL)containDefaultService;
/**
扫描设备
*/
- (void)scanDevice;
/**
停止扫描设备
*/
- (void)stopScanDevice;
/**
连接设备
@param peripheral 设备
@param options 配置信息
*/
- (void)connectToDevice:(CBPeripheral *)peripheral options:(NSDictionary <NSString *, id> *)options;
/**
断开连接设备
@param peripheral 设备
*/
- (void)disconnectToDevice:(CBPeripheral *)peripheral;
/**
设备管理者向设备发送数据
@param data 二进制数据
@param peripheral 接受数据的设备
@param CBCharacteristic 特征
@param type 请求类型
*/
- (void)sendData:(NSData *)data toPeripheral:(CBPeripheral *)peripheral forCharacteristic:(CBCharacteristic *)CBCharacteristic type:(CBCharacteristicWriteType)type complete:(void(^)(NSError *error))completeBlock;
@end
NS_ASSUME_NONNULL_END
typedef void(^JKBTCenterSendCompleteBlock)(NSError *error);
@interface JKBlueToothCenterHelper()<CBCentralManagerDelegate,CBPeripheralDelegate>
@property (nonatomic, strong) CBCentralManager *centerManager; ///< 管理者
@property (nonatomic, strong) NSMutableArray *scanServices; ///< 扫描的服务
@property (nonatomic, strong) NSDictionary <NSString *, id> *scanOptions; ///< 扫描的配置
@property (nonatomic,strong) NSMutableArray *scannedDevices; ///< s扫描到的设备
@property (strong , nonatomic) CBPeripheral * discoveredPeripheral;//周边设备
@property (nonatomic,copy) JKBTCenterSendCompleteBlock sendCompleteBlock;
@end
@implementation JKBlueToothCenterHelper
- (instancetype)initWithScanServices:(nullable NSArray<CBUUID *> *)services options:(nullable NSDictionary <NSString *, id> *)options containDefaultService:(BOOL)containDefaultService
self = [super init];
if (self)
[self.scanServices addObjectsFromArray:services];
if (containDefaultService)
[self setupDefalutScanService];
self.scanOptions = options;
[self centerManager];
return self;
- (void)setupDefalutScanService
CBUUID *uuid = [CBUUID UUIDWithString:DEFAULT_SERVICE_UUID];
[self.scanServices addObject:uuid];
- (void)scanDevice
[self.scannedDevices removeAllObjects];
[self.centerManager scanForPeripheralsWithServices:self.scanServices options:self.scanOptions];
- (void)stopScanDevice
[self.centerManager stopScan];
- (void)connectToDevice:(CBPeripheral *)peripheral options:(NSDictionary <NSString *, id> *)options
self.discoveredPeripheral = peripheral;
self.discoveredPeripheral.delegate = self;
[self.centerManager connectPeripheral:peripheral options:options];
- (void)disconnectToDevice:(CBPeripheral *)peripheral
[self.centerManager cancelPeripheralConnection:peripheral];
- (void)sendData:(NSData *)data toPeripheral:(CBPeripheral *)peripheral forCharacteristic:(CBCharacteristic *)CBCharacteristic type:(CBCharacteristicWriteType)type complete:(void(^)(NSError *error))completeBlock
self.sendCompleteBlock = completeBlock;
[peripheral writeValue:data forCharacteristic:CBCharacteristic type:type];
#pragma mark - - - - CBCentralManagerDelegate - - - -
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
if (@available(ios 10.0, *))
switch (central.state)
case CBManagerStatePoweredOn://蓝牙打开
[self.centerManager scanForPeripheralsWithServices:self.scanServices options:self.scanOptions];
break;
case CBManagerStatePoweredOff://蓝牙关闭了
if (self.btStatusBlock)
self.btStatusBlock(CBManagerStatePoweredOff, @"蓝牙已关闭,请打开蓝牙");
break;
case CBManagerStateUnsupported://不支持
if (self.btStatusBlock)
self.btStatusBlock(CBManagerStatePoweredOff, @"蓝牙设备不支持!");
break;
default:
break;
else
// Fallback on earlier versions
switch (central.state)
case 5://蓝牙打开
[self.centerManager scanForPeripheralsWithServices:self.scanServices options:self.scanOptions];
break;
case 4://蓝牙关闭了
if (self.btStatusBlock)
self.btStatusBlock(4, @"蓝牙已关闭,请打开蓝牙");
break;
case 2://不支持
if (self.btStatusBlock)
self.btStatusBlock(2, @"蓝牙设备不支持!");
break;
default:
break;
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI
if (!peripheral)
return;
[peripheral discoverServices:self.scanServices];
if (![self.scannedDevices containsObject:peripheral])
[self.scannedDevices addObject:peripheral];
if (self.btScanDevicesBlock)
self.btScanDevicesBlock(self.scannedDevices);
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(nonnull CBPeripheral *)peripheral error:(nullable NSError *)error
if (self.btConnectStatusBlock)
self.btConnectStatusBlock(peripheral, error);
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
if (@available(iOS 9.0, *))
if (self.centerManager.isScanning)
[self stopScanDevice];
else
// Fallback on earlier versions
[self stopScanDevice];
self.discoveredPeripheral = peripheral;
[peripheral setDelegate:self];
[peripheral discoverServices:nil];
if (self.btConnectStatusBlock)
self.btConnectStatusBlock(peripheral, nil);
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(nonnull CBPeripheral *)peripheral error:(nullable NSError *)error
if (self.btDisconnectStatusBlock)
self.btDisconnectStatusBlock(peripheral, error);
//- (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary<NSString *, id> *)dict
//
//
#pragma mark - - - - CBPeripheralDelegate - - - -
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error
peripheral.delegate = self;
NSArray *services = peripheral.services;
for (CBService *service in services)
[peripheral discoverCharacteristics:nil forService:service];
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error
for (CBCharacteristic *characteristic in service.characteristics)
if ([[NSString stringWithFormat:@"%@",characteristic.UUID] isEqualToString: DEFAULT_CHARACTERISTIC_UUID])
[self.discoveredPeripheral setNotifyValue:YES forCharacteristic:characteristic];
// 写入成功
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error
if (self.sendCompleteBlock)
self.sendCompleteBlock(error);
-(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
if (error)
//NSLog(@"订阅失败");
//NSLog(@"%@",error);
if (characteristic.isNotifying)
//NSLog(@"订阅成功");
else
//NSLog(@"取消订阅");
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
[peripheral readValueForCharacteristic:characteristic];
NSData *data = characteristic.value;
if (self.receivedDataBlock)
self.receivedDataBlock(data,error);
#pragma mark - - - - lazyLoad - - - -
- (CBCentralManager *)centerManager
if (!_centerManager)
dispatch_queue_t queue = dispatch_queue_create("com.btCenterManager.queue", 0);
CBCentralManager *centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:queue options:nil];
centralManager.delegate = self;
_centerManager = centralManager;
return _centerManager;
- (NSMutableArray *)scannedDevices
if (!_scannedDevices)
_scannedDevices = [NSMutableArray new];
return _scannedDevices;
- (NSMutableArray *)scanServices
if (!_scanServices)
_scanServices = [NSMutableArray new];
return _scanServices;
@end
从设备进行的操作如下:
- 添加服务
- 开始广播
- 停止广播
- 发送数据
具体源码如下:
#import <Foundation/Foundation.h>
#import <CoreBluetooth/CoreBluetooth.h>
typedef void(^JKBTPeripheralStatusBlock)(NSInteger status,NSString *message);
typedef void(^JKBTPeripheralRecievedDataBlock)(NSData *data,NSError *error);
@interface JKBlueToothPeripheralHelper : NSObject
@property (nonatomic,copy) JKBTPeripheralStatusBlock btStatusBlock;///< 蓝牙状态的block
@property (nonatomic,copy) JKBTPeripheralRecievedDataBlock receivedDataBlock;
/**
初始化JKBlueToothPeripheralHelper 对象
@param services 提供的服务数组
@param adContent 广播内容
*/
- (void)addServices:(NSArray <CBMutableService *>*)services adContent:(NSDictionary *)adContent;
/**
添加默认的服务
*/
- (void)addDefaultService;
/**
开始广播
*/
- (void)startAdvertising;
/**
停止广播
*/
- (void)stopAdvertising;
/**
发送数据到设备管理器
@param data 二进制数据
@param centerDevices 主设备
@param characteristic 特征
*/
- (void)sendData:(NSData *)data toCenterManagers:(NSArray <CBCentral*>*)centerDevices forCharacteristic:(CBMutableCharacteristic *)characteristic;
@end
#import "JKBlueToothPeripheralHelper.h"
#import <CoreBluetooth/CoreBluetooth.h>
#import <UIKit/UIKit.h>
#import "JKBlueToothMacro.h"
@interface JKBlueToothPeripheralHelper()<CBPeripheralManagerDelegate>
@property (nonatomic,strong)CBPeripheralManager *peripheralManager;
@property (nonatomic,strong) NSDictionary *adContent; ///< 广播内容
@property (nonatomic,strong) CBMutableService *defaultService; ///< 默认提供的服务
@property (nonatomic,strong) CBMutableCharacteristic *defaultCharacteristic; ///< 默认具有的特征
@property (nonatomic,strong) NSMutableArray <CBMutableService *>*services;
@property (nonatomic,strong) NSMutableArray <CBCentral *>*centrals;
@end
@implementation JKBlueToothPeripheralHelper
- (void)addServices:(NSArray <CBMutableService *>*)services adContent:(NSDictionary *)adContent
if (!self.peripheralManager)
dispatch_queue_t queue = dispatch_queue_create("com.btPeripheralManager.queue", 0);
self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:queue];
if (services.count>0)
[self.services addObjectsFromArray:services];
self.adContent = adContent;
- (void)addDefaultService
CBUUID *serviceID = [CBUUID UUIDWithString:DEFAULT_SERVICE_UUID];
CBMutableService *service = [[CBMutableService alloc] initWithType:serviceID primary:YES];
// 创建服务中的特征
CBUUID *characteristicID = [CBUUID UUIDWithString:DEFAULT_CHARACTERISTIC_UUID];
CBMutableCharacteristic *characteristic = [
[CBMutableCharacteristic alloc]
initWithType:characteristicID
properties:
CBCharacteristicPropertyRead |
CBCharacteristicPropertyWrite |
CBCharacteristicPropertyNotify
value:nil
permissions:CBAttributePermissionsReadable |
CBAttributePermissionsWriteable
];
// 特征添加进服务
[JKBlueToothModule service:service addCharacteristic:characteristic];
self.defaultCharacteristic = characteristic;
self.defaultService = service;
[self.services addObject:self.defaultService];
- (void)startAdvertising
[self.peripheralManager startAdvertising:self.adContent];
- (void)stopAdvertising
[self.peripheralManager stopAdvertising];
- (void)sendData:(NSData *)data toCenterManagers:(NSArray <CBCentral*>*)centerDevices forCharacteristic:(CBMutableCharacteristic *)characteristic
characteristic = characteristic?:self.defaultCharacteristic;
if (!centerDevices || centerDevices.count == 0)
centerDevices = self.centrals;
[self.peripheralManager updateValue:data forCharacteristic:characteristic onSubscribedCentrals:centerDevices];
#pragma mark - - - - CBPeripheralManagerDelegate - - - -
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral
if (@available(iOS 10.0, *))
switch (peripheral.state)
case CBManagerStatePoweredOn://蓝牙打开
for (CBMutableService *service in self.services)
[self.peripheralManager addService:service];
[self startAdvertising];
break;
case CBManagerStatePoweredOff://蓝牙关闭了
if (self.btStatusBlock)
self.btStatusBlock(CBManagerStatePoweredOff, @"蓝牙已关闭,请打开蓝牙");
break;
case CBManagerStateUnsupported://不支持
if (self.btStatusBlock)
self.btStatusBlock(CBManagerStatePoweredOff, @"蓝牙设备不支持!");
break;
default:
break;
else
// Fallback on earlier versions
switch (peripheral.state)
case 5://蓝牙打开
for (CBMutableService *service in self.services)
[self.peripheralManager addService:service];
[self startAdvertising];
break;
case 4://蓝牙关闭了
if (self.btStatusBlock)
self.btStatusBlock(4, @"蓝牙已关闭,请打开蓝牙");
break;
case 2://不支持
if (self.btStatusBlock)
self.btStatusBlock(2, @"蓝牙设备不支持!");
break;
default:
break;
-(void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request
// if (request.characteristic.properties & CBCharacteristicPropertyRead)
// NSData *data = request.characteristic.value;
// // [request setValue:data];
// //[self.peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
// else
// [self.peripheralManager respondToRequest:request withResult:CBATTErrorReadNotPermitted];
//
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests
CBATTRequest *request = requests.lastObject;
if (request.characteristic.properties & CBCharacteristicPropertyWrite)
CBMutableCharacteristic *characteristic = (CBMutableCharacteristic *)request.characteristic;
characteristic.value = request.value;
if (self.receivedDataBlock)
self.receivedDataBlock(request.value,nil);
[self.peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
else
if (self.receivedDataBlock)
NSError *error = [[NSError alloc] initWithDomain:@"JKBlueToothModule" code:CBATTErrorWriteNotPermitted userInfo:@@"msg":@"CBATTErrorWriteNotPermitted error"];
self.receivedDataBlock(nil,error);
[self.peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
//订阅characteristics
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic
//NSLog(@"订阅了 %@的数据",characteristic.UUID);
[self.centrals addObject:central];
//取消订阅characteristics
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic
//NSLog(@"取消订阅 %@的数据",characteristic.UUID);
[self.centrals removeObject:central];
#pragma mark - - - - lazyLoad - - - -
- (NSMutableArray *)services
if (!_services)
_services = [NSMutableArray new];
return _services;
- (NSMutableArray *)centrals
if (!_centrals)
_centrals = [NSMutableArray new];
return _centrals;
@end
代码可以直接复制直接使用。由于我这边是私有库,就不开放给大家了。另外文件传输时,参考我之前写的一篇文章《iOS蓝牙开发之数据传输精华篇》
里面有讲到数据拼接的一个工具 ,pod集成如下:
pod 'JKTransferDataHelper'
以上是关于iOS 蓝牙开发实现文件传输的主要内容,如果未能解决你的问题,请参考以下文章