From 33574a1cc247fdf27ecca86ead509c9d3bbccf2c Mon Sep 17 00:00:00 2001 From: joywayer Date: Mon, 22 Jun 2026 07:33:33 +0800 Subject: [PATCH] =?UTF-8?q?Design=20=C2=A73.7=EF=BC=9A=E8=A1=A5=20WKUIDele?= =?UTF-8?q?gate=20alert=20/=20confirm=202=20=E9=A1=B9=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 覆盖契约 §4.3 H5 alert(msg) / confirm(msg) 触发原生弹窗的硬约束。 WKWebView 默认行为是直接吞掉 H5 的 alert/confirm 不弹任何窗口,必须实现 WKUIDelegate 才有响应。不实现的代价:H5 业务里 alert("登录失败") 之类的提示用户完全看不见,业务哑火无可视化报错。 §3.7 实现骨架: - runJavaScriptAlertPanelWithMessage → UIAlertController .alert, 标题 appDisplayName,单"确定"按钮,completionHandler() 必须调一次 - runJavaScriptConfirmPanelWithMessage → "取消"/"确定" 两按钮, completionHandler(false/true) 必须调一次 - 接入点:bridgedWebView.webView.uiDelegate = self 在 viewDidLoad 与 navigationDelegate 一起设置 §3.7.1 与 msext 4 维度差异表(标题来源 / 按钮顺序 / 样式 / 并发)+ 明示"未实现导致的现象"提醒,方便 1.17 真机联调时排查。 至此契约 §3 + §附录 A + §4.3 共 51 项接口(22 异步 handler + 15 反向 callback + 3 弹层 polyfill + 9 同步 getter polyfill + 2 WKUIDelegate)的 Design 实现 骨架全部覆盖完整。 Co-Authored-By: Claude Opus 4.7 --- docs/H5-Native-Implementation-Design.md | 72 +++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/docs/H5-Native-Implementation-Design.md b/docs/H5-Native-Implementation-Design.md index 33afe6e..6852cff 100644 --- a/docs/H5-Native-Implementation-Design.md +++ b/docs/H5-Native-Implementation-Design.md @@ -827,6 +827,78 @@ extension WebContainerViewController: WKNavigationDelegate { - **Sentry 上报**:每次崩溃都打 breadcrumb,持续高崩溃率应该触发告警 - **大厅 / 子游戏 / 弹层都受这条覆盖**,因为它们都继承自 WebContainerViewController +### 3.7 WKUIDelegate alert / confirm(契约 §4.3) + +H5 用 `alert(msg)` / `confirm(msg)` 触发原生弹窗,WKWebView 默认行为是**直接吞掉不弹**——必须在容器 VC 上实现 `WKUIDelegate` 才能让 H5 业务的提示信息正常显示。这是契约 §4.3 的硬约束,**漏掉直接导致 H5 任何 alert/confirm 无响应**。 + +涉及 2 项接口(不在 §3.1 主表,单列): +1. `runJavaScriptAlertPanelWithMessage` — H5 调 `alert(msg)` +2. `runJavaScriptConfirmPanelWithMessage` — H5 调 `confirm(msg)` 期待返回 true/false + +```swift +// Source/WebView/WebContainerViewController.swift(追加 WKUIDelegate 扩展) +extension WebContainerViewController: WKUIDelegate { + + // MARK: - 【契约 §4.3】 H5 alert(msg) + // + // 标题:BundleConfig.shared.appDisplayName(msext 用 gamehallname 常量) + // 按钮:单"确定" + // completionHandler 必须调一次(否则 WKWebView 永久阻塞同一帧的 JS 执行) + + public func webView(_ webView: WKWebView, + runJavaScriptAlertPanelWithMessage message: String, + initiatedByFrame frame: WKFrameInfo, + completionHandler: @escaping () -> Void) { + let alert = UIAlertController( + title: appDisplayName, + message: message, + preferredStyle: .alert + ) + alert.addAction(UIAlertAction(title: "确定", style: .default) { _ in + completionHandler() + }) + present(alert, animated: true) + } + + // MARK: - 【契约 §4.3】 H5 confirm(msg) + // + // 标题:同 alert + // 按钮:确定 / 取消,分别 completionHandler(true) / completionHandler(false) + // completionHandler 必须以 bool 调一次 + + public func webView(_ webView: WKWebView, + runJavaScriptConfirmPanelWithMessage message: String, + initiatedByFrame frame: WKFrameInfo, + completionHandler: @escaping (Bool) -> Void) { + let alert = UIAlertController( + title: appDisplayName, + message: message, + preferredStyle: .alert + ) + alert.addAction(UIAlertAction(title: "取消", style: .cancel) { _ in + completionHandler(false) + }) + alert.addAction(UIAlertAction(title: "确定", style: .default) { _ in + completionHandler(true) + }) + present(alert, animated: true) + } +} +``` + +接入:`bridgedWebView.webView.uiDelegate = self` 在 `viewDidLoad` 跟 `navigationDelegate` 一起设置。 + +#### 3.7.1 与原 msext 的差异 + +| 维度 | msext 现状 | 新外壳决策 | +|------|----------|---------| +| 标题文案 | `gamehallname` PCH 常量 | `appDisplayName`(CFBundleDisplayName / CFBundleName,自包含) | +| confirm 按钮顺序 | "取消" 在左 / "确定" 在右 | 严格保持(iOS UIAlertController 默认 cancel 在左 + default 在右) | +| 弹窗样式 | UIAlertController + .alert | 同款 | +| 多帧并发 | msext 偶发"alert 串"叠加,体验略乱 | 当前同时只允许一个 alert(present 队列);如未来 H5 业务有真并发,再加 pendingAlertQueue | + +> ⚠️ 不实现这两个方法的代价:H5 业务里 `alert("登录失败")` 之类的提示**用户完全看不见**,业务流程哑火无可视化报错。是 Phase 1.17 真机联调时最容易暴露的缺漏点,因此 §10 验收清单单列。 + --- ## 4. 启动流程重新设计