import Foundation import XuqmCoreSDK #if canImport(UIKit) import UIKit #endif /// JSBridge 标准处理器 — 提供 xuqm.* 命名空间的标准桥接方法。 /// 类型在所有平台可见;UIKit 依赖的实现仅在 iOS 上编译。 @MainActor public final class XWebViewStandardHandlers { #if canImport(UIKit) private weak var viewController: UIViewController? private var onClose: (() -> Void)? public init(viewController: UIViewController? = nil, onClose: (() -> Void)? = nil) { self.viewController = viewController self.onClose = onClose } #else public init() {} #endif // MARK: - Message Dispatch public func handleMessage(_ message: [String: Any]) -> Bool { #if canImport(UIKit) guard let action = message["action"] as? String else { return false } let callbackId = message["callbackId"] as? String switch action { case "xuqm.getDeviceInfo": handleGetDeviceInfo(callbackId: callbackId); return true case "xuqm.getToken": handleGetToken(callbackId: callbackId); return true case "xuqm.getUserInfo": handleGetUserInfo(callbackId: callbackId); return true case "xuqm.closeWebView": onClose?(); return true case "xuqm.showToast": handleShowToast(message: message); return true default: return false } #else return false #endif } // MARK: - Handlers (UIKit only) #if canImport(UIKit) private func handleGetDeviceInfo(callbackId: String?) { guard let callbackId else { return } let device = UIDevice.current callCallback(callbackId, data: [ "model": device.model, "osVersion": "iOS \(device.systemVersion)", "platform": "IOS", "deviceId": device.identifierForVendor?.uuidString ?? "", ]) } private func handleGetToken(callbackId: String?) { guard let callbackId else { return } callCallback(callbackId, data: ["token": XuqmSDK.shared.token ?? ""]) } private func handleGetUserInfo(callbackId: String?) { guard let callbackId else { return } // userId is @MainActor; we post empty now — override in app layer if needed callCallback(callbackId, data: ["userId": XuqmSDK.shared.userId ?? ""]) } private func handleShowToast(message: [String: Any]) { guard let msg = message["message"] as? String else { return } DispatchQueue.main.async { [weak self] in guard let vc = self?.viewController else { return } let alert = UIAlertController(title: nil, message: msg, preferredStyle: .alert) vc.present(alert, animated: true) DispatchQueue.main.asyncAfter(deadline: .now() + 2) { alert.dismiss(animated: true) } } } private func callCallback(_ callbackId: String, data: [String: Any]) { guard let jsonData = try? JSONSerialization.data(withJSONObject: data), let jsonString = String(data: jsonData, encoding: .utf8) else { return } let js = "window[\"\(callbackId)\"](\(jsonString))" NotificationCenter.default.post(name: .xWebViewCallJs, object: nil, userInfo: ["js": js]) } #endif // MARK: - Injected JS (all platforms) public nonisolated static var injectedJavaScript: String { """ (function() { window.xuqm = window.xuqm || {}; window.xuqm.getDeviceInfo = function(callback) { var callbackId = 'cb_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); window[callbackId] = function(data) { if (callback) callback(data); delete window[callbackId]; }; window.ReactNativeWebView.postMessage(JSON.stringify({ action: 'xuqm.getDeviceInfo', callbackId: callbackId })); }; window.xuqm.getToken = function(callback) { var callbackId = 'cb_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); window[callbackId] = function(data) { if (callback) callback(data.token); delete window[callbackId]; }; window.ReactNativeWebView.postMessage(JSON.stringify({ action: 'xuqm.getToken', callbackId: callbackId })); }; window.xuqm.getUserInfo = function(callback) { var callbackId = 'cb_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); window[callbackId] = function(data) { if (callback) callback(data); delete window[callbackId]; }; window.ReactNativeWebView.postMessage(JSON.stringify({ action: 'xuqm.getUserInfo', callbackId: callbackId })); }; window.xuqm.closeWebView = function() { window.ReactNativeWebView.postMessage(JSON.stringify({ action: 'xuqm.closeWebView' })); }; window.xuqm.showToast = function(message) { window.ReactNativeWebView.postMessage(JSON.stringify({ action: 'xuqm.showToast', message: message })); }; })(); true; """ } } // MARK: - Notification Name (UIKit only, used by WKWebView coordinator) #if canImport(UIKit) extension Notification.Name { static let xWebViewCallJs = Notification.Name("xWebViewCallJs") } #endif