处理ANCS设备连接绑定问题

ANCS.jpg

ANCS(Apple Notification Center Service,苹果通知中心)的目的是提供给蓝牙外设一种简单、方便的获取iOS设备通知信息的方式。使得蓝牙手环,手表可以接收到来自iPhone的来电、短信及QQ、微信等应用的通知消息。

如果你已经能够连接普通蓝牙,初次面对ANCS设备可能会有以下问题:

问题一:遵循ANCS协议的的设备会直接和系统相连,即使杀掉应用,连接还是存在的。而如果蓝牙设备处于连接状态,它不会被扫描到,怎么再次连接呢?

在Core Bluetooth framework里提供了两个方法,用于获取已连接的设备

//通过传入的peripherals.identifier返回与系统连接的已知设备数组
- (NSArray<CBPeripheral *> *)retrievePeripheralsWithIdentifiers:(NSArray<NSUUID *> *)identifiers;
//通过传入设备的serviceID返回已连接的设备数组
- (NSArray<CBPeripheral *> *)retrieveConnectedPeripheralsWithServices:(NSArray<CBUUID *> *)serviceUUIDs;

我们就可以通过这两个方法,获取已连接设备,并建立重连。参考代码:

NSArray *peripherals = [central retrieveConnectedPeripheralsWithServices:@[serviceUUID]];
if (peripherals.count > 0) {
    CBPeripheral *peripheral = [peripherals firstObject];
    peripheral.delegate = self;
    self.peripheral = peripheral;//**关键**需要转存外设值,才能发起连接
    [central connectPeripheral:self.peripheral options:nil];
} else {
    [central scanForPeripheralsWithServices:@[serviceUUID] options:nil];
}

根据不同的使用情况,可能会有不同的扫描,连接的逻辑,苹果提供了一个流程图:

workflow.png

问题二:有绑定和解除功能,如何处理两者的关系

首先我们要知道,不能通过代码,断开ANCS设备与系统之间的连接,那么如果我们想解除设备的绑定,只能控制设备与APP之间的断开。

###绑定 再回看上面提到的苹果提供的两个获取已连接设备的方法,一个是通过serviceUUID,它可以返回同一类型的设备列表;一个是通过设备UUID,它在一定情况下就是唯一的(如果设备名唯一,这里可以使用设备名),返回的是唯一设备。那么我们就可以利用UUID的唯一性,作为绑定的标示,存到NSUserDefault里面,对于未绑定的设备通过serviceUUID去获取设备列表。参考代码:

NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
NSString *uuidString = [userDefault objectForKey:RWBLE_BANDIDENTIFI_ID];
    
NSArray *peripherals;
if (uuidString) {
        //通过uuid获取连接设备
    NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString];
        peripherals = [self.centralManager retrievePeripheralsWithIdentifiers:@[uuid]];
}else{
        //通过serviceUUID获取连接设备
        peripherals = [self.centralManager retrieveConnectedPeripheralsWithServices:@[[CBUUID UUIDWithString:ST_SERVICE_UUID]]];
}
/* peripherals connect code */

###解绑 不能使ANCS设备与系统连接断开,那么我们就在程序里销毁这个外设对象,这样APP与蓝牙设备的连接通讯就不存在了,造成了一种断开的感觉。参考代码:

//解绑设备
- (void)unbindDevice{
    [self disconnect];//通知app,设备已经断开

    NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
    [userDefault removeObjectForKey:RWBLE_BANDIDENTIFI_ID];//销毁uuid
    self.peripheral = nil;
}

这么写看似已经解决问题了,但是会出现一种情况:解绑了设备,杀掉应用,再次进入设备还是能连上。why?因为虽然没有了UUID,但进入程序会通过serviceUUID再次获取连接。

这时可以在扫面做一个判断,是否刚解绑过设备。可以是个BOOL值,绑定和初始绑定为NO,解绑操作改为YES。如果刚解绑过设备,就直接返回不做后面的扫描操作,这样就解决了上面的问题。这个比较简单,就不列具体代码了。

参考文档: Best Practices for Interacting with a Remote Peripheral Device