Post

Replies

Boosts

Views

Created

How to implement normal loading of web content when I intercept all request header information using the Swizzling method
I am developing a web application, I hope to be able to intercept all requests in the loaded web page, match the network request I want to specifically intercept the network request and then obtain the request header information (not the user's personal privacy data interception), if the use is the method of injection, the request header information is incomplete, such as the more important "content-type" field in the request header, so I try to use Swizzling method to intercept all http and https requests, and I can now intercept all the request information. But there is still a problem with my project, when loading x.com, logging in, appearing white screen after the successful landing, and not loading to the page after the successful landing, I don't know where the reason is。 Below is my core code implementation fragment. import WebKit // MARK: - WebViewController class WebViewController: UIViewController { fileprivate var webView: WKWebView! private var schemeHandler: WWKProxyWKURLSchemeHandler! override func viewDidLoad() { super.viewDidLoad() Self.initSwizzling setupWebView() loadInitialContent() } private func setupWebView() { let config = WKWebViewConfiguration() schemeHandler = WWKProxyWKURLSchemeHandler() config.setURLSchemeHandler(self.schemeHandler, forURLScheme: "https") config.setURLSchemeHandler(self.schemeHandler, forURLScheme: "http") webView = WKWebView(frame: view.bounds, configuration: config) webView.autoresizingMask = [.flexibleWidth, .flexibleHeight] view.addSubview(webView) } private func loadInitialContent() { if let url = URL(string: "https://x.com") { let request = URLRequest(url: url,cachePolicy:.reloadIgnoringLocalCacheData,timeoutInterval: 15) webView.load(request) } } func reloadWebView() { webView?.reloadFromOrigin() } } class WWKProxyWKURLSchemeHandler: NSObject, WKURLSchemeHandler { private let lock = NSLock() private var activeTasks = [ObjectIdentifier: URLSessionDataTask]() func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) { lock.lock() defer { lock.unlock() } let taskID = ObjectIdentifier(urlSchemeTask) guard !activeTasks.keys.contains(taskID) else { print("⚠️ Task already started: \(urlSchemeTask.request.url?.absoluteString ?? "")") return } print("Intercepted URL---:",urlSchemeTask.request.url?.absoluteString ?? "") print("All requests intercepted----:",urlSchemeTask.request.allHTTPHeaderFields) let request = urlSchemeTask.request let dataTask = URLSession.shared.dataTask(with: request) { [weak self] data, response, error in guard let self = self else { return } self.lock.lock() let isActive = self.activeTasks[taskID] != nil self.lock.unlock() guard isActive else { print("🌀 Task already cancelled: \(urlSchemeTask.request.url?.absoluteString ?? "")") return } // Make sure it only handles once guard self.activeTasks.keys.contains(taskID) else { return } self.activeTasks.removeValue(forKey: taskID) DispatchQueue.main.async { if let error = error { urlSchemeTask.didFailWithError(error) print("🔴 Task failed: \(urlSchemeTask.request.url?.absoluteString ?? "") - \(error.localizedDescription)") return } guard let response = response, let data = data else { urlSchemeTask.didFailWithError(URLError(.unknown)) print("🔴 Invalid response: \(urlSchemeTask.request.url?.absoluteString ?? "")") return } urlSchemeTask.didReceive(response) urlSchemeTask.didReceive(data) urlSchemeTask.didFinish() print("🟢 Task completed: \(urlSchemeTask.request.url?.absoluteString ?? "")") } } self.activeTasks[taskID] = dataTask dataTask.resume() } func webView(_ webView: WKWebView, stop task: WKURLSchemeTask) {} private func finishTask(taskID: ObjectIdentifier ,task: WKURLSchemeTask, response: URLResponse?, data: Data?, error: Error?) { lock.lock() defer { lock.unlock() } guard activeTasks.keys.contains(taskID) else { return } activeTasks.removeValue(forKey: taskID) DispatchQueue.main.async { if let error = error { task.didFailWithError(error) print("🔴 Task failed: \(task.request.url?.absoluteString ?? "") - \(error.localizedDescription)")return} guard let response = response, let data = data else { task.didFailWithError(URLError(.unknown)) print("🔴 Invalid response: \(task.request.url?.absoluteString ?? "")") return } task.didReceive(response) task.didReceive(data) task.didFinish() print("🟢 Task completed: \(task.request.url?.absoluteString ?? "")") } } } extension WKWebView { @objc dynamic class func qm_handlesURLScheme(_ urlScheme: String) -> Bool { if urlScheme == "https" || urlScheme == "http" {return false} return self.qm_handlesURLScheme(urlScheme) } // Exchange of execution methods static func setupSwizzling() { let originalSelector = #selector(handlesURLScheme(_:)) let swizzledSelector = #selector(qm_handlesURLScheme(_:)) // Implemented by Runtime Get Method guard let originalMethod = class_getClassMethod(WKWebView.self, originalSelector), let swizzledMethod = class_getClassMethod(WKWebView.self, swizzledSelector) else { return } // Implementation of Exchange Methods method_exchangeImplementations(originalMethod, swizzledMethod) } } extension WebViewController { private static let initSwizzling: Void = { DispatchQueue.once(token: "com.webview.swizzling") { WKWebView.setupSwizzling() } }() } // GCD Once extension DispatchQueue { private static var tokens = Set<String>() class func once(token: String, block: () -> Void) { objc_sync_enter(self) defer { objc_sync_exit(self) } guard !tokens.contains(token) else { return } tokens.insert(token) block() } } extension WebViewController: WKNavigationDelegate { func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { decisionHandler(.allow) } }
1
0
89
Apr ’25