iOS对于ANCS设备的处理

1.什么是ANCS?

ANCS是Apple Notification Center Service的简称,中文为苹果通知中心服务。
ANCS是苹果让周边蓝牙设备(手环、手表等)可以通过低功耗蓝牙访问IOS设备(iphone、ipad等)上的各类通知提供的一种简单方便的机制。

ANCS是基于BLE协议中的通用属性协议(Generic Attribute Profile,GATT)协议实现的,他是GATT协议的一个子集。在ANCS协议中,IOS设备作为gatt-server,而周边设备作为gatt client来连接和使用server提供的其他services。

详细的可以参考官网上的解释ANCS spec

2.带来的影响是什么?

目前iOS这边关于蓝牙的开发大多都是基于Ble来实现的,对连接断开有较深使用经验的人都知道,iOS蓝牙这边的Ble关于断连重新连接模块都是用的懒加载(再次连上同一个mac地址的ble设备,底层没有重新获取特征值,只是从缓存里读了一份给到上层)

所以ANCS带来的影响是:

  • 1.无法从代码层面断开BLE连接了。

既然无法断开,那就意味着一旦连上了就无法让别的手机去搜索到,而且杀死当前连上的应用后,也无法通过常规的手段(centerManager.scanForPeripherals(withServices: nil, options: nil))去搜索到设备。

  • 2.业务流程设计上变得用户体验很糟糕

3.如何解决?

  • 1.目前除了手动解除绑定以外,没有其他的方法可以解除绑定
  • 2.iOS提供了 **central.retrievePeripherals(withIdentifiers:[uuid]) **的方法来获取已连接上的ANCS设备

以下提供了一个demo来感受一下整个过程:

import Foundation
import CoreBluetooth
class BLEManager: NSObject,CBCentralManagerDelegate,CBPeripheralDelegate{
    lazy var centerManager:CBCentralManager = {
        CBCentralManager(delegate: self, queue: DispatchQueue.main)
    }()
    var myPeripheral:CBPeripheral?
    private var list:[CBPeripheral] = []
    override init() {
        super.init()
    }
    func start(){
        centerManager.scanForPeripherals(withServices: nil, options: nil)
        print("⚠️ Need to hasPerfix your ble device's name or it maybe found nothing")
        DispatchQueue.main.asyncAfter(deadline: .now()+2, execute: {
            if let item = self.list.first {
                if item.state == .disconnected {
                    self.centerManager.connect(item, options: nil)
                }
            }
        })
    }
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        switch central.state {
        case .unknown:
            break
        case .resetting:
            break
        case .unsupported:
            break
        case .unauthorized:
            break
        case .poweredOff:
            break
        case .poweredOn:
            centerManager.scanForPeripherals(withServices: nil, options: nil)
        @unknown default:
            break
        }
    }
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        if ((peripheral.name?.hasPrefix("AC701N_")) != nil){
            if peripheral.name == "AC701N_GJ" {
                centerManager.stopScan()
                list.append(peripheral)
                print("Did found:\n\(peripheral)")
            }
        }
    }
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        self.myPeripheral = peripheral
        self.myPeripheral?.delegate = self
        self.myPeripheral?.discoverServices(nil)
    }
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
        print("disconnect Ble in this application !")
        print("The Ble ANCS device is alway here,you can watch it in system setting")
        let uuid = UUID(uuid: peripheral.identifier.uuid)
        let array = central.retrievePeripherals(withIdentifiers:[uuid])
        for item in array {
            print("Now the ble is founded in here:\n\(item)")
            DispatchQueue.main.asyncAfter(deadline: .now()+10) {
                print("Here is to reconnect by 10 sec.")
                self.centerManager.connect(item, options: nil)
            }
        }
    }
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        guard let services = peripheral.services else{
            return
        }
        for service in services {
            if service.uuid.uuidString == "AE00" {
                peripheral.discoverCharacteristics(nil, for: service)
            }
        }
    }
    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        guard let charts = service.characteristics else { return }
        for chart in charts {
            if chart.uuid.uuidString == "AE01" {
                print("get wirte chart");
            }
            if chart.uuid.uuidString == "AE02" {
                print("get read chart")
            }
        }
        print("Please make sure pair in your phone ,it will disconnect by 10 sec.")
        DispatchQueue.main.asyncAfter(deadline: .now()+10) {
            print("go to cancel connect BLE")
            self.centerManager.cancelPeripheralConnection(peripheral)
        }
    }
}

运行起来:

class ViewController: UIViewController {
    var mgr = BLEManager()
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        mgr.start()
    }
}

log分析

2022-04-01 12:00:37.131809+0800 BleConnect[683:73634] [CoreBluetooth] API MISUSE:  can only accept this command while in the powered on state
⚠️ Need to hasPerfix your ble device's name or it maybe found nothing
Did found:

get wirte chart
get read chart
Please make sure pair in your phone ,it will disconnect by 10 sec.
go to cancel connect BLE
disconnect Ble in this application !
The Ble ANCS device is alway here,you can watch it in system setting
Now the ble is founded in here:

Here is to reconnect by 10 sec.
get wirte chart
get read chart
Please make sure pair in your phone ,it will disconnect by 10 sec.
go to cancel connect BLE
disconnect Ble in this application !
The Ble ANCS device is alway here,you can watch it in system setting
Now the ble is founded in here:

最后注意的点

  • 1.设备通过A应用连接手机,A应用内无法通过API直接断开设备,当杀死应用后,设备会处于一直连接着手机的情况。
    这个时候只能通过A应用retrieveconnectedperipher去搜索出来,重新进行连接。
  • 2.在情景1的A应用杀死时设备还处于连接手机系统,B应用/其他应用通过retrieveconnectedperipher搜索出来设备,但不可以获取设备的service。
  • 3.在同一应用,同一次连接BLE下,如果设备Mac不变,短期内多次发起断连请求,iOS系统不会去重复请求设备的Service,读到的仅仅是缓存。

你可能感兴趣的:(iOS对于ANCS设备的处理)