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. 启动流程重新设计