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 蓝牙开发实现文件传输的主要内容,如果未能解决你的问题,请参考以下文章

iOS 蓝牙开发实现文件传输

Android蓝牙开发(二)经典蓝牙消息传输实现

如何使用Android蓝牙开发

Android-蓝牙传输

如何使用Android蓝牙开发

从 iPhone 应用程序通过蓝牙传输文件