注:本代码只能真机测试,还需要一个蓝牙防丢器,没有做连接判断,若未连接成功或者不是连接的蓝牙防丢器,就不能点击报警或取消报警,主界面是直接通过StoryBoard搭建的:NavigationController-> UItableViewController
另外防丢功能一般都需要后台可以执行,所以要设置程序可以后台执行,设置方法如图所示:
GET
这里只需要勾选Uses Bluetooth LE accessories选项,如果有其他需求再勾选其他选项,但是不能随意勾选,否则在app上架时会被拒
Demo:
蓝牙防丢器和手机之间可以互相通信,当防丢器给手机发送消息时,需要用到本地消息,就需要在appdelegate中做相应的操作
- AppDelegate相关方法
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
//开启通知权限
if ([UIApplication instancesRespondToSelector:@selector(registerUserNotificationSettings:)]) {
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert categories:nil];
[[UIApplication sharedApplication]registerUserNotificationSettings:settings];
}
return YES;
}
//收到通知后的回调
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:notification.alertTitle message:notification.alertBody delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
[alertView show];
}
- 核心代码
#import "SearchTableViewController.h"
//蓝牙开发框架
#import
//将当前iOS设备作为中心设备用来连接外设
//报警服务
NSString *const kImmediateAlertService = @"1802";
NSString *const kAlertLevel = @"2A06";
//按键服务
NSString *const kHeyStartService = @"FFE0";
NSString *const kKeyPressState = @"FFE1";
@interface SearchTableViewController ()
//中心角色管理对象
@property (nonatomic , strong) CBCentralManager *centralManager;
//存储所有外设
@property (nonatomic , strong) NSMutableArray *peripheralalArray;
//信号强度数组
@property (nonatomic , strong) NSMutableArray *rssiArray;
//保存连接成功的设备
@property (nonatomic , strong) CBPeripheral *connectedPeripheral;
//报警特征
@property (nonatomic , strong) CBCharacteristic *alertCharacteristic;
//按键特征
@property (nonatomic , strong) CBCharacteristic *keyPressCharacteristic;
@end
@implementation SearchTableViewController
//懒加载, 在OC中重写属性的getter,只能通过.语法进行访问
- (NSMutableArray *)peripheralalArray{
if (!_peripheralalArray){
_peripheralalArray = [NSMutableArray array];
}
return _peripheralalArray;
}
- (NSMutableArray *)rssiArray{
if (!_rssiArray) {
_rssiArray = [NSMutableArray array];
}
return _rssiArray;
}
- (IBAction)alertAction:(UIBarButtonItem *)sender {
//0x02:开启报警
int alertNum = 0x02;
NSData *alertData = [NSData dataWithBytes:&alertNum length:1];
[self.connectedPeripheral writeValue:alertData forCharacteristic :self.alertCharacteristic type:CBCharacteristicWriteWithoutResponse];
}
- (IBAction)cancelAlertAction:(UIBarButtonItem *)sender {
//0x00:取消报警
int alertNum = 0x00;
NSData *alertData = [NSData dataWithBytes:&alertNum length:1];
[self.connectedPeripheral writeValue:alertData forCharacteristic :self.alertCharacteristic type:CBCharacteristicWriteWithoutResponse];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self setupCentralManager];
}
- (void) setupCentralManager{
self.centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_main_queue() options:@{CBCentralManagerOptionShowPowerAlertKey: @YES}];
}
/**
* 扫描蓝牙设备
*/
- (void)startScan{
//参数1:限定要扫描的服务
//参数2:选项(字典)
[self.centralManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@YES}];
}
-(void)stopScan{
[self.centralManager stopScan];
}
-(void)connectPeripheral:(CBPeripheral *)peripheral{
//停止扫描
[self stopScan];
//连接
[self.centralManager connectPeripheral:peripheral options:@{CBConnectPeripheralOptionNotifyOnConnectionKey:@YES}];
}
#pragma mark - CBCentralManagerDelegate
/**
* 当蓝牙设备状态改变时的回调
*
* @param central 蓝牙状态
*/
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
switch (central.state) {
case CBCentralManagerStateUnknown:{
NSLog(@"未知情况");
break;
}
case CBCentralManagerStatePoweredOn:{
NSLog(@"蓝牙开启");
[self startScan];
break;
}
case CBCentralManagerStateResetting:{
NSLog(@"蓝牙重置中");
break;
}
case CBCentralManagerStatePoweredOff:{
NSLog(@"蓝牙关闭");
[self stopScan];
break;
}
case CBCentralManagerStateUnsupported:{
NSLog(@"蓝牙不支持");
break;
}
case CBCentralManagerStateUnauthorized:{
NSLog(@"蓝牙未授权");
break;
}
default:
break;
}
}
/**
* 当扫描到外设时的回调
*
* @param central 中心
* @param peripheral 外设
* @param advertisementData 广告包数据
* @param RSSI 信号强度
*/
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
NSLog(@"======================");
NSLog(@"advertisementData=%@",advertisementData);
NSLog(@"RSSI=%@",RSSI);
__block BOOL isFind = NO;
__weak typeof (self) weakSelf = self;
[self.peripheralalArray enumerateObjectsUsingBlock:^(CBPeripheral *tempPeripheral, NSUInteger idx, BOOL * _Nonnull stop) {
//判断UUID是否一样
if ([tempPeripheral.identifier.UUIDString isEqualToString:peripheral.identifier.UUIDString]){
//替换
[weakSelf.peripheralalArray replaceObjectAtIndex:idx withObject:peripheral];
[weakSelf.rssiArray replaceObjectAtIndex:idx withObject:RSSI];
isFind = YES;
//停止循环
*stop = YES;
}
}];
//未找到
if (!isFind){
[self.peripheralalArray addObject:peripheral];
[self.rssiArray addObject:RSSI];
}
//刷新视图
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.tableView reloadData];
});
}
//连接成功的回调
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
self.connectedPeripheral = peripheral;
self.connectedPeripheral.delegate = self;
//扫描外部设备的服务和特征
[self.connectedPeripheral discoverServices:nil];
}
//连接失败的回调
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
}
//断开连接的回调
-(void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
}
#pragma mark - tableViewDataSouce
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(@"%lu",(unsigned long)self.peripheralalArray.count);
return self.peripheralalArray.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
CBPeripheral *peripheral = self.peripheralalArray[indexPath.row];
NSNumber *RSSI = self.rssiArray[indexPath.row];
cell.textLabel.text = peripheral.name;
cell.detailTextLabel.text = RSSI.stringValue;
return cell;
}
#pragma mark - tableViewDelegate
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
CBPeripheral *peripheral = self.peripheralalArray[indexPath.row];
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"连接外设" message: [NSString stringWithFormat:@"是否连接外设名为%@的设备",peripheral.name] preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *connectAction = [UIAlertAction actionWithTitle:@"连接" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[self connectPeripheral:peripheral];
}];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
}];
[alert addAction:connectAction];
[alert addAction:cancelAction];
[self presentViewController:alert animated:YES completion:nil];
}
#pragma mark - CBPripheralDelegate
//发现外设中可用服务
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
if (error) {
NSLog(@"%s %@",__func__,error.localizedDescription);
return;
}
//扫描外设的服务,找到的服务放在CBPeripheral.services中
for (CBService *service in peripheral.services) {
NSLog(@"services=%@",service);
//扫描服务中的特征
[peripheral discoverCharacteristics:nil forService:service];
}
}
//发现外设中某个服务的所有特征
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
if (error) {
NSLog(@"%s %@",__func__,error.localizedDescription);
return;
}
//发现的特征放在服务的characteristics数组
for (CBCharacteristic *characteristic in service.characteristics) {
//找到匹配的特征
if ([service.UUID.UUIDString isEqualToString:kImmediateAlertService] && [characteristic.UUID.UUIDString isEqualToString:kAlertLevel]) {
self.alertCharacteristic = characteristic;
}
if ([service.UUID.UUIDString isEqualToString:kHeyStartService] && [characteristic.UUID.UUIDString isEqualToString:kKeyPressState]) {
self.keyPressCharacteristic = characteristic;
//开启通知
[peripheral setNotifyValue:YES forCharacteristic:self.keyPressCharacteristic];
}
NSLog(@"characteristic=%@",characteristic);
//发现特征中描述
[peripheral discoverDescriptorsForCharacteristic:characteristic];
}
}
//发现特征中的描述信息
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
if (error) {
NSLog(@"%s %@",__func__,error.localizedDescription);
return;
}
for (CBDescriptor *descriptor in characteristic.descriptors) {
NSLog(@"descriptor=%@",descriptor);
}
}
//特征值改变时的回调
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
if (error) {
NSLog(@"%s %@",__func__,error.localizedDescription);
return;
}
if ([characteristic.UUID.UUIDString isEqualToString:self.keyPressCharacteristic.UUID.UUIDString]) {
//当按键按下时,收到特征数据的改变
//创建本地通知
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertTitle = @"防丢器查找";
notification.alertBody = @"防丢器正在查找中";
notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:1];
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
}
@end