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? 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环境下,先只初始化AccessorySetup os_log("iOS 18环境: 延迟BleService初始化,先初始化AccessorySetup", log: logger, type: .info) let bleAccessorySetup = BleAccessorySetup.shared // 在accessoryAdded回调中延迟设置BleService bleAccessorySetup.setDeviceDiscoveredCallback { [weak self] uuid in // 确保服务已初始化 self?.ensureServicesInitialized() // 然后处理发现的设备 self?.handleDiscoveredDevice(uuid: uuid) } // 初始化发现会话 bleAccessorySetup.initializeDiscoverySession() } else { // 低于iOS 18,直接初始化所有服务 initializeAllServices() } #else // 非AccessorySetupKit环境,直接初始化所有服务 initializeAllServices() #endif } // 确保所有服务都已初始化 private func ensureServicesInitialized() { if !servicesInitialized { os_log("按需初始化BleService和相关服务", log: logger, type: .info) // 设置BLE服务回调 BleService.shared.setDelegate(self) // 设置BleService到AccessorySetup BleAccessorySetup.shared.setBleService(BleService.shared) servicesInitialized = true } } // 初始化所有服务(iOS 17及以下版本的标准行为) private func initializeAllServices() { os_log("标准初始化所有服务", log: logger, type: .info) // 设置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() 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) // 注册事件通道 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": // 确保服务已初始化 #if canImport(AccessorySetupKit) && swift(>=5.9) if #available(iOS 18.0, *) { ensureServicesInitialized() } #endif result(BleService.shared.isConnected()) case "getBatteryLevel": // 确保服务已初始化 #if canImport(AccessorySetupKit) && swift(>=5.9) if #available(iOS 18.0, *) { ensureServicesInitialized() } #endif // 获取电池信息,通过回调返回结果 if BleService.shared.getBatteryInfo() { result(0) // 通过回调返回实际值 } else { result(FlutterError(code: "UNAVAILABLE", message: "无法获取电池信息", details: nil)) } case "isCompanionSupported": // iOS 18+ 支持 AccessorySetupKit #if canImport(AccessorySetupKit) && swift(>=5.9) if #available(iOS 18.0, *) { result(true) } else { result(false) } #else result(false) #endif case "getPairedMacAddress": // iOS上禁用AccessorySetupKit,始终返回无配对设备 result(nil) case "registerBackgroundScan": // iOS上禁用,直接返回成功以避免Flutter端报错 result(true) case "unregisterBackgroundScan": // iOS上禁用,直接返回成功以避免Flutter端报错 result(true) case "clearAssociations": // iOS上禁用,直接返回成功以避免Flutter端报错 result(true) case "getDeviceVersion": // 确保服务已初始化 #if canImport(AccessorySetupKit) && swift(>=5.9) if #available(iOS 18.0, *) { ensureServicesInitialized() } #endif result(BleService.shared.getDeviceVersion()) case "getProductId": // 确保服务已初始化 #if canImport(AccessorySetupKit) && swift(>=5.9) if #available(iOS 18.0, *) { ensureServicesInitialized() } #endif result(BleService.shared.getProductId()) case "getBatteryInfo": // 确保服务已初始化 #if canImport(AccessorySetupKit) && swift(>=5.9) if #available(iOS 18.0, *) { ensureServicesInitialized() } #endif result(BleService.shared.getBatteryInfo()) case "openEncoder": // 确保服务已初始化 #if canImport(AccessorySetupKit) && swift(>=5.9) if #available(iOS 18.0, *) { ensureServicesInitialized() } #endif result(BleService.shared.openEncoder()) case "openDecoder": // 确保服务已初始化 #if canImport(AccessorySetupKit) && swift(>=5.9) if #available(iOS 18.0, *) { ensureServicesInitialized() } #endif result(BleService.shared.openDecoder()) case "openA2DPDecoder": // 确保服务已初始化 #if canImport(AccessorySetupKit) && swift(>=5.9) if #available(iOS 18.0, *) { ensureServicesInitialized() } #endif result(BleService.shared.openA2DPDecoder()) case "closeCodec": // 确保服务已初始化 #if canImport(AccessorySetupKit) && swift(>=5.9) if #available(iOS 18.0, *) { ensureServicesInitialized() } #endif result(BleService.shared.closeCodec()) case "playA2DP": // 确保服务已初始化 #if canImport(AccessorySetupKit) && swift(>=5.9) if #available(iOS 18.0, *) { ensureServicesInitialized() } #endif result(BleService.shared.playA2DP()) // iOS 18+ AccessorySetupKit相关方法 case "startDeviceAssociation": #if canImport(AccessorySetupKit) && swift(>=5.9) if #available(iOS 18.0, *) { // 从主线程调用 DispatchQueue.main.async { // 获取当前活跃的视图控制器 if let rootVC = UIApplication.shared.windows.first?.rootViewController { let topVC = self.getTopViewController(rootVC) os_log("尝试从顶层视图控制器显示设备选择器: %{public}@", log: self.logger, type: .debug, String(describing: topVC)) // 调用带UI上下文的方法 BleAccessorySetup.shared.showDevicePickerFromViewController(topVC) } else { os_log("无法获取顶层视图控制器,使用安全方法", log: self.logger, type: .error) BleAccessorySetup.shared.safeShowDevicePicker() } } result(true) } else { os_log("iOS版本低于18.0,不支持AccessorySetupKit", log: self.logger, type: .info) result(true) } #else os_log("环境不支持AccessorySetupKit,模拟成功以继续流程", log: self.logger, type: .info) result(true) #endif default: result(FlutterMethodNotImplemented) } } // MARK: - 私有方法 // AccessorySetupKit发现设备的处理 private func handleDiscoveredDevice(uuid: UUID) { // 通过AccessorySetupKit处理发现的设备 BleAccessorySetup.shared.handleDiscoveredAccessory(uuid: uuid) } 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: - 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: "发送设备信息异常") } }