* 临时提交 * 临时提交 * 临时提交 * fix: 调整接口 * fix: 修复问题 * fix: 调整 iOS 实现 * fix: 添加注释 * fix: 修改预览版本号 * fix: 修复已知问题 * fix: 优化接口 * fix: 解决 32 位 UUID 报错问题 * fix: 修复问题 * fix: 修复依赖项 * fix: 移除多余代码 * fix: 修复已知问题 * fix: 修复问题 * fix: 修改版本号 * fix: 修复问题 * fix: 发布正式版本
376 lines
16 KiB
Swift
376 lines
16 KiB
Swift
//
|
|
// MyPeripheralManager.swift
|
|
// bluetooth_low_energy_darwin
|
|
//
|
|
// Created by 闫守旺 on 2023/10/7.
|
|
//
|
|
|
|
import Foundation
|
|
import CoreBluetooth
|
|
|
|
#if os(iOS)
|
|
import Flutter
|
|
#elseif os(macOS)
|
|
import FlutterMacOS
|
|
#else
|
|
#error("Unsupported platform.")
|
|
#endif
|
|
|
|
class MyPeripheralManager: MyPeripheralManagerHostApi {
|
|
init(_ binaryMessenger: FlutterBinaryMessenger) {
|
|
self.binaryMessenger = binaryMessenger
|
|
}
|
|
|
|
private let binaryMessenger: FlutterBinaryMessenger
|
|
private let peripheralManager = CBPeripheralManager()
|
|
|
|
private lazy var api = MyPeripheralManagerFlutterApi(binaryMessenger: binaryMessenger)
|
|
private lazy var peripheralManagerDelegate = MyPeripheralManagerDelegate(self)
|
|
|
|
private var centrals = [Int64: CBCentral]()
|
|
private var services = [Int64: CBMutableService]()
|
|
private var characteristics = [Int64: CBMutableCharacteristic]()
|
|
private var descriptors = [Int64: CBMutableDescriptor]()
|
|
private var requests = [Int64: CBATTRequest]()
|
|
|
|
private var centralsArgs = [Int: MyCentralArgs]()
|
|
private var servicesArgs = [Int: MyGattServiceArgs]()
|
|
private var characteristicsArgs = [Int: MyGattCharacteristicArgs]()
|
|
private var descriptorsArgs = [Int: MyGattDescriptorArgs]()
|
|
|
|
private var setUpCompletion: ((Result<MyPeripheralManagerArgs, Error>) -> Void)?
|
|
private var addServiceCompletion: ((Result<Void, Error>) -> Void)?
|
|
private var startAdvertisingCompletion: ((Result<Void, Error>) -> Void)?
|
|
private var notifyCharacteristicValueChangedCallbacks = [() -> Void]()
|
|
|
|
func setUp(completion: @escaping (Result<MyPeripheralManagerArgs, Error>) -> Void) {
|
|
do {
|
|
if setUpCompletion != nil {
|
|
throw MyError.illegalState
|
|
}
|
|
try tearDown()
|
|
peripheralManager.delegate = peripheralManagerDelegate
|
|
if peripheralManager.state == .unknown {
|
|
setUpCompletion = completion
|
|
} else {
|
|
let stateArgs = peripheralManager.state.toArgs()
|
|
let stateNumberArgs = Int64(stateArgs.rawValue)
|
|
let args = MyPeripheralManagerArgs(stateNumberArgs: stateNumberArgs)
|
|
completion(.success(args))
|
|
}
|
|
} catch {
|
|
completion(.failure(error))
|
|
}
|
|
}
|
|
|
|
func tearDown() throws {
|
|
if(peripheralManager.isAdvertising) {
|
|
peripheralManager.stopAdvertising()
|
|
}
|
|
centrals.removeAll()
|
|
services.removeAll()
|
|
characteristics.removeAll()
|
|
descriptors.removeAll()
|
|
requests.removeAll()
|
|
centralsArgs.removeAll()
|
|
servicesArgs.removeAll()
|
|
characteristicsArgs.removeAll()
|
|
descriptorsArgs.removeAll()
|
|
setUpCompletion = nil
|
|
addServiceCompletion = nil
|
|
startAdvertisingCompletion = nil
|
|
notifyCharacteristicValueChangedCallbacks.removeAll()
|
|
}
|
|
|
|
func addService(serviceArgs: MyGattServiceArgs, completion: @escaping (Result<Void, Error>) -> Void) {
|
|
do {
|
|
if addServiceCompletion != nil {
|
|
throw MyError.illegalState
|
|
}
|
|
let service = serviceArgs.toService()
|
|
var characteristics = [CBMutableCharacteristic]()
|
|
let characteristicsArgs = serviceArgs.characteristicsArgs
|
|
for args in characteristicsArgs {
|
|
guard let characteristicArgs = args else {
|
|
continue
|
|
}
|
|
let characteristic = characteristicArgs.toCharacteristic()
|
|
var descriptors = [CBMutableDescriptor]()
|
|
let descriptorsArgs = characteristicArgs.descriptorsArgs
|
|
for args in descriptorsArgs {
|
|
guard let descriptorArgs = args else {
|
|
continue
|
|
}
|
|
let descriptor = descriptorArgs.toDescriptor()
|
|
descriptors.append(descriptor)
|
|
let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
|
|
let descriptorHashCode = descriptor.hash
|
|
self.descriptorsArgs[descriptorHashCode] = descriptorArgs
|
|
self.descriptors[descriptorHashCodeArgs] = descriptor
|
|
}
|
|
characteristic.descriptors = descriptors
|
|
characteristics.append(characteristic)
|
|
let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
|
|
let characteristicHashCode = characteristic.hash
|
|
self.characteristicsArgs[characteristicHashCode] = characteristicArgs
|
|
self.characteristics[characteristicHashCodeArgs] = characteristic
|
|
}
|
|
service.characteristics = characteristics
|
|
let serviceHashCodeArgs = serviceArgs.hashCodeArgs
|
|
let serviceHashCode = service.hash
|
|
self.servicesArgs[serviceHashCode] = serviceArgs
|
|
self.services[serviceHashCodeArgs] = service
|
|
peripheralManager.add(service)
|
|
addServiceCompletion = completion
|
|
} catch {
|
|
freeService(serviceArgs)
|
|
completion(.failure(error))
|
|
}
|
|
}
|
|
|
|
func removeService(serviceHashCodeArgs: Int64) throws {
|
|
guard let service = services[serviceHashCodeArgs] else {
|
|
throw MyError.illegalArgument
|
|
}
|
|
let serviceHashCode = service.hash
|
|
guard let serviceArgs = servicesArgs[serviceHashCode] else {
|
|
throw MyError.illegalArgument
|
|
}
|
|
peripheralManager.remove(service)
|
|
freeService(serviceArgs)
|
|
}
|
|
|
|
func clearServices() throws {
|
|
peripheralManager.removeAllServices()
|
|
let servicesArgs = self.servicesArgs.values
|
|
for serviceArgs in servicesArgs {
|
|
freeService(serviceArgs)
|
|
}
|
|
}
|
|
|
|
private func freeService(_ serviceArgs: MyGattServiceArgs) {
|
|
let characteristicsArgs = serviceArgs.characteristicsArgs
|
|
for args in characteristicsArgs {
|
|
guard let characteristicArgs = args else {
|
|
continue
|
|
}
|
|
let descriptorsArgs = characteristicArgs.descriptorsArgs
|
|
for args in descriptorsArgs {
|
|
guard let descriptorArgs = args else {
|
|
continue
|
|
}
|
|
let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
|
|
guard let descriptor = self.descriptors.removeValue(forKey: descriptorHashCodeArgs) else {
|
|
continue
|
|
}
|
|
let descriptorHashCode = descriptor.hash
|
|
self.descriptorsArgs.removeValue(forKey: descriptorHashCode)
|
|
}
|
|
let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
|
|
guard let characteristic = self.characteristics.removeValue(forKey: characteristicHashCodeArgs) else {
|
|
continue
|
|
}
|
|
let characteristicHashCode = characteristic.hash
|
|
self.characteristicsArgs.removeValue(forKey: characteristicHashCode)
|
|
}
|
|
let serviceHashCodeArgs = serviceArgs.hashCodeArgs
|
|
guard let service = self.services.removeValue(forKey: serviceHashCodeArgs) else {
|
|
return
|
|
}
|
|
let serviceHashCode = service.hash
|
|
self.servicesArgs.removeValue(forKey: serviceHashCode)
|
|
}
|
|
|
|
func startAdvertising(advertiseDataArgs: MyAdvertiseDataArgs, completion: @escaping (Result<Void, Error>) -> Void) {
|
|
do {
|
|
if startAdvertisingCompletion != nil {
|
|
throw MyError.illegalState
|
|
}
|
|
let advertisementData = try advertiseDataArgs.toAdvertiseData()
|
|
peripheralManager.startAdvertising(advertisementData)
|
|
startAdvertisingCompletion = completion
|
|
} catch {
|
|
completion(.failure(error))
|
|
}
|
|
}
|
|
|
|
func stopAdvertising() throws {
|
|
peripheralManager.stopAdvertising()
|
|
}
|
|
|
|
func getMaximumWriteLength(centralHashCodeArgs: Int64) throws -> Int64 {
|
|
guard let central = centrals[centralHashCodeArgs] else {
|
|
throw MyError.illegalArgument
|
|
}
|
|
let maximumWriteLength = central.maximumUpdateValueLength
|
|
let maximumWriteLengthArgs = Int64(maximumWriteLength)
|
|
return maximumWriteLengthArgs
|
|
}
|
|
|
|
func sendReadCharacteristicReply(centralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, idArgs: Int64, offsetArgs: Int64, statusArgs: Bool, valueArgs: FlutterStandardTypedData) throws {
|
|
guard let request = requests[idArgs] else {
|
|
throw MyError.illegalArgument
|
|
}
|
|
request.value = valueArgs.data
|
|
let result = statusArgs ? CBATTError.Code.success : CBATTError.Code.requestNotSupported
|
|
peripheralManager.respond(to: request, withResult: result)
|
|
}
|
|
|
|
func sendWriteCharacteristicReply(centralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, idArgs: Int64, offsetArgs: Int64, statusArgs: Bool) throws {
|
|
guard let request = requests[idArgs] else {
|
|
throw MyError.illegalArgument
|
|
}
|
|
let result = statusArgs ? CBATTError.Code.success : CBATTError.Code.requestNotSupported
|
|
peripheralManager.respond(to: request, withResult: result)
|
|
}
|
|
|
|
func notifyCharacteristicValueChanged(centralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, completion: @escaping (Result<Void, Error>) -> Void) {
|
|
do {
|
|
if notifyCharacteristicValueChangedCallbacks.count > 0 {
|
|
throw MyError.illegalState
|
|
}
|
|
let value = valueArgs.data
|
|
guard let characteristic = characteristics[characteristicHashCodeArgs] else {
|
|
throw MyError.illegalArgument
|
|
}
|
|
guard let central = centrals[centralHashCodeArgs] else {
|
|
throw MyError.illegalArgument
|
|
}
|
|
let centrals = [central]
|
|
let updated = peripheralManager.updateValue(value, for: characteristic, onSubscribedCentrals: centrals)
|
|
if updated {
|
|
completion(.success(()))
|
|
} else {
|
|
notifyCharacteristicValueChangedCallbacks.append {
|
|
let updated = self.peripheralManager.updateValue(value, for: characteristic, onSubscribedCentrals: centrals)
|
|
if updated {
|
|
completion(.success(()))
|
|
} else {
|
|
completion(.failure(MyError.unknown))
|
|
}
|
|
}
|
|
}
|
|
} catch {
|
|
completion(.failure(error))
|
|
}
|
|
}
|
|
|
|
func didUpdateState() {
|
|
let state = peripheralManager.state
|
|
let stateArgs = state.toArgs()
|
|
let stateNumberArgs = Int64(stateArgs.rawValue)
|
|
if state != .unknown && setUpCompletion != nil {
|
|
let args = MyPeripheralManagerArgs(stateNumberArgs: stateNumberArgs)
|
|
setUpCompletion!(.success(args))
|
|
setUpCompletion = nil
|
|
}
|
|
api.onStateChanged(stateNumberArgs: stateNumberArgs) {}
|
|
}
|
|
|
|
func didAdd(_ service: CBService, _ error: Error?) {
|
|
guard let completion = addServiceCompletion else {
|
|
return
|
|
}
|
|
addServiceCompletion = nil
|
|
if error == nil {
|
|
completion(.success(()))
|
|
} else {
|
|
completion(.failure(error!))
|
|
let serviceHashCode = service.hash
|
|
guard let serviceArgs = servicesArgs[serviceHashCode] else {
|
|
return
|
|
}
|
|
freeService(serviceArgs)
|
|
}
|
|
}
|
|
|
|
func didStartAdvertising(_ error: Error?) {
|
|
guard let completion = startAdvertisingCompletion else {
|
|
return
|
|
}
|
|
startAdvertisingCompletion = nil
|
|
if error == nil {
|
|
completion(.success(()))
|
|
} else {
|
|
completion(.failure(error!))
|
|
}
|
|
}
|
|
|
|
func didReceiveRead(_ request: CBATTRequest) {
|
|
let central = request.central
|
|
let centralHashCode = central.hash
|
|
let centralArgs = centralsArgs.getOrPut(centralHashCode) { central.toArgs() }
|
|
let centralHashCodeArgs = centralArgs.hashCodeArgs
|
|
centrals[centralHashCodeArgs] = central
|
|
let characteristic = request.characteristic
|
|
let characteristicHashCode = characteristic.hash
|
|
guard let characteristicArgs = characteristicsArgs[characteristicHashCode] else {
|
|
peripheralManager.respond(to: request, withResult: .attributeNotFound)
|
|
return
|
|
}
|
|
let idArgs = Int64(request.hash)
|
|
requests[idArgs] = request
|
|
let offsetArgs = Int64(request.offset)
|
|
api.onReadCharacteristicCommandReceived(centralArgs: centralArgs, characteristicArgs: characteristicArgs, idArgs: idArgs, offsetArgs: offsetArgs) {}
|
|
}
|
|
|
|
func didReceiveWrite(_ requests: [CBATTRequest]) {
|
|
for request in requests {
|
|
let central = request.central
|
|
let centralHashCode = central.hash
|
|
let centralArgs = centralsArgs.getOrPut(centralHashCode) { central.toArgs() }
|
|
let centralHashCodeArgs = centralArgs.hashCodeArgs
|
|
centrals[centralHashCodeArgs] = central
|
|
let characteristic = request.characteristic
|
|
let characteristicHashCode = characteristic.hash
|
|
guard let characteristicArgs = characteristicsArgs[characteristicHashCode] else {
|
|
peripheralManager.respond(to: request, withResult: .attributeNotFound)
|
|
return
|
|
}
|
|
let idArgs = Int64(request.hash)
|
|
self.requests[idArgs] = request
|
|
let offsetArgs = Int64(request.offset)
|
|
guard let value = request.value else {
|
|
peripheralManager.respond(to: request, withResult: .requestNotSupported)
|
|
return
|
|
}
|
|
let valueArgs = FlutterStandardTypedData(bytes: value)
|
|
api.onWriteCharacteristicCommandReceived(centralArgs: centralArgs, characteristicArgs: characteristicArgs, idArgs: idArgs, offsetArgs: offsetArgs, valueArgs: valueArgs) {}
|
|
}
|
|
}
|
|
|
|
func didSubscribeTo(_ central: CBCentral, _ characteristic: CBCharacteristic) {
|
|
let centralHashCode = central.hash
|
|
let centralArgs = centralsArgs.getOrPut(centralHashCode) { central.toArgs() }
|
|
let centralHashCodeArgs = centralArgs.hashCodeArgs
|
|
centrals[centralHashCodeArgs] = central
|
|
let characteristicHashCode = characteristic.hash
|
|
guard let characteristicArgs = characteristicsArgs[characteristicHashCode] else {
|
|
return
|
|
}
|
|
let stateArgs = true
|
|
api.onNotifyCharacteristicCommandReceived(centralArgs: centralArgs, characteristicArgs: characteristicArgs, stateArgs: stateArgs) {}
|
|
}
|
|
|
|
func didUnsubscribeFrom(_ central: CBCentral, _ characteristic: CBCharacteristic) {
|
|
let centralHashCode = central.hash
|
|
let centralArgs = centralsArgs.getOrPut(centralHashCode) { central.toArgs() }
|
|
let centralHashCodeArgs = centralArgs.hashCodeArgs
|
|
centrals[centralHashCodeArgs] = central
|
|
let characteristicHashCode = characteristic.hash
|
|
guard let characteristicArgs = characteristicsArgs[characteristicHashCode] else {
|
|
return
|
|
}
|
|
let stateArgs = false
|
|
api.onNotifyCharacteristicCommandReceived(centralArgs: centralArgs, characteristicArgs: characteristicArgs, stateArgs: stateArgs) {}
|
|
}
|
|
|
|
func isReadyToUpdateSubscribers() {
|
|
let callbacks = notifyCharacteristicValueChangedCallbacks
|
|
notifyCharacteristicValueChangedCallbacks.removeAll()
|
|
for callback in callbacks {
|
|
callback()
|
|
}
|
|
}
|
|
}
|