import Flutter import UIKit import CoreBluetooth import os @available(iOS 13.0, *) public class SwiftBleServicePlugin: NSObject, FlutterPlugin { // MARK: - 属性 private var methodChannel: FlutterMethodChannel private var dataEventSink: FlutterEventSink? private var statusEventSink: FlutterEventSink? // MARK: - 初始化方法 init(_ channel: FlutterMethodChannel) { self.methodChannel = channel super.init() // 设置BLE服务回调 BleService.shared.setDelegate(self) // 初始化AccessorySetup BleAccessorySetup.shared.setBleService(BleService.shared) BleAccessorySetup.shared.setDeviceDiscoveredCallback { [weak self] uuid in self?.handleDiscoveredDevice(uuid: uuid) } BleAccessorySetup.shared.initializeDiscoverySession() } // MARK: - FlutterPlugin Protocol public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel(name: "com.yunqiinnovation.ble_service", binaryMessenger: registrar.messenger()) let instance = SwiftBleServicePlugin(channel) registrar.addMethodCallDelegate(instance, channel: channel) // 注册事件通道 let dataEventChannel = FlutterEventChannel(name: "com.yunqiinnovation.ble_service/data_events", binaryMessenger: registrar.messenger()) let statusEventChannel = FlutterEventChannel(name: "com.yunqiinnovation.ble_service/status_events", binaryMessenger: registrar.messenger()) dataEventChannel.setStreamHandler(instance) statusEventChannel.setStreamHandler(instance) } public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { case "isConnected": result(BleService.shared.isConnected()) case "getBatteryLevel": // 获取电池信息,通过回调返回结果 if BleService.shared.getBatteryInfo() { result(0) // 通过回调返回实际值 } else { result(FlutterError(code: "UNAVAILABLE", message: "无法获取电池信息", details: nil)) } case "isCompanionSupported": // 检查设备是否支持iOS 18的AccessorySetupKit功能 #if canImport(AccessorySetupKit) if #available(iOS 18.0, *) { // 设备运行iOS 18+,支持AccessorySetupKit result(true) } else { // 设备运行iOS 17或更低版本 result(false) } #else // 编译时没有AccessorySetupKit框架 result(false) #endif case "getPairedMacAddress": // 获取已配对设备的标识符 #if canImport(AccessorySetupKit) if #available(iOS 18.0, *) { if let wrapper = BleAccessorySetup.shared.accessorySessionWrapper as? BleAccessorySetup.AccessorySessionWrapper { // 检查是否有配对设备 if let firstAccessory = wrapper.session.accessories.first, let btId = firstAccessory.bluetoothIdentifier { // 在iOS中,我们返回UUID字符串作为"MAC地址"的等价物 result(btId.uuidString) } else { // 无配对设备 result(nil) } } else { // 会话未初始化 result(nil) } } else { // iOS版本太低 result(nil) } #else // 编译环境不支持 result(nil) #endif case "registerBackgroundScan": // iOS通过AccessorySetupKit自动处理后台扫描,不需要单独注册 #if canImport(AccessorySetupKit) if #available(iOS 18.0, *) { // 在iOS 18+上,不需要额外操作 result(true) } else { // iOS版本太低 result(false) } #else result(false) #endif case "unregisterBackgroundScan": // iOS通过AccessorySetupKit自动处理后台扫描,不需要单独注销 result(true) case "clearAssociations": // 清除设备关联 #if canImport(AccessorySetupKit) if #available(iOS 18.0, *) { if let wrapper = BleAccessorySetup.shared.accessorySessionWrapper as? BleAccessorySetup.AccessorySessionWrapper { // 遍历所有配对设备 for accessory in wrapper.session.accessories { if let btId = accessory.bluetoothIdentifier { // 移除设备的系统处理已经集成在设置中,这里什么都不做 os_log("设备需要在系统设置中手动移除: %{public}@", log: OSLog(subsystem: "com.yunqiinnovation.ble_service", category: "BleService"), type: .info, btId.uuidString) } } } // 返回成功 result(true) } else { // iOS版本太低 result(false) } #else result(false) #endif case "getDeviceVersion": result(BleService.shared.getDeviceVersion()) case "getProductId": result(BleService.shared.getProductId()) case "getBatteryInfo": result(BleService.shared.getBatteryInfo()) case "openEncoder": result(BleService.shared.openEncoder()) case "openDecoder": result(BleService.shared.openDecoder()) case "openA2DPDecoder": result(BleService.shared.openA2DPDecoder()) case "closeCodec": result(BleService.shared.closeCodec()) case "playA2DP": result(BleService.shared.playA2DP()) // iOS 18+ AccessorySetupKit相关方法 case "startDeviceAssociation": #if canImport(AccessorySetupKit) if #available(iOS 18.0, *) { BleAccessorySetup.shared.showDevicePicker() result(true) } else { result(FlutterError(code: "UNAVAILABLE", message: "AccessorySetupKit需要iOS 18或更高版本", details: nil)) } #else result(FlutterError(code: "UNAVAILABLE", message: "编译环境不支持AccessorySetupKit", details: nil)) #endif default: result(FlutterMethodNotImplemented) } } // MARK: - 私有方法 private func handleDiscoveredDevice(uuid: UUID) { BleAccessorySetup.shared.handleDiscoveredAccessory(uuid: uuid) } private func sendEvent(_ eventSink: FlutterEventSink?, data: T, errorPrefix: String) { if let eventSink = eventSink { DispatchQueue.main.async { eventSink(data) } } } } // MARK: - FlutterStreamHandler @available(iOS 13.0, *) extension SwiftBleServicePlugin: FlutterStreamHandler { public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { if let args = arguments as? [String: Any], let type = args["type"] as? String { if type == "data" { dataEventSink = events } else if type == "status" { statusEventSink = events } } else { // 默认行为:根据注册的通道来确定事件类型 if arguments == nil { // 如果无参数,根据函数被调用的顺序确定 if dataEventSink == nil { dataEventSink = events } else { statusEventSink = events } } } return nil } public func onCancel(withArguments arguments: Any?) -> FlutterError? { if let args = arguments as? [String: Any], let type = args["type"] as? String { if type == "data" { dataEventSink = nil } else if type == "status" { statusEventSink = nil } } else { // 同时清除两个sink dataEventSink = nil statusEventSink = nil } return nil } } // MARK: - BleService.Callback @available(iOS 13.0, *) extension SwiftBleServicePlugin: BleService.Callback { func onScanResult(devices: [[String : Any]]) { sendEvent(statusEventSink, data: ["type": "scanResult", "devices": devices], errorPrefix: "发送扫描结果异常") } func onConnectionStateChanged(state: Int) { var stateStr = "Unknown" switch state { case BleConst.STATE_DISCONNECTED: stateStr = "Disconnected" case BleConst.STATE_CONNECTING: stateStr = "Connecting" case BleConst.STATE_CONNECTED: stateStr = "Connected" case BleConst.STATE_DISCONNECTING: stateStr = "Disconnecting" case BleConst.STATE_ERROR: stateStr = "Connection Error" default: stateStr = "Unknown State(\(state))" } let stateMap: [String: Any] = [ "type": "connectionStateChanged", "state": state, "stateStr": stateStr ] sendEvent(statusEventSink, data: stateMap, errorPrefix: "发送连接状态异常") } func onAudioDataReceived(data: Data) { let dataMap: [String: Any] = [ "type": "audioData", "data": data ] sendEvent(dataEventSink, data: dataMap, errorPrefix: "发送音频数据异常") } func onDeviceInfoReceived(infoType: Int, infoData: [String : Any]) { var dataMap: [String: Any] = [ "type": "deviceInfo", "infoType": infoType ] // 合并infoData for (key, value) in infoData { dataMap[key] = value } sendEvent(statusEventSink, data: dataMap, errorPrefix: "发送设备信息异常") } }