代码层落地 AppDataWriter + NetworkMonitor + WebContainer 接入
按 Design §7.5 蓝图落地,让 H5 启动时能从沙盒读到实际渠道/启动/设备
值,闭合 Phase 1.17 联调"app_gameconfig 不正确"现象。
新增 Source/WebView/AppDataWriter.swift:
- public struct AppDataWriter(@MainActor)
- ContainerRole enum:.lobby / .subGame(name:, dir:)
- writeInitial():写 app_data.js(12 项)+ app_gamesname.js(暂只写当前
gameStart 一项,Phase 6 子游戏完整时扩为扫描已装列表)
- writeBattery(level:):写 app_battery.js(var app_getbattery=N;)
- writeNetwork(code:):写 app_network.js(var app_getnetwork=N;)
- 字面严格对齐 msext:
* 字符串单引号包裹 'value'
* 数值无引号(version=1 / Launchtype=0 / getwifisignalLevel=1)
* app_gamesname 用 "var app_gamesname=new Array(...)"(var 后两空格)
* 大小写硬约束:Launchtype L 大、getwifisignalLevel wifi 小 + signal/Level 区分
- escape 转义反斜杠 / 单引号 / 换行(避免渠道字段含单引号导致 H5 JS 解析错)
- Logger debug 输出文件路径 + 每个 key=value 一行,便于 §7.5.8 联调对账
新增 Source/Resource/NetworkMonitor.swift:
- @MainActor public final class NetworkMonitor
- NWPathMonitor 最简 wrap:currentCode 同步快照(默认 2 WiFi,启动后被
首次 path 更新)+ onChange MainActor 回调
- shared 单例 + 幂等 start()
WebContainer.runBootPipelineSteps 接入:
- switch outcome 前提取 let resolved: ResolvedVersion,避免变量作用域
限制 case 内
- 新增 writeAppDataFiles(resolved:):启动 NetworkMonitor + 开 battery
监控 + 写 4 个文件首次值 + 挂 addObserver(battery) + onChange(network)
- 调用位置:step 5 LobbyZipUpgrader 之后、step 6 loadFileURL 之前
(与 msext NewRootVC.initJSdata 等价时序)
BuildProject 通过
Plan §6.4 里程碑加一行。
Phase 1.17 联调验证:启动后 H5 console 应读到实际 app_channel /
app_gameconfig 等值,不再是 H5 zip 包内默认占位。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
13cc24b463
commit
829cc8f825
@@ -129,6 +129,7 @@ public final class WebContainerViewController: UIViewController {
|
||||
splash.update(text: "拉取配置中...", progress: nil)
|
||||
let outcome = try await RemoteConfigClient.shared.fetch()
|
||||
|
||||
let resolved: ResolvedVersion
|
||||
switch outcome {
|
||||
case .shortText(let msg):
|
||||
// 运营杀手锏 #1:运营把整段服务端响应替换为短文本,前端弹窗永停
|
||||
@@ -136,7 +137,7 @@ public final class WebContainerViewController: UIViewController {
|
||||
|
||||
case .parsed(let cfg):
|
||||
let bc = BundleConfig.shared
|
||||
let resolved = VersionResolver.resolve(
|
||||
let r = VersionResolver.resolve(
|
||||
config: cfg,
|
||||
agentId: bc.agent,
|
||||
channelId: bc.channel,
|
||||
@@ -145,13 +146,13 @@ public final class WebContainerViewController: UIViewController {
|
||||
)
|
||||
|
||||
// 3. 运营杀手锏 #2:showmessage 非空 → 弹窗永停
|
||||
if let msg = resolved.showmessage, !msg.isEmpty {
|
||||
if let msg = r.showmessage, !msg.isEmpty {
|
||||
throw BootError.operationalMessage(msg)
|
||||
}
|
||||
|
||||
// 4. IPA 升级(远端 appVersion > 本地):弹窗 + Safari 外链永停
|
||||
if resolved.appVersion > LocalVersionReader.localAppVersion,
|
||||
let dl = resolved.appDownload {
|
||||
if r.appVersion > LocalVersionReader.localAppVersion,
|
||||
let dl = r.appDownload {
|
||||
throw BootError.ipaUpgradeRequired(downloadURL: dl)
|
||||
}
|
||||
|
||||
@@ -159,7 +160,7 @@ public final class WebContainerViewController: UIViewController {
|
||||
// LobbyZipUpgrader 的 onProgress 由 URLSession delegate 在后台队列调度,
|
||||
// 必须显式 hop 到 MainActor 才能动 splash UI。
|
||||
_ = try await LobbyZipUpgrader.shared.upgradeIfNeeded(
|
||||
resolved: resolved,
|
||||
resolved: r,
|
||||
onProgress: { [weak self] p in
|
||||
Task { @MainActor in
|
||||
self?.splash.update(
|
||||
@@ -169,9 +170,14 @@ public final class WebContainerViewController: UIViewController {
|
||||
}
|
||||
}
|
||||
)
|
||||
resolved = r
|
||||
}
|
||||
|
||||
// 6. 加载本地 H5(allowingReadAccessTo 必须给 lobbyRoot 才能跨目录引用资源)
|
||||
// 6. 在 loadFileURL 之前写 4 个 app_*.js(与原 msext NewRootVC.initJSdata 等价时序)
|
||||
// 详见 docs/H5-Native-Implementation-Design.md §7.5
|
||||
try writeAppDataFiles(resolved: resolved)
|
||||
|
||||
// 7. 加载本地 H5(allowingReadAccessTo 必须给 lobbyRoot 才能跨目录引用资源)
|
||||
splash.update(text: "加载大厅...", progress: nil)
|
||||
bridgedWebView.webView.loadFileURL(
|
||||
SandboxPaths.lobbyIndex,
|
||||
@@ -179,6 +185,40 @@ public final class WebContainerViewController: UIViewController {
|
||||
)
|
||||
}
|
||||
|
||||
/// 写 4 个 app_*.js 文件,并挂钩 battery / network 变化的重写监听。
|
||||
/// 与原 msext NewRootVC.initJSdata + changebattery + changenetstate 等价。
|
||||
private func writeAppDataFiles(resolved: ResolvedVersion) throws {
|
||||
let writer = AppDataWriter(
|
||||
bundleConfig: .shared,
|
||||
resolvedVersion: resolved,
|
||||
containerRole: .lobby
|
||||
)
|
||||
|
||||
// 启动 NetworkMonitor(幂等:内部 started 标记)
|
||||
NetworkMonitor.shared.start()
|
||||
UIDevice.current.isBatteryMonitoringEnabled = true
|
||||
|
||||
// 首次写入:4 个文件全部落盘
|
||||
try writer.writeInitial()
|
||||
try writer.writeBattery(UIDevice.current.batteryLevel)
|
||||
try writer.writeNetwork(NetworkMonitor.shared.currentCode)
|
||||
|
||||
// 持续监听:battery / network 变化时重写对应文件。
|
||||
// 注意:H5 此次加载已读到首次写入的值,下次 reload 才会读到新值;
|
||||
// 业务期间的实时变化由 §3.2 [2][3]反向 callback 推送(Phase 2 实现)。
|
||||
NotificationCenter.default.addObserver(
|
||||
forName: UIDevice.batteryLevelDidChangeNotification,
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { _ in
|
||||
try? writer.writeBattery(UIDevice.current.batteryLevel)
|
||||
}
|
||||
|
||||
NetworkMonitor.shared.onChange = { code in
|
||||
try? writer.writeNetwork(code)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Alerts (terminal / retry states)
|
||||
|
||||
private var appDisplayName: String {
|
||||
|
||||
Reference in New Issue
Block a user