You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS WebView提取PWA的URL参数问题(集成OneSignal需求)

解决WebView内PWA单页导航时URL不更新的问题

问题原因

你的PWA属于单页应用(SPA),前端路由通过JavaScript修改history API(pushState/replaceState)或hash实现,这类路由变化不会触发WKWebView的传统导航回调(比如decidePolicyFor)——因为没有发起新的网络请求,所以原生端无法直接捕获URL的动态变化。

解决方案

通过注入JavaScript监听前端路由变化,再通过WKScriptMessageHandler将当前URL传递给原生代码,从而实时获取URL参数。

步骤1:注入路由监听脚本

修改createWebView函数,在userContentController中添加监听路由变化的WKUserScript:

func createWebView(container: UIView, WKSMH: WKScriptMessageHandler, WKND: WKNavigationDelegate, NSO: NSObject, VC: ViewController) -> WKWebView{
    
    let config = WKWebViewConfiguration()
    let userContentController = WKUserContentController()

    userContentController.add(WKSMH, name: "print")
    userContentController.add(WKSMH, name: "push-subscribe")
    userContentController.add(WKSMH, name: "push-permission-request")
    userContentController.add(WKSMH, name: "push-permission-state")
    // 添加用于接收路由变化的message handler
    userContentController.add(WKSMH, name: "routeChange")
    
    // 注入监听路由变化的JS脚本
    let routeScriptSource = """
    // 监听hash变化
    window.addEventListener('hashchange', function() {
        window.webkit.messageHandlers.routeChange.postMessage(window.location.href);
    });
    // 监听浏览器前进/后退触发的popstate事件
    window.addEventListener('popstate', function() {
        window.webkit.messageHandlers.routeChange.postMessage(window.location.href);
    });
    // 拦截pushState和replaceState方法(前端路由常用)
    const originalPushState = history.pushState;
    const originalReplaceState = history.replaceState;
    history.pushState = function() {
        originalPushState.apply(this, arguments);
        window.webkit.messageHandlers.routeChange.postMessage(window.location.href);
    };
    history.replaceState = function() {
        originalReplaceState.apply(this, arguments);
        window.webkit.messageHandlers.routeChange.postMessage(window.location.href);
    };
    // 页面初始加载时发送一次URL
    window.addEventListener('load', function() {
        window.webkit.messageHandlers.routeChange.postMessage(window.location.href);
    });
    """
    let routeScript = WKUserScript(source: routeScriptSource, injectionTime: .atDocumentStart, forMainFrameOnly: true)
    userContentController.addUserScript(routeScript)
    
    config.userContentController = userContentController
    
    // 原函数剩余代码保持不变
    if #available(iOS 14, *) {
        config.limitsNavigationsToAppBoundDomains = true;
    }
    config.preferences.javaScriptCanOpenWindowsAutomatically = true
    config.allowsInlineMediaPlayback = true
    config.preferences.setValue(true, forKey: "standalone")
    
    let webView = WKWebView(frame: calcWebviewFrame(webviewView: container, toolbarView: nil), configuration: config)
    setCustomCookie(webView: webView)
    webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    webView.isHidden = true;
    webView.navigationDelegate = WKND;
    webView.scrollView.bounces = false;
    webView.allowsBackForwardNavigationGestures = true
    webView.configuration.applicationNameForUserAgent = "Safari/604.1"
    webView.customUserAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1";    
    webView.scrollView.contentInsetAdjustmentBehavior = .never
    webView.addObserver(NSO, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: NSKeyValueObservingOptions.new, context: nil)
    
    return webView
}

步骤2:实现ScriptMessageHandler接收URL

确保你的ViewController实现WKScriptMessageHandler协议,处理routeChange消息以获取实时URL:

extension ViewController: WKScriptMessageHandler {
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        switch message.name {
        case "print":
            if let printMessage = message.body as? String {
                print(printMessage)
            }
        case "push-subscribe":
            // 保留原OneSignal相关处理逻辑
        case "push-permission-request":
            // 保留原处理逻辑
        case "push-permission-state":
            // 保留原处理逻辑
        case "routeChange":
            // 接收并处理当前URL
            if let currentUrlString = message.body as? String, let currentUrl = URL(string: currentUrlString) {
                print("当前URL: \(currentUrl)")
                // 提取URL参数(示例:获取user_id参数)
                let queryItems = URLComponents(url: currentUrl, resolvingAgainstBaseURL: false)?.queryItems
                if let userId = queryItems?.first(where: { $0.name == "user_id" })?.value {
                    // 此处可对接OneSignal逻辑,比如设置用户ID
                    print("提取到用户ID: \(userId)")
                }
            }
        default:
            break
        }
    }
}

补充说明

  • 该方案覆盖了SPA常见的路由变化场景:hashchange、popstate、pushState、replaceState,以及页面初始加载。
  • 若只需处理特定路由,可调整JS脚本中的监听逻辑。
  • 确保前端路由完成更新后再传递URL,避免获取到旧值。

内容的提问来源于stack exchange,提问作者snowsnow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 08:59:17