* 修复 UUID 创建失败的问题 * 移除 scanning 属性 * 临时提交 * CentralManager 开发 & 示例项目开发 * CentralManager 开发 & 示例项目开发 * android 插件生命周期监听 * 修改 API * 示例程序开发 * 修改字体,添加 API,解决后台问题 * Central#connect API * 蓝牙连接部分开发 * 蓝牙连接部分开发 * 解决一些问题 * 解决一些问题 * Connect API 优化 * 添加 API * example 开发 * API 基本完成 * 消息重命名 * API 修改,Android 实现 * 删除多余代码 * 删除多余文件 * 解决 descriptor 自动生成报错的问题 * 还原 Kotlin 版本,广播处理代码迁移至 dart 端 * Kotlin 版本升至 1.5.20 * 解决特征值通知没有在主线程触发的问题,优化代码 * 引入哈希值,避免对象销毁后继续使用 * 使用下拉刷新代替搜索按钮 * 解决由于热重载和蓝牙关闭产生的问题 * 更新插件信息 * 更新 README 和 CHANGELOG * 更新许可证 * 添加注释 * 添加注释,central 拆分 * dartfmt -w . * flutter build ios --no-codesign * API 重构 * 添加 connectable 属性 * Android 8.0 之前无法获取 connectable 属性 * 解决合并错误 * 解决连接时可能引发异常的一个问题,iOS 开发 * API 修改,TODO: iOS 哈希值为 64 位无法用 Int32 表示 * iOS 开发 * iOS 开发完成,使用 UUID 实现对象映射 * 更新版本记录和文档
513 lines
24 KiB
Swift
513 lines
24 KiB
Swift
import Flutter
|
|
import UIKit
|
|
import CoreBluetooth
|
|
|
|
let NAMESAPCE = "yanshouwang.dev/bluetooth_low_energy"
|
|
|
|
typealias MessageCategory = Dev_Yanshouwang_BluetoothLowEnergy_MessageCategory
|
|
typealias Message = Dev_Yanshouwang_BluetoothLowEnergy_Message
|
|
typealias BluetoothState = Dev_Yanshouwang_BluetoothLowEnergy_BluetoothState
|
|
typealias StartDiscoveryArguments = Dev_Yanshouwang_BluetoothLowEnergy_StartDiscoveryArguments
|
|
typealias Discovery = Dev_Yanshouwang_BluetoothLowEnergy_Discovery
|
|
typealias GATT = Dev_Yanshouwang_BluetoothLowEnergy_GATT
|
|
typealias GattService = Dev_Yanshouwang_BluetoothLowEnergy_GattService
|
|
typealias GattCharacteristic = Dev_Yanshouwang_BluetoothLowEnergy_GattCharacteristic
|
|
typealias GattDescriptor = Dev_Yanshouwang_BluetoothLowEnergy_GattDescriptor
|
|
typealias GattConnectionLost = Dev_Yanshouwang_BluetoothLowEnergy_GattConnectionLost
|
|
typealias GattCharacteristicValue = Dev_Yanshouwang_BluetoothLowEnergy_GattCharacteristicValue
|
|
|
|
public class SwiftBluetoothLowEnergyPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, CBCentralManagerDelegate, CBPeripheralDelegate {
|
|
let SHORTENED_LOCAL_NAME_TYPE = 0x08
|
|
let COMPLETE_LOCAL_NAME_TYPE: UInt8 = 0x09
|
|
let MANUFACTURER_SPECIFIC_DATA_TYPE: UInt8 = 0xff
|
|
let SERVICE_DATA_16BIT_TYPE: UInt8 = 0x16
|
|
let SERVICE_DATA_32BIT_TYPE: UInt8 = 0x20
|
|
let SERVICE_DATA_128BIT_TYPE: UInt8 = 0x21
|
|
let INCOMPLETE_SERVICE_UUIDS_16BIT_TYPE: UInt8 = 0x02
|
|
let INCOMPLETE_SERVICE_UUIDS_32BIT_TYPE: UInt8 = 0x04
|
|
let INCOMPLETE_SERVICE_UUIDS_128BIT_TYPE: UInt8 = 0x06
|
|
let COMPLETE_SERVICE_UUIDS_16BIT_TYPE: UInt8 = 0x03
|
|
let COMPLETE_SERVICE_UUIDS_32BIT_TYPE: UInt8 = 0x05
|
|
let COMPLETE_SERVICE_UUIDS_128BIT_TYPE: UInt8 = 0x07
|
|
let SOLICITTED_SERVICE_UUIDS_16BIT_TYPE: UInt8 = 0x14
|
|
let SOLICITTED_SERVICE_UUIDS_32BIT_TYPE: UInt8 = 0x1f
|
|
let SOLICITTED_SERVICE_UUIDS_128BIT_TYPE: UInt8 = 0x15
|
|
let TX_POWER_LEVEL_TYPE: UInt8 = 0x0a
|
|
|
|
var events: FlutterEventSink? = nil
|
|
|
|
var central: CBCentralManager!
|
|
var oldState: BluetoothState? = nil
|
|
|
|
public override init() {
|
|
super.init()
|
|
central = CBCentralManager(delegate: self, queue: nil)
|
|
}
|
|
|
|
lazy var nativeGATTs = [String: NativeGATT]()
|
|
|
|
lazy var connects = [CBPeripheral: FlutterResult]()
|
|
lazy var disconnects = [CBPeripheral: FlutterResult]()
|
|
lazy var characteristicReads = [CBCharacteristic: FlutterResult]()
|
|
lazy var characteristicWrites = [CBCharacteristic: FlutterResult]()
|
|
lazy var characteristicNotifies = [CBCharacteristic: FlutterResult]()
|
|
lazy var descriptorReads = [CBDescriptor: FlutterResult]()
|
|
lazy var descriptorWrites = [CBDescriptor: FlutterResult]()
|
|
|
|
public static func register(with registrar: FlutterPluginRegistrar) {
|
|
debugPrint("SwiftBluetoothLowEnergyPlugin: register")
|
|
let instance = SwiftBluetoothLowEnergyPlugin()
|
|
let messenger = registrar.messenger()
|
|
let method = FlutterMethodChannel(name: "\(NAMESAPCE)/method", binaryMessenger: messenger)
|
|
registrar.addMethodCallDelegate(instance, channel: method)
|
|
let event = FlutterEventChannel(name: "\(NAMESAPCE)/event", binaryMessenger: messenger)
|
|
event.setStreamHandler(instance)
|
|
}
|
|
|
|
public func detachFromEngine(for registrar: FlutterPluginRegistrar) {
|
|
debugPrint("SwiftBluetoothLowEnergyPlugin: detachFromEngine")
|
|
// Clear connections.
|
|
for nativeGATT in nativeGATTs.values {
|
|
central.cancelPeripheralConnection(nativeGATT.value)
|
|
}
|
|
// Stop scan.
|
|
if central.isScanning {
|
|
central.stopScan()
|
|
}
|
|
}
|
|
|
|
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
|
let arguments = call.arguments as! FlutterStandardTypedData
|
|
let command = try! Message(serializedData: arguments.data)
|
|
switch command.category {
|
|
case .bluetoothState:
|
|
result(central.messageState.rawValue)
|
|
case .centralStartDiscovery:
|
|
let withServices = command.startDiscoveryArguments.services.map { CBUUID(string: $0) }
|
|
let options = [CBCentralManagerScanOptionAllowDuplicatesKey: true]
|
|
central.scanForPeripherals(withServices: withServices, options: options)
|
|
result(nil)
|
|
case .centralStopDiscovery:
|
|
central.stopScan()
|
|
result(nil)
|
|
case .centralConnect:
|
|
let uuid = UUID(uuidString: command.connectArguments.uuid)!
|
|
let peripheral = central.retrievePeripherals(withIdentifiers: [uuid]).first!
|
|
connects[peripheral] = result
|
|
central.connect(peripheral, options: nil)
|
|
case .gattDisconnect:
|
|
let nativeGATT = nativeGATTs[command.disconnectArguments.key]!
|
|
disconnects[nativeGATT.value] = result
|
|
central.cancelPeripheralConnection(nativeGATT.value)
|
|
case .gattCharacteristicRead:
|
|
let nativeGATT = nativeGATTs[command.characteristicReadArguments.gattKey]!
|
|
let nativeService = nativeGATT.services[command.characteristicReadArguments.serviceKey]!
|
|
let nativeCharacteristic = nativeService.characteristics[command.characteristicReadArguments.key]!
|
|
characteristicReads[nativeCharacteristic.value] = result
|
|
nativeGATT.value.readValue(for: nativeCharacteristic.value)
|
|
case .gattCharacteristicWrite:
|
|
let nativeGATT = nativeGATTs[command.characteristicWriteArguments.gattKey]!
|
|
let nativeService = nativeGATT.services[command.characteristicWriteArguments.serviceKey]!
|
|
let nativeCharacteristic = nativeService.characteristics[command.characteristicWriteArguments.key]!
|
|
let value = command.characteristicWriteArguments.value
|
|
let type: CBCharacteristicWriteType = command.characteristicWriteArguments.withoutResponse ? .withoutResponse : .withResponse
|
|
characteristicWrites[nativeCharacteristic.value] = result
|
|
nativeGATT.value.writeValue(value, for: nativeCharacteristic.value, type: type)
|
|
break
|
|
case .gattCharacteristicNotify:
|
|
let nativeGATT = nativeGATTs[command.characteristicNotifyArguments.gattKey]!
|
|
let nativeService = nativeGATT.services[command.characteristicNotifyArguments.serviceKey]!
|
|
let nativeCharacteristic = nativeService.characteristics[command.characteristicNotifyArguments.key]!
|
|
let state = command.characteristicNotifyArguments.state
|
|
characteristicNotifies[nativeCharacteristic.value] = result
|
|
nativeGATT.value.setNotifyValue(state, for: nativeCharacteristic.value)
|
|
break
|
|
case .gattDescriptorRead:
|
|
let nativeGATT = nativeGATTs[command.descriptorReadArguments.gattKey]!
|
|
let nativeService = nativeGATT.services[command.descriptorReadArguments.serviceKey]!
|
|
let nativeCharacteristic = nativeService.characteristics[command.descriptorReadArguments.characteristicKey]!
|
|
let nativeDescriptor = nativeCharacteristic.descriptors[command.descriptorReadArguments.key]!
|
|
descriptorReads[nativeDescriptor.value] = result
|
|
nativeGATT.value.readValue(for: nativeDescriptor.value)
|
|
break
|
|
case .gattDescriptorWrite:
|
|
let nativeGATT = nativeGATTs[command.descriptorReadArguments.gattKey]!
|
|
let nativeService = nativeGATT.services[command.descriptorReadArguments.serviceKey]!
|
|
let nativeCharacteristic = nativeService.characteristics[command.descriptorReadArguments.characteristicKey]!
|
|
let nativeDescriptor = nativeCharacteristic.descriptors[command.descriptorReadArguments.key]!
|
|
let value = command.descriptorWriteArguments.value
|
|
descriptorWrites[nativeDescriptor.value] = result
|
|
nativeGATT.value.writeValue(value, for: nativeDescriptor.value)
|
|
break
|
|
default:
|
|
result(FlutterMethodNotImplemented)
|
|
}
|
|
}
|
|
|
|
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
|
|
debugPrint("SwiftBluetoothLowEnergyPlugin: onListen")
|
|
self.events = events
|
|
return nil
|
|
}
|
|
|
|
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
|
|
debugPrint("SwiftBluetoothLowEnergyPlugin: onCancel")
|
|
// This must be a hot reload for now, clear all status here.
|
|
// Clear connections.
|
|
for nativeGATT in nativeGATTs.values {
|
|
central.cancelPeripheralConnection(nativeGATT.value)
|
|
}
|
|
// Stop scan.
|
|
if central.isScanning {
|
|
central.stopScan()
|
|
}
|
|
events = nil
|
|
return nil
|
|
}
|
|
|
|
public func centralManagerDidUpdateState(_ central: CBCentralManager) {
|
|
let newState = central.messageState
|
|
if newState == oldState {
|
|
return
|
|
} else if oldState == nil {
|
|
oldState = newState
|
|
} else {
|
|
oldState = newState
|
|
let event = try! Message.with {
|
|
$0.category = .bluetoothState
|
|
$0.state = newState
|
|
}.serializedData()
|
|
events?(event)
|
|
// Send connection lost event and remove all nativeGATTs after central closed, same behaior with Android.
|
|
if newState != .poweredOn {
|
|
for nativeGATT in nativeGATTs {
|
|
let connectionLost = GattConnectionLost.with {
|
|
$0.key = nativeGATT.key
|
|
$0.error = "Central closed."
|
|
}
|
|
let event = try! Message.with {
|
|
$0.category = .gattConnectionLost
|
|
$0.connectionLost = connectionLost
|
|
}.serializedData()
|
|
events?(event)
|
|
}
|
|
nativeGATTs.removeAll()
|
|
}
|
|
}
|
|
}
|
|
|
|
public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
|
|
// Can't analyze complete raw advertisements on iOS.
|
|
var advertisements = Data()
|
|
for key in advertisementData.keys {
|
|
switch key {
|
|
case CBAdvertisementDataLocalNameKey:
|
|
let name = advertisementData[key] as! String
|
|
let data = name.data(using: .utf8)!
|
|
let length = UInt8(data.count) + 1
|
|
advertisements.append(length)
|
|
// TODO: We don't know is this a SHORTENED_LOCAL_NAME or COMPLETE_LOCAL_NAME
|
|
// Just use COMPLETE_LOCAL_NAME as type
|
|
advertisements.append(COMPLETE_LOCAL_NAME_TYPE)
|
|
advertisements.append(data)
|
|
break
|
|
case CBAdvertisementDataManufacturerDataKey:
|
|
let data = advertisementData[key] as! Data
|
|
let length = UInt8(data.count) + 1
|
|
advertisements.append(length)
|
|
advertisements.append(MANUFACTURER_SPECIFIC_DATA_TYPE)
|
|
advertisements.append(data)
|
|
break
|
|
case CBAdvertisementDataServiceDataKey:
|
|
let serviceData = advertisementData[key] as! [CBUUID: Data]
|
|
var data = Data()
|
|
for item in serviceData {
|
|
data.append(item.key.data)
|
|
data.append(item.value)
|
|
}
|
|
let length = UInt8(data.count) + 1
|
|
advertisements.append(length)
|
|
// TODO: Need to know the real SERVICE_DATA_TYPE
|
|
advertisements.append(SERVICE_DATA_128BIT_TYPE)
|
|
advertisements.append(data)
|
|
break
|
|
case CBAdvertisementDataServiceUUIDsKey:
|
|
let serviceUUIDs = advertisementData[key] as! [CBUUID]
|
|
var data = Data()
|
|
for serviceUUID in serviceUUIDs {
|
|
data.append(serviceUUID.data)
|
|
}
|
|
let length = UInt8(data.count) + 1
|
|
advertisements.append(length)
|
|
// TODO: Need to know the real SERVICE_UUIDS_TYPE
|
|
advertisements.append(COMPLETE_SERVICE_UUIDS_128BIT_TYPE)
|
|
advertisements.append(data)
|
|
break
|
|
case CBAdvertisementDataOverflowServiceUUIDsKey:
|
|
// TODO: What is an OVERFLOW_SERVICE_UUIDS_TYPE?
|
|
// Maybe INCOMPLETE_SERVICE_UUIDS?
|
|
break
|
|
case CBAdvertisementDataTxPowerLevelKey:
|
|
let txPowerLevel = advertisementData[key] as! UInt8
|
|
advertisements.append(2)
|
|
advertisements.append(TX_POWER_LEVEL_TYPE)
|
|
advertisements.append(txPowerLevel)
|
|
break
|
|
case CBAdvertisementDataIsConnectable:
|
|
break
|
|
case CBAdvertisementDataSolicitedServiceUUIDsKey:
|
|
let serviceUUIDs = advertisementData[key] as! [CBUUID]
|
|
var data = Data()
|
|
for serviceUUID in serviceUUIDs {
|
|
data.append(serviceUUID.data)
|
|
}
|
|
let length = UInt8(data.count) + 1
|
|
advertisements.append(length)
|
|
// TODO: Need to know the real SOLICITED_SERVICE_UUIDS_TYPE
|
|
advertisements.append(SOLICITTED_SERVICE_UUIDS_128BIT_TYPE)
|
|
advertisements.append(data)
|
|
break
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
let connectable = advertisementData[CBAdvertisementDataIsConnectable] as? Bool ?? false
|
|
let discovery = Discovery.with {
|
|
$0.uuid = peripheral.identifier.uuidString
|
|
$0.rssi = RSSI.int32Value
|
|
$0.advertisements = advertisements
|
|
$0.connectable = connectable
|
|
}
|
|
let event = try! Message.with {
|
|
$0.category = .centralDiscovered
|
|
$0.discovery = discovery
|
|
}.serializedData()
|
|
events?(event)
|
|
}
|
|
|
|
public func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
|
|
peripheral.delegate = self
|
|
peripheral.discoverServices(nil)
|
|
}
|
|
|
|
public func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
|
|
let connect = connects.removeValue(forKey: peripheral)!
|
|
let error = FlutterError(code: error!.localizedDescription, message: nil, details: nil)
|
|
connect(error)
|
|
}
|
|
|
|
public func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
|
|
if connects.keys.contains(peripheral) {
|
|
let connect = connects.removeValue(forKey: peripheral)!
|
|
let error = FlutterError(code: "GATT disconnected.", message: nil, details: nil)
|
|
connect(error)
|
|
} else {
|
|
let nativeGATT = nativeGATTs.first(where: { $1.value === peripheral })!
|
|
nativeGATTs.removeValue(forKey: nativeGATT.key)
|
|
if disconnects.keys.contains(peripheral) {
|
|
let disconnect = disconnects.removeValue(forKey: peripheral)!
|
|
disconnect(nil)
|
|
} else {
|
|
let connectionLost = GattConnectionLost.with {
|
|
$0.key = nativeGATT.key
|
|
$0.error = error!.localizedDescription
|
|
}
|
|
let event = try! Message.with {
|
|
$0.category = MessageCategory.gattConnectionLost
|
|
$0.connectionLost = connectionLost
|
|
}.serializedData()
|
|
events?(event)
|
|
}
|
|
}
|
|
}
|
|
|
|
public func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
|
|
if error == nil {
|
|
let service = peripheral.services?.first(where: { $0.characteristics == nil })
|
|
peripheral.discoverCharacteristics(nil, for: service!)
|
|
} else {
|
|
central.cancelPeripheralConnection(peripheral)
|
|
}
|
|
}
|
|
|
|
public func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
|
|
if error == nil {
|
|
let characteristic = service.characteristics?.first(where: { $0.descriptors == nil })
|
|
if characteristic != nil {
|
|
peripheral.discoverDescriptors(for: characteristic!)
|
|
}
|
|
} else {
|
|
central.cancelPeripheralConnection(peripheral)
|
|
}
|
|
}
|
|
|
|
public func peripheral(_ peripheral: CBPeripheral, didDiscoverDescriptorsFor characteristic: CBCharacteristic, error: Error?) {
|
|
if error == nil {
|
|
let nextCharacteristic = characteristic.service.characteristics?.first(where: { $0.descriptors == nil })
|
|
if nextCharacteristic == nil {
|
|
let nextService = peripheral.services?.first(where: { $0.characteristics == nil })
|
|
if nextService == nil {
|
|
var nativeServices = [String: NativeGattService]()
|
|
var messageServices = [GattService]()
|
|
for service in peripheral.services! {
|
|
var nativeCharacteristics = [String: NativeGattCharacteristic]()
|
|
var messageCharacteristics = [GattCharacteristic]()
|
|
for characteristic in service.characteristics! {
|
|
var nativeDescriptors = [String: NativeGattDescriptor]()
|
|
var messageDescriptors = [GattDescriptor]()
|
|
for descriptor in characteristic.descriptors! {
|
|
// Add native descriptor.
|
|
let nativeDescriptor = NativeGattDescriptor(descriptor)
|
|
nativeDescriptors[nativeDescriptor.key] = nativeDescriptor
|
|
// Add message descriptor.
|
|
let messageDescriptor = GattDescriptor.with {
|
|
$0.key = nativeDescriptor.key
|
|
$0.uuid = descriptor.uuid.uuidString
|
|
}
|
|
messageDescriptors.append(messageDescriptor)
|
|
}
|
|
// Add native characteristic.
|
|
let nativeCharacteristic = NativeGattCharacteristic(characteristic, nativeDescriptors)
|
|
nativeCharacteristics[nativeCharacteristic.key] = nativeCharacteristic
|
|
// Add message characteristic.
|
|
let messageCharacteristic = GattCharacteristic.with {
|
|
$0.key = nativeCharacteristic.key
|
|
$0.uuid = characteristic.uuid.uuidString
|
|
$0.canRead = characteristic.properties.contains(.read)
|
|
$0.canWrite = characteristic.properties.contains(.write)
|
|
$0.canWriteWithoutResponse = characteristic.properties.contains(.writeWithoutResponse)
|
|
$0.canNotify = characteristic.properties.contains(.notify)
|
|
$0.descriptors = messageDescriptors
|
|
}
|
|
messageCharacteristics.append(messageCharacteristic)
|
|
}
|
|
// Add native service.
|
|
let nativeService = NativeGattService(service, nativeCharacteristics)
|
|
nativeServices[nativeService.key] = nativeService
|
|
// Add message service.
|
|
let messageService = GattService.with {
|
|
$0.key = nativeService.key
|
|
$0.uuid = service.uuid.uuidString
|
|
$0.characteristics = messageCharacteristics
|
|
}
|
|
messageServices.append(messageService)
|
|
}
|
|
// Add native gatt.
|
|
let nativeGATT = NativeGATT(peripheral, nativeServices)
|
|
nativeGATTs[nativeGATT.key] = nativeGATT
|
|
// Add message gatt.
|
|
let reply = try! GATT.with {
|
|
$0.key = nativeGATT.key
|
|
$0.maximumWriteLength = peripheral.maximumWriteLength
|
|
$0.services = messageServices
|
|
}.serializedData()
|
|
let connect = connects.removeValue(forKey: peripheral)!
|
|
connect(reply)
|
|
} else {
|
|
peripheral.discoverCharacteristics(nil, for: nextService!)
|
|
}
|
|
} else {
|
|
peripheral.discoverDescriptors(for: nextCharacteristic!)
|
|
}
|
|
} else {
|
|
central.cancelPeripheralConnection(peripheral)
|
|
}
|
|
}
|
|
|
|
public func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
|
|
let read = characteristicReads.removeValue(forKey: characteristic)
|
|
if read == nil {
|
|
let nativeGATT = nativeGATTs.values.first(where: { $0.value === peripheral })!
|
|
let nativeService = nativeGATT.services.values.first(where: { $0.value === characteristic.service })!
|
|
let nativeCharacteristic = nativeService.characteristics.values.first(where: { $0.value === characteristic })!
|
|
let characteristicValue = GattCharacteristicValue.with {
|
|
$0.gattKey = nativeGATT.key
|
|
$0.serviceKey = nativeService.key
|
|
$0.key = nativeCharacteristic.key
|
|
$0.value = characteristic.value!
|
|
}
|
|
let event = try! Message.with {
|
|
$0.category = .gattCharacteristicNotify
|
|
$0.characteristicValue = characteristicValue
|
|
}.serializedData()
|
|
events?(event)
|
|
} else if error == nil {
|
|
read!(characteristic.value!)
|
|
} else {
|
|
let error = FlutterError(code: error!.localizedDescription, message: nil, details: nil)
|
|
read!(error)
|
|
}
|
|
}
|
|
|
|
public func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
|
|
let write = characteristicWrites.removeValue(forKey: characteristic)!
|
|
if error == nil {
|
|
write(nil)
|
|
} else {
|
|
let error = FlutterError(code: error!.localizedDescription, message: nil, details: nil)
|
|
write(error)
|
|
}
|
|
}
|
|
|
|
public func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
|
|
let notify = characteristicNotifies[characteristic]!
|
|
if error == nil {
|
|
notify(nil)
|
|
} else {
|
|
let error = FlutterError(code: error!.localizedDescription, message: nil, details: nil)
|
|
notify(error)
|
|
}
|
|
}
|
|
|
|
public func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor descriptor: CBDescriptor, error: Error?) {
|
|
let read = descriptorReads[descriptor]!
|
|
if error == nil {
|
|
read(descriptor.value!)
|
|
} else {
|
|
let error = FlutterError(code: error!.localizedDescription, message: nil, details: nil)
|
|
read(error)
|
|
}
|
|
}
|
|
|
|
public func peripheral(_ peripheral: CBPeripheral, didWriteValueFor descriptor: CBDescriptor, error: Error?) {
|
|
let write = descriptorWrites[descriptor]!
|
|
if error == nil {
|
|
write(nil)
|
|
} else {
|
|
let error = FlutterError(code: error!.localizedDescription, message: nil, details: nil)
|
|
write(error)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension CBCentralManager {
|
|
var messageState: BluetoothState {
|
|
switch state {
|
|
case .unknown:
|
|
return .unsupported
|
|
case .resetting:
|
|
return .unsupported
|
|
case .unsupported:
|
|
return .unsupported
|
|
case .unauthorized:
|
|
return .unsupported
|
|
case .poweredOff:
|
|
return .poweredOff
|
|
case .poweredOn:
|
|
return .poweredOn
|
|
default:
|
|
return .UNRECOGNIZED(-1)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension CBPeripheral {
|
|
var maximumWriteLength: Int32 {
|
|
let maximumWriteLengthWithResponse = maximumWriteValueLength(for: .withResponse)
|
|
let maximumWriteLengthWithoutResponse = maximumWriteValueLength(for: .withoutResponse)
|
|
// TODO: Is this two length the same value?
|
|
let maximumWriteLength = min(maximumWriteLengthWithResponse, maximumWriteLengthWithoutResponse)
|
|
return Int32(maximumWriteLength)
|
|
}
|
|
}
|