Files
youle_app_ios_v2/ylgamehall/Source/WebView/BridgedWebView.swift
T
joywayerandClaude Opus 4.7 e50dfb75af 子游戏跳转链路修复 + H5 音频统一可用
修复后子游戏 SwitchOverGameData 跳转完全可用,行为对齐 daoqi msext:

【zip URL 解析】H5 字段 gamedownloadurl 实为 gameid(msext gameController.m:544
注释"游戏ID")。RemoteConfigClient 新增 lastParsed 缓存 + current() getter;
SubGameViewController.resolveBoot 做两次 VersionResolver.resolve:
  - lobby 视角(gameId=bc.gameId)→ ResolvedVersion 传 AppDataWriter 算
    app_appversion 审核标志(msext result_state 等价)
  - sub-game 视角(gameId=H5 传的 gameid)→ 真实 game_zip 下载 URL

之前 SubGameDownloader 直接把 token 当 URL 用,URLSession 报 -1002
unsupported URL;AppDataWriter resolvedVersion 始终 nil 导致子游戏 app_appversion
永远 0、丢失审核切换能力。

【push 前停大厅背景音】AppCoordinator.showSubGame 节流通过后、push 前调
AudioPlayer.stopAllBackground(msext NewRootVC.m:549-552 等价)。

【H5 音频】
- AVAudioSession.setCategory(.playback) 在 AppDelegate 启动期设置,让原生
  + WKWebView 内 <audio> 都不受静音键影响(msext RootVC.m:1302 等多处等价)
- WKWebViewConfiguration.mediaTypesRequiringUserActionForPlayback = [],子游戏
  push 后立即 loadFileURL 自播背景音不再被 WebKit 静默拦截
- LocalAudioHandler.register 增 assetsRoot 参数,srcIsloop 按容器各自的 H5
  根目录拼 assets/wav/{src}(msext gameController.m:364 等价,大厅 / 子游戏
  音频文件在各自 zip 内不重叠)。修前所有路径都指向大厅根,子游戏读不到自己的
  音频文件,AVAudioPlayer 报 OSStatus 2003334207 (wht?
  kAudioFileUnsupportedFileTypeError)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-23 21:33:46 +08:00

99 lines
4.5 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// BridgedWebView.swift
// ylgamehall
//
// WKWebView + WebViewJavascriptBridge.js 注入 + BridgeBus 一体封装。
// 详见 docs/H5-Native-Implementation-Design.md §3.3 / 契约 §4.1。
//
import UIKit
import WebKit
/// 内嵌 H5 的 WebView 组件,已挂好桥与必要配置。WebContainerViewControllerPhase 1.10
/// 通过把本 view 嵌入自己的内容区,再加上 16:9 letterbox / 生命周期管理。
@MainActor
public final class BridgedWebView: UIView {
public let webView: WKWebView
public let bridge: BridgeBus
public init() {
// ── WKWebViewConfiguration(契约 §4.1)─────────────────
let configuration = WKWebViewConfiguration()
configuration.defaultWebpagePreferences.allowsContentJavaScript = true
configuration.preferences.javaScriptCanOpenWindowsAutomatically = false
configuration.preferences.minimumFontSize = 10
configuration.allowsInlineMediaPlayback = true
// H5 背景音自播:默认 .all 要求该 WKWebView 实例有 user gesture 才放行媒体,
// 大厅页用户点击过没问题;子游戏 push 后立即 loadFileURL 自播背景音被静默拦截,
// 表现为"子游戏 webview 没声音"。设空集让所有媒体都不要求 user gesture。
configuration.mediaTypesRequiringUserActionForPlayback = []
configuration.processPool = SharedProcessPool.shared
// ── 在 documentStart 注入 WebViewJavascriptBridge.js ─────
// .atDocumentStart 保证 H5 业务代码运行时 window.WebViewJavascriptBridge 已就绪
if let url = Bundle.main.url(forResource: "WebViewJavascriptBridge",
withExtension: "js"),
let source = try? String(contentsOf: url, encoding: .utf8) {
let userScript = WKUserScript(
source: source,
injectionTime: .atDocumentStart,
forMainFrameOnly: true
)
configuration.userContentController.addUserScript(userScript)
} else {
// 编译期保证文件存在;运行时若缺,留 print 便于排查
print("[BridgedWebView] ERROR: WebViewJavascriptBridge.js 未在 Bundle 找到")
}
// ── H5 错误中继(Phase 9.2,原则 A 零修改 H5)─────────────
// 把 console.error / window.onerror / unhandledrejection 通过独立
// 的 webkit.messageHandlers.h5error 桥到原生 print,开发期减少盲点。
H5ErrorRelay.shared.install(into: configuration.userContentController)
// ── 创建 WKWebView + BridgeBus ─────────────────────────
let webView = WKWebView(frame: .zero, configuration: configuration)
let bridge = BridgeBus(webView: webView,
controller: configuration.userContentController)
self.webView = webView
self.bridge = bridge
super.init(frame: .zero)
// ── ScrollView 配置(契约 §4.1,禁手势滚动 / 弹性 / 自动 inset)──
let scroll = webView.scrollView
scroll.bounces = false
scroll.isScrollEnabled = false // ⚠️ 漏则 H5 上下滑动失控(契约硬约束)
scroll.contentInsetAdjustmentBehavior = .never
scroll.showsVerticalScrollIndicator = false
scroll.showsHorizontalScrollIndicator = false
// ── 布局:WebView 撑满 self ─────────────────────────────
webView.translatesAutoresizingMaskIntoConstraints = false
addSubview(webView)
NSLayoutConstraint.activate([
webView.topAnchor.constraint(equalTo: topAnchor),
webView.bottomAnchor.constraint(equalTo: bottomAnchor),
webView.leadingAnchor.constraint(equalTo: leadingAnchor),
webView.trailingAnchor.constraint(equalTo: trailingAnchor)
])
backgroundColor = .black
}
@available(*, unavailable)
public required init?(coder: NSCoder) {
fatalError("BridgedWebView does not support init(coder:)")
}
}
// MARK: - 跨 WebView 共享的进程池
/// 大厅 / 子游戏 / 弹层共用同一 WKProcessPool,共享 Cookie / 资源缓存,
/// 启动加速。详见 Design §3.5。
@MainActor
public enum SharedProcessPool {
public static let shared = WKProcessPool()
}