Files
bluetooth_low_energy/bluetooth_low_energy_darwin/darwin/Classes/MyPeripheralManager.swift
渐渐被你吸引 108b6a804f 6.0.0 (#74)
* 调整接口

* 临时提交

* 重构 Android 平台代码

* 临时提交

* 临时提交

* Android 6.0.0-dev.0

* 临时提交

* 实现 Windows 接口

* windows-6.0.0-dev.0

* Darwin 6.0.0-dev.0

* 临时提交

* 1

* 临时提交

* 调整接口

* windows-6.0.0-dev.1

* 临时提交

* interface-6.0.0-dev.7

* interface-6.0.0-dev.8

* 临时提交

* windows-6.0.0-dev.2

* 删除多余脚本

* interface-6.0.0-dev.9

* 临时提交

* 临时提交

* interface-6.0.0-dev.10

* android-6.0.0-dev.1

* windows-6.0.0-dev.3

* 临时提交

* interface-6.0.0-dev.11

* windows-6.0.0-dev.4

* 更新 pubspec.lock

* 1

* interface-6.0.0-dev.12

* interface-6.0.0-dev.13

* interface-6.0.0-dev.14

* 临时提交

* interface-6.0.0-dev.15

* 临时提交

* interface-6.0.0-dev.16

* android-6.0.0-dev.2

* 临时提交

* windows-6.0.0-dev.5

* 临时提交

* 临时提交

* windows-6.0.0-dev.6

* 优化注释和代码样式

* 优化代码

* 临时提交

* 实现 Dart 接口

* darwin-6.0.0-dev.0

* linux-6.0.0-dev.0

* 修复已知问题

* 修复问题

* 6.0.0-dev.0

* 修改包名

* 更新版本

* 移除原生部分

* 临时提交

* 修复问题

* 更新 pigeon 19.0.0

* 更新 README,添加迁移文档

* linux-6.0.0-dev.1

* 解析扫描回复和扩展广播

* 修复 googletest 版本警告问题

* Use centralArgs instead of addressArgs

* interface-6.0.0-dev.18

* android-6.0.0-dev.4

* linux-6.0.0-dev.2

* windows-6.0.0-dev.8

* darwin-6.0.0-dev.2

* 6.0.0-dev.1

* Update LICENSE

* clang-format

* Combine ADV_IND and SCAN_RES

* TEMP commit: update exampe

* Adjust advertisement combine logic

* Implement `MyPeripheralMananger` on Windows

* Added NuGet auto download and scan for names on peripheral (#67)

* fetch nuget using other technique

* move FetchContent to right location in CMakeLists.txt

* also added hash for googletest

---------

Co-authored-by: Kevin De Keyser <kevin@dekeyser.ch>

* Fix errors.

* Check BluetoothAdapter role supported state and implement PeripheralManager on Flutter side.

* Sort code

* Fix known errors

* interface-6.0.0-dev.19

* windows-6.0.0-dev.9

* Optimize example

* android-6.0.0-dev.5

* Optimize the Adverrtisement BottomSheet.

* linux-6.0.0-dev.3

* Update dependency

* Fix example errors.

* Temp commit.

* darwin-6.0.0-dev.3

* 6.0.0-dev.2

* Update README.md

* 6.0.0

* darwin-6.0.0-dev.4

* android-6.0.0-dev.6

* 6.0.0-dev.3

* Update docs.

* interface-6.0.0

* android-6.0.0

* darwin-6.0.0

* linux-6.0.0

* windows-6.0.0

* 6.0.0

* Update dependency

---------

Co-authored-by: Kevin De Keyser <dekeyser.kevin97@gmail.com>
Co-authored-by: Kevin De Keyser <kevin@dekeyser.ch>
2024-06-04 00:44:39 +08:00

362 lines
14 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 {
private let mAPI: MyPeripheralManagerFlutterAPI
private let mPeripheralManager: CBPeripheralManager
private lazy var mPeripheralManagerDelegate = MyPeripheralManagerDelegate(peripheralManager: self)
private var mServicesArgs: [Int: MyMutableGATTServiceArgs]
private var mCharacteristicsArgs: [Int: MyMutableGATTCharacteristicArgs]
private var mDescriptorsArgs: [Int: MyMutableGATTDescriptorArgs]
private var mCentrals: [String: CBCentral]
private var mServices: [Int64: CBMutableService]
private var mCharacteristics: [Int64: CBMutableCharacteristic]
private var mDescriptors: [Int64: CBMutableDescriptor]
private var mRequests: [Int64: CBATTRequest]
private var mAddServiceCompletion: ((Result<Void, Error>) -> Void)?
private var mStartAdvertisingCompletion: ((Result<Void, Error>) -> Void)?
init(messenger: FlutterBinaryMessenger) {
mAPI = MyPeripheralManagerFlutterAPI(binaryMessenger: messenger)
mPeripheralManager = CBPeripheralManager()
mServicesArgs = [:]
mCharacteristicsArgs = [:]
mDescriptorsArgs = [:]
mCentrals = [:]
mServices = [:]
mCharacteristics = [:]
mDescriptors = [:]
mRequests = [:]
mAddServiceCompletion = nil
mStartAdvertisingCompletion = nil
}
func initialize() throws {
if(mPeripheralManager.isAdvertising) {
mPeripheralManager.stopAdvertising()
}
mServicesArgs.removeAll()
mCharacteristicsArgs.removeAll()
mDescriptorsArgs.removeAll()
mCentrals.removeAll()
mServices.removeAll()
mCharacteristics.removeAll()
mDescriptors.removeAll()
mRequests.removeAll()
mAddServiceCompletion = nil
mStartAdvertisingCompletion = nil
mPeripheralManager.delegate = mPeripheralManagerDelegate
}
func getState() throws -> MyBluetoothLowEnergyStateArgs {
let state = mPeripheralManager.state
let stateArgs = state.toArgs()
return stateArgs
}
func showAppSettings(completion: @escaping (Result<Void, any Error>) -> Void) {
#if os(iOS)
do {
guard let url = URL(string: UIApplication.openSettingsURLString) else {
throw MyError.illegalArgument
}
UIApplication.shared.open(url) { success in
if (success) {
completion(.success(()))
} else {
completion(.failure(MyError.unknown))
}
}
} catch {
completion(.failure(error))
}
#else
completion(.failure(MyError.unsupported))
#endif
}
func addService(serviceArgs: MyMutableGATTServiceArgs, completion: @escaping (Result<Void, Error>) -> Void) {
do {
let service = try addServiceArgs(serviceArgs)
mPeripheralManager.add(service)
mAddServiceCompletion = completion
} catch {
completion(.failure(error))
}
}
func removeService(hashCodeArgs: Int64) throws {
guard let service = mServices[hashCodeArgs] else {
throw MyError.illegalArgument
}
guard let serviceArgs = mServicesArgs[service.hash] else {
throw MyError.illegalArgument
}
mPeripheralManager.remove(service)
try removeServiceArgs(serviceArgs)
}
func removeAllServices() throws {
mPeripheralManager.removeAllServices()
mServices.removeAll()
mCharacteristics.removeAll()
mDescriptors.removeAll()
mServicesArgs.removeAll()
mCharacteristicsArgs.removeAll()
mDescriptors.removeAll()
}
func startAdvertising(advertisementArgs: MyAdvertisementArgs, completion: @escaping (Result<Void, Error>) -> Void) {
let advertisement = advertisementArgs.toAdvertisement()
mPeripheralManager.startAdvertising(advertisement)
mStartAdvertisingCompletion = completion
}
func stopAdvertising() throws {
mPeripheralManager.stopAdvertising()
}
func getMaximumNotifyLength(uuidArgs: String) throws -> Int64 {
guard let central = mCentrals[uuidArgs] else {
throw MyError.illegalArgument
}
let maximumNotifyLength = central.maximumUpdateValueLength
let maximumNotifyLengthArgs = maximumNotifyLength.toInt64()
return maximumNotifyLengthArgs
}
func respond(hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData?, errorArgs: MyATTErrorArgs) throws {
guard let request = mRequests.removeValue(forKey: hashCodeArgs) else {
throw MyError.illegalArgument
}
if valueArgs != nil {
request.value = valueArgs!.data
}
let error = errorArgs.toError()
mPeripheralManager.respond(to: request, withResult: error)
}
func updateValue(hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, uuidsArgs: [String]?) throws -> Bool {
let centrals = try uuidsArgs?.map { uuidArgs in
guard let central = self.mCentrals[uuidArgs] else {
throw MyError.illegalArgument
}
return central
}
guard let characteristic = mCharacteristics[hashCodeArgs] else {
throw MyError.illegalArgument
}
let value = valueArgs.data
let updated = mPeripheralManager.updateValue(value, for: characteristic, onSubscribedCentrals: centrals)
return updated
}
func didUpdateState(peripheral: CBPeripheralManager) {
let state = peripheral.state
let stateArgs = state.toArgs()
mAPI.onStateChanged(stateArgs: stateArgs) { _ in }
}
func didAdd(peripheral: CBPeripheralManager, service: CBService, error: Error?) {
guard let completion = mAddServiceCompletion else {
return
}
mAddServiceCompletion = nil
if error == nil {
completion(.success(()))
} else {
completion(.failure(error!))
}
}
func didStartAdvertising(peripheral: CBPeripheralManager, error: Error?) {
guard let completion = mStartAdvertisingCompletion else {
return
}
mStartAdvertisingCompletion = nil
if error == nil {
completion(.success(()))
} else {
completion(.failure(error!))
}
}
func didReceiveRead(peripheral: CBPeripheralManager, request: CBATTRequest) {
let hashCodeArgs = request.hash.toInt64()
let central = request.central
let centralArgs = central.toArgs()
mCentrals[centralArgs.uuidArgs] = central
let characteristic = request.characteristic
guard let characteristicArgs = mCharacteristicsArgs[characteristic.hash] else {
mPeripheralManager.respond(to: request, withResult: .attributeNotFound)
return
}
let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
let value = request.value
let valueArgs = value == nil ? nil : FlutterStandardTypedData(bytes: value!)
let offsetArgs = request.offset.toInt64()
let requestArgs = MyATTRequestArgs(hashCodeArgs: hashCodeArgs, centralArgs: centralArgs, characteristicHashCodeArgs: characteristicHashCodeArgs, valueArgs: valueArgs, offsetArgs: offsetArgs)
mRequests[hashCodeArgs] = request
mAPI.didReceiveRead(requestArgs: requestArgs) { _ in }
}
func didReceiveWrite(peripheral: CBPeripheralManager, requests: [CBATTRequest]) {
var requestsArgs = [MyATTRequestArgs]()
for request in requests {
let hashCodeArgs = request.hash.toInt64()
let central = request.central
let centralArgs = central.toArgs()
mCentrals[centralArgs.uuidArgs] = central
let characteristic = request.characteristic
guard let characteristicArgs = mCharacteristicsArgs[characteristic.hash] else {
mPeripheralManager.respond(to: request, withResult: .attributeNotFound)
return
}
let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
let value = request.value
let valueArgs = value == nil ? nil : FlutterStandardTypedData(bytes: value!)
let offsetArgs = request.offset.toInt64()
let requestArgs = MyATTRequestArgs(hashCodeArgs: hashCodeArgs, centralArgs: centralArgs, characteristicHashCodeArgs: characteristicHashCodeArgs, valueArgs: valueArgs, offsetArgs: offsetArgs)
requestsArgs.append(requestArgs)
}
guard let request = requests.first else {
return
}
guard let requestArgs = requestsArgs.first else {
return
}
self.mRequests[requestArgs.hashCodeArgs] = request
mAPI.didReceiveWrite(requestsArgs: requestsArgs) { _ in }
}
func didSubscribeTo(peripheral: CBPeripheralManager, central: CBCentral, characteristic: CBCharacteristic) {
let centralArgs = central.toArgs()
mCentrals[centralArgs.uuidArgs] = central
let hashCode = characteristic.hash
guard let characteristicArgs = mCharacteristicsArgs[hashCode] else {
return
}
let hashCodeArgs = characteristicArgs.hashCodeArgs
let stateArgs = true
mAPI.onCharacteristicNotifyStateChanged(centralArgs: centralArgs, hashCodeArgs: hashCodeArgs, stateArgs: stateArgs) { _ in }
}
func didUnsubscribeFrom(peripheral: CBPeripheralManager, central: CBCentral, characteristic: CBCharacteristic) {
let centralArgs = central.toArgs()
mCentrals[centralArgs.uuidArgs] = central
let hashCode = characteristic.hash
guard let characteristicArgs = mCharacteristicsArgs[hashCode] else {
return
}
let hashCodeArgs = characteristicArgs.hashCodeArgs
let stateArgs = false
mAPI.onCharacteristicNotifyStateChanged(centralArgs: centralArgs, hashCodeArgs: hashCodeArgs, stateArgs: stateArgs) { _ in }
}
func isReadyToUpdateSubscribers(peripheral: CBPeripheralManager) {
mAPI.isReady() { _ in }
}
private func addServiceArgs(_ serviceArgs: MyMutableGATTServiceArgs) throws -> CBMutableService {
let service = serviceArgs.toService()
mServicesArgs[service.hash] = serviceArgs
mServices[serviceArgs.hashCodeArgs] = service
var includedServices = [CBService]()
let includedServicesArgs = serviceArgs.includedServicesArgs
for args in includedServicesArgs {
guard let includedServiceArgs = args else {
throw MyError.illegalArgument
}
let includedService = try addServiceArgs(includedServiceArgs)
self.mServicesArgs[includedService.hash] = includedServiceArgs
self.mServices[includedServiceArgs.hashCodeArgs] = includedService
includedServices.append(includedService)
}
service.includedServices = includedServices
var characteristics = [CBMutableCharacteristic]()
let characteristicsArgs = serviceArgs.characteristicsArgs
for args in characteristicsArgs {
guard let characteristicArgs = args else {
throw MyError.illegalArgument
}
let characteristic = characteristicArgs.toCharacteristic()
self.mCharacteristicsArgs[characteristic.hash] = characteristicArgs
self.mCharacteristics[characteristicArgs.hashCodeArgs] = characteristic
characteristics.append(characteristic)
var descriptors = [CBMutableDescriptor]()
let descriptorsArgs = characteristicArgs.descriptorsArgs
for args in descriptorsArgs {
guard let descriptorArgs = args else {
continue
}
let descriptor = descriptorArgs.toDescriptor()
self.mDescriptorsArgs[descriptor.hash] = descriptorArgs
self.mDescriptors[descriptorArgs.hashCodeArgs] = descriptor
descriptors.append(descriptor)
}
characteristic.descriptors = descriptors
}
service.characteristics = characteristics
return service
}
private func removeServiceArgs(_ serviceArgs: MyMutableGATTServiceArgs) throws {
for args in serviceArgs.includedServicesArgs {
guard let includedServiceArgs = args else {
throw MyError.illegalArgument
}
try removeServiceArgs(includedServiceArgs)
}
for args in serviceArgs.characteristicsArgs {
guard let characteristicArgs = args else {
throw MyError.illegalArgument
}
for args in characteristicArgs.descriptorsArgs {
guard let descriptorArgs = args else {
throw MyError.illegalArgument
}
guard let descriptor = mDescriptors.removeValue(forKey: descriptorArgs.hashCodeArgs) else {
throw MyError.illegalArgument
}
mDescriptorsArgs.removeValue(forKey: descriptor.hash)
}
guard let characteristic = mCharacteristics.removeValue(forKey: characteristicArgs.hashCodeArgs) else {
throw MyError.illegalArgument
}
mCharacteristicsArgs.removeValue(forKey: characteristic.hash)
}
guard let service = mServices.removeValue(forKey: serviceArgs.hashCodeArgs) else {
throw MyError.illegalArgument
}
mServicesArgs.removeValue(forKey: service.hash)
}
}