import Flutter import UIKit import CoreBluetooth import os @available(iOS 13.0, *) public class SwiftBleServicePlugin: NSObject, FlutterPlugin { // MARK: - 属性 private var methodChannel: FlutterMethodChannel internal var dataEventSink: FlutterEventSink? internal var statusEventSink: FlutterEventSink? private let logger = OSLog(subsystem: "com.yunqiinnovation.ble_service", category: "BleServicePlugin") // 标记是否已初始化所有服务 private var servicesInitialized = false // MARK: - 初始化方法 init(_ channel: FlutterMethodChannel) { self.methodChannel = channel super.init() #if canImport(AccessorySetupKit) && swift(>=5.9) if #available(iOS 18.0, *) { // iOS 18环境下,只设置回调但不主动初始化会话 let bleAccessorySetup = BleAccessorySetup.shared // 在accessoryAdded回调中延迟设置BleService bleAccessorySetup.setDeviceDiscoveredCallback { [weak self] uuid in // 确保服务已初始化 self?.ensureServicesInitialized() // 然后处理发现的设备 self?.handleDiscoveredDevice(uuid: uuid) } // 注释掉自动初始化,改为等待用户主动调用startDeviceAssociation // bleAccessorySetup.initializeSession() os_log("iOS 18环境:已设置设备发现回调,等待用户主动调用关联", log: logger, type: .info) } else { // 低于iOS 18,直接初始化所有服务 initializeAllServices() } #else // 非AccessorySetupKit环境,直接初始化所有服务 initializeAllServices() #endif } // 确保所有服务都已初始化 private func ensureServicesInitialized() { if !servicesInitialized { // 设置BLE服务回调 BleService.shared.addCallback(self) // 设置BleService到AccessorySetup BleAccessorySetup.shared.setBleService(BleService.shared) servicesInitialized = true } } // 初始化所有服务(iOS 17及以下版本的标准行为) private func initializeAllServices() { // 设置BLE服务回调 BleService.shared.addCallback(self) // 初始化AccessorySetup,但不自动开始会话 BleAccessorySetup.shared.setBleService(BleService.shared) BleAccessorySetup.shared.setDeviceDiscoveredCallback { [weak self] uuid in self?.handleDiscoveredDevice(uuid: uuid) } // 移除自动初始化,统一由startDeviceAssociation方法触发 // BleAccessorySetup.shared.initializeSession() os_log("非iOS 18环境:已设置BLE服务但不自动初始化会话", log: logger, type: .info) servicesInitialized = true } // 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) // 注册事件通道 - 为每个通道创建独立的handler 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()) // 创建独立的StreamHandler let dataHandler = BleDataStreamHandler(instance) let statusHandler = BleStatusStreamHandler(instance) dataEventChannel.setStreamHandler(dataHandler) statusEventChannel.setStreamHandler(statusHandler) } public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { // 检查是否需要初始化服务 checkAndInitializeServices() 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": #if canImport(AccessorySetupKit) && swift(>=5.9) if #available(iOS 18.0, *) { result(true) } else { result(false) } #else result(false) #endif case "getPairedMacAddress": #if canImport(AccessorySetupKit) && swift(>=5.9) if #available(iOS 18.0, *) { let pairedUUID = BleAccessorySetup.shared.getPairedMacAddress() result(pairedUUID) } else { result(nil) } #else result(nil) #endif case "isBleConnected": result(BleService.shared.isConnected()) case "registerBackgroundScan", "unregisterBackgroundScan": // iOS上禁用,直接返回成功以避免Flutter端报错 result(true) 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 "clearAssociations": // 清除设备配对关系 #if canImport(AccessorySetupKit) && swift(>=5.9) if #available(iOS 18.0, *) { os_log("清除设备配对关系", log: logger, type: .info) BleAccessorySetup.shared.clearAssociations() result(true) } else { result(true) } #else result(true) #endif case "startDeviceAssociation": // 用户主动触发设备关联流程 #if canImport(AccessorySetupKit) && swift(>=5.9) if #available(iOS 18.0, *) { os_log("用户主动触发设备关联流程", log: logger, type: .info) // 确保服务已初始化 ensureServicesInitialized() // 重新初始化会话 BleAccessorySetup.shared.initializeSession() // 返回成功,实际配对结果会通过回调通知 result(true) } else { os_log("当前iOS版本不支持AccessorySetupKit", log: logger, type: .info) result(false) } #else os_log("当前环境不支持AccessorySetupKit", log: logger, type: .info) result(false) #endif default: result(FlutterMethodNotImplemented) } } // MARK: - 私有方法 // 检查并按需初始化服务 private func checkAndInitializeServices() { #if canImport(AccessorySetupKit) && swift(>=5.9) if #available(iOS 18.0, *) { ensureServicesInitialized() } #endif } // AccessorySetupKit发现设备的处理 private func handleDiscoveredDevice(uuid: UUID) { // BleAccessorySetup内部已进行连接,此处不再重复调用 // os_log("收到设备发现回调: %{public}@", log: logger, type: .info, uuid.uuidString) // 可以在这里添加设备记录或其他操作 } private func sendEvent(_ eventSink: FlutterEventSink?, data: T, errorPrefix: String) { if let eventSink = eventSink { DispatchQueue.main.async { eventSink(data) } } } // 获取顶层视图控制器的辅助方法 private func getTopViewController(_ viewController: UIViewController) -> UIViewController { if let presented = viewController.presentedViewController { return getTopViewController(presented) } if let tabBarController = viewController as? UITabBarController, let selected = tabBarController.selectedViewController { return getTopViewController(selected) } if let navigationController = viewController as? UINavigationController, let visibleViewController = navigationController.visibleViewController { return getTopViewController(visibleViewController) } return viewController } } // MARK: - BleService.Callback @available(iOS 13.0, *) extension SwiftBleServicePlugin: BleService.Callback { public func onScanResult(devices: [[String: Any]]) { if let eventSink = statusEventSink { DispatchQueue.main.async { eventSink([ "type": "scanResult", "devices": devices ]) } } } public func onConnectionStateChanged(state: Int) { if let eventSink = statusEventSink { 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))" } DispatchQueue.main.async { eventSink([ "type": "connectionStateChanged", "state": state, "stateStr": stateStr ]) } } } public func onAudioDataReceived(data: Data) { if let eventSink = dataEventSink { DispatchQueue.main.async { eventSink([ "type": "audioData", "data": FlutterStandardTypedData(bytes: data) ]) } } } public func onWakeupSignalReceived() { if let eventSink = statusEventSink { DispatchQueue.main.async { eventSink([ "type": "wakeup" ]) } } } public func onDeviceInfoReceived(infoType: Int, infoData: [String: Any]) { if let eventSink = statusEventSink { var resultMap = infoData resultMap["type"] = "deviceInfo" resultMap["result"] = infoType DispatchQueue.main.async { eventSink(resultMap) } } } } // MARK: - 独立的StreamHandler类 @available(iOS 13.0, *) class BleDataStreamHandler: NSObject, FlutterStreamHandler { private weak var plugin: SwiftBleServicePlugin? init(_ plugin: SwiftBleServicePlugin) { self.plugin = plugin } func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { plugin?.dataEventSink = events return nil } func onCancel(withArguments arguments: Any?) -> FlutterError? { plugin?.dataEventSink = nil return nil } } @available(iOS 13.0, *) class BleStatusStreamHandler: NSObject, FlutterStreamHandler { private weak var plugin: SwiftBleServicePlugin? init(_ plugin: SwiftBleServicePlugin) { self.plugin = plugin } func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { plugin?.statusEventSink = events return nil } func onCancel(withArguments arguments: Any?) -> FlutterError? { plugin?.statusEventSink = nil return nil } }