代码层落地 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:
joywayer
2026-06-22 12:44:45 +08:00
co-authored by Claude Opus 4.7
parent 13cc24b463
commit 829cc8f825
4 changed files with 267 additions and 7 deletions
@@ -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. #2showmessage
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. H5allowingReadAccessTo lobbyRoot
// 6. loadFileURL 4 app_*.js msext NewRootVC.initJSdata
// docs/H5-Native-Implementation-Design.md §7.5
try writeAppDataFiles(resolved: resolved)
// 7. H5allowingReadAccessTo 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 23 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 {