docs:Plan + Design 扩展 Phase 1.14 为 5 子项(含 AppIcon / LaunchScreen / progress / splash UI)

原 Phase 1.14 仅含"WebContainer 16:9 letterbox + 串接整链路",
漏掉了用户实际能看到的三件事:AppIcon / LaunchScreen 启动图 / zip
下载进度条。msext 上线时这三件都有,新外壳上线必须对齐。按用户偏好
方案 A 合并到 Phase 1.14,避免做两遍 WebContainer。

- Plan §5 Phase 1.14 拆为 1.14.a–e 五个子项
  - 1.14.a AppIcon:拷贝 docs/res/Images.xcassets/AppIcon-1.appiconset
    (9 个 png + Contents.json) 到 ylgamehall/Assets.xcassets
  - 1.14.b LaunchScreen:Default-568h@2x~iphone.png → SplashImage
    imageset;改写 LaunchScreen.storyboard 横屏铺满 UIImageView
  - 1.14.c LobbyZipUpgrader 加 onProgress 回调(@Sendable (Double) -> Void)
  - 1.14.d WebContainerViewController:splash 覆盖层(启动图 +
    UIProgressView + 状态文字)+ 16:9 letterbox + 启动流水线串接
  - 1.14.e webView didFinish 淡出 splash(0.3s 动画 + removeFromSuperview)
- Plan §8 进度追踪同步:5 个子项独立勾选条目
- Design §6.3.5 WebContainer 调用串完整重写:
  - Splash UI 6 状态切换表(拼命启动中... / 拉取配置中... /
    下载更新中 XX% / 加载大厅... / 淡出)
  - runBootPipeline 完整 swift 骨架:含 .shortText 短文本响应处理 /
    showmessage 阻塞处理 / IPA 升级 alert / zip 升级 onProgress 回调
  - 所有失败 / 阻塞终态都用 UIAlertController(非 splash 文字):
    短文本 / showmessage / IPA 升级 / 网络错误 / zip 下载失败
    各场景的按钮文案 + tap 行为表
This commit is contained in:
joywayer
2026-06-22 02:35:37 +08:00
parent 87304552c3
commit 2280462a22
2 changed files with 115 additions and 20 deletions
+16 -3
View File
@@ -287,12 +287,21 @@ Contract Design Plan(本文档)
##### 1.E 集成 + 烟雾测试 ##### 1.E 集成 + 烟雾测试
- [ ] **1.14** 实现 `Source/WebView/WebContainerViewController.swift`(基类) - [ ] **1.14.a** AppIcon:从 `docs/res/Images.xcassets/AppIcon-1.appiconset` 拷贝 9 个 png + Contents.json 到 `ylgamehall/Assets.xcassets/AppIcon.appiconset`,替换 Xcode 默认空模板。验收:Home Screen 显示真实图标
- [ ] **1.14.b** LaunchScreen:把 `docs/res/Res/Default-568h@2x~iphone.png` 拷到 `ylgamehall/Assets.xcassets/SplashImage.imageset`;改写 `LaunchScreen.storyboard` 为单一 UIImageView 横屏铺满(contentMode = .scaleAspectFill,背景 .black)。验收:启动瞬间看到启动图而非黑屏
- [ ] **1.14.c** `LobbyZipUpgrader` 加 progress 回调
- `upgradeIfNeeded(resolved:onProgress:) async throws -> Outcome``onProgress: @Sendable (Double) -> Void` 报告 0.0…1.0
-`URLSession.download``URLSessionTaskDelegate.urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)` 上报
- [ ] **1.14.d** 实现 `Source/WebView/WebContainerViewController.swift`(基类)
- 持有 `BridgedWebView` + `BridgeBus` - 持有 `BridgedWebView` + `BridgeBus`
- 16:9 比例布局:屏幕比 < 16:9 用宽度撑满上下黑边;屏幕比 > 16:9 用高度撑满左右黑边 - 16:9 比例布局:屏幕比 < 16:9 用宽度撑满上下黑边;屏幕比 > 16:9 用高度撑满左右黑边
- `viewDidLoad` 内串:`ResourceUnzipper.ensureReady() → RemoteConfigClient.fetch → VersionResolver.resolve → LobbyZipUpgrader.upgradeIfNeeded → loadFileURL(lobbyIndex)` - **Splash 状态层**:与 LaunchScreen 同款启动图 + UIProgressView + 状态文字标签("拼命启动中..." / "下载更新中 XX%" / "网络异常重试中"
- `viewDidLoad` 内串:`ResourceUnzipper.ensureReady() → RemoteConfigClient.fetch → VersionResolver.resolve → LobbyZipUpgrader.upgradeIfNeeded(onProgress: 更新 UIProgressView) → loadFileURL(lobbyIndex)`
- 无升级路径走快路(< 200 ms),有升级路径显示 progress UI - 无升级路径走快路(< 200 ms),有升级路径显示 progress UI
- 实现 `webViewWebContentProcessDidTerminate:` 退避 reloadDesign §3.6 - 实现 `webViewWebContentProcessDidTerminate:` 退避 reloadDesign §3.6
- [ ] **1.14.e** WebView 加载完成后淡出 splash
- `webView(_:didFinish:)``UIView.animate(duration: 0.3)` splash.alpha = 0 → `removeFromSuperview`
- 期间 webView 已渲染好 H5 大厅,无白屏闪烁
- [ ] **1.15** 实现 `Source/Bridge/Handlers/VibratorHandler.swift` - [ ] **1.15** 实现 `Source/Bridge/Handlers/VibratorHandler.swift`
- `register(_ bridge)` 注册 `vibrator` handler → `AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)` + responseCallback `"vibrator"` - `register(_ bridge)` 注册 `vibrator` handler → `AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)` + responseCallback `"vibrator"`
- [ ] **1.16** `SceneDelegate``WebContainerViewController` 作 rootViewController - [ ] **1.16** `SceneDelegate``WebContainerViewController` 作 rootViewController
@@ -785,7 +794,11 @@ H5 调 `OpenurlTitleData` 打开弹层 WebView,弹层内 H5 用 `window.settin
- [x] 1.11 VersionResolverchulishengji 双子树合并算法,纯函数) - [x] 1.11 VersionResolverchulishengji 双子树合并算法,纯函数)
- [x] 1.12 LocalVersionReaderversion.xml 解析 + ATS 全局放行) - [x] 1.12 LocalVersionReaderversion.xml 解析 + ATS 全局放行)
- [x] 1.13 LobbyZipUpgraderactor,原子 rename 升级;端到端实测 260→261) - [x] 1.13 LobbyZipUpgraderactor,原子 rename 升级;端到端实测 260→261)
- [ ] 1.14 WebContainerViewController16:9 letterbox + 串接整链路) - [ ] 1.14.a AppIcon 替换 Xcode 默认空模板
- [ ] 1.14.b LaunchScreen 改启动图(Default-568h@2x 横屏铺满)
- [ ] 1.14.c LobbyZipUpgrader 加 progress 回调
- [ ] 1.14.d WebContainerViewController(含 splash UI 状态层 + 16:9 letterbox + 启动流水线串接)
- [ ] 1.14.e WebView didFinish 后淡出 splash
- [ ] 1.15 VibratorHandler - [ ] 1.15 VibratorHandler
- [ ] 1.16 SceneDelegate → WebContainerViewController - [ ] 1.16 SceneDelegate → WebContainerViewController
- [ ] 1.17 Demo H5 联调 + 真机验证升级路径 - [ ] 1.17 Demo H5 联调 + 真机验证升级路径
+99 -17
View File
@@ -1236,36 +1236,118 @@ public actor LobbyZipUpgrader {
} }
``` ```
#### 6.3.5 WebContainer 调用串 #### 6.3.5 WebContainer 调用串 + Splash UI 状态机(Phase 1.14
WebContainerViewController 同时承担两层:
- **下层**BridgedWebView 嵌入 + 16:9 letterbox(屏幕比 < 16:9 上下黑边;> 16:9 左右黑边)
- **上层(Splash 覆盖)**:与 LaunchScreen.storyboard 同款启动图(横屏铺满)+ UIProgressView + 状态文字标签
- 启动瞬间:LaunchScreen 静态图(iOS 系统级,0 延迟)
- viewDidLoadWebContainer 把自己的 Splash 覆盖在 BridgedWebView 之上(两者 alpha 0/1 由状态切换)
- 启动流水线推进时:updateSplash(text: ..., progress: ...) 更新文字 + 进度条
- WKWebView didFinish 后:UIView.animate(duration: 0.3) splash.alpha = 0 → removeFromSuperview
```swift ```swift
// Source/WebView/WebContainerViewController.swiftPhase 1.14 // Source/WebView/WebContainerViewController.swiftPhase 1.14
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
view.backgroundColor = .black
setupBridgedWebView() // 16:9 letterbox
setupSplashOverlay() // + +
bridgedWebView.webView.navigationDelegate = self
Task { @MainActor in Task { @MainActor in
try await ResourceUnzipper.shared.ensureReady() // Bundle zip await runBootPipeline()
let config = try await RemoteConfigClient.shared.fetch() }
let resolved = VersionResolver.resolve( }
config: config,
agentId: BundleConfig.shared.agent, private func runBootPipeline() async {
channelId: BundleConfig.shared.channel, do {
marketId: BundleConfig.shared.market, updateSplash(text: "拼命启动中...", progress: nil)
gameId: BundleConfig.shared.gameId try await ResourceUnzipper.shared.ensureReady()
)
if resolved.appVersion > LocalVersionReader.localAppVersion { updateSplash(text: "拉取配置中...", progress: nil)
// + Safari resolved.appDownload let outcome = try await RemoteConfigClient.shared.fetch()
return showAppUpgradeAlert(resolved.appDownload)
switch outcome {
case .shortText(let msg):
// #1 alertApp msext NewRootVC.m:1239-1243
showBlockingAlert(msg)
return
case .parsed(let cfg):
let bc = BundleConfig.shared
let resolved = VersionResolver.resolve(
config: cfg,
agentId: bc.agent,
channelId: bc.channel,
marketId: bc.market,
gameId: bc.gameId
)
// #2showmessage alert +
if let msg = resolved.showmessage, !msg.isEmpty {
showBlockingAlert(msg)
return
}
// IPA + Safari msext NewRootVC.m:1510-1520
if resolved.appVersion > LocalVersionReader.localAppVersion,
let dl = resolved.appDownload {
showIPAUpgradeAlert(dl)
return
}
// H5 zip
_ = try await LobbyZipUpgrader.shared.upgradeIfNeeded(
resolved: resolved,
onProgress: { @MainActor [weak self] p in
self?.updateSplash(
text: String(format: "下载更新中 %d%%", Int(p * 100)),
progress: p
)
}
)
} }
_ = try await LobbyZipUpgrader.shared.upgradeIfNeeded(
remoteGameVersion: resolved.gameVersion, updateSplash(text: "加载大厅...", progress: nil)
remoteGameZip: resolved.gameZip
)
bridgedWebView.webView.loadFileURL( bridgedWebView.webView.loadFileURL(
SandboxPaths.lobbyIndex, SandboxPaths.lobbyIndex,
allowingReadAccessTo: SandboxPaths.lobbyRoot allowingReadAccessTo: SandboxPaths.lobbyRoot
) )
// didFinish splash WKNavigationDelegate
} catch {
showFatalAlert(error)
} }
} }
// MARK: - WKNavigationDelegate
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
UIView.animate(withDuration: 0.3, animations: { [weak self] in
self?.splash.alpha = 0
}, completion: { [weak self] _ in
self?.splash.removeFromSuperview()
})
}
```
**Splash UI 状态切换表**
| 阶段 | 文字 | progress | 持续时长(典型) |
|------|------|----------|---------------|
| viewDidLoad 起 | "拼命启动中..." | 隐藏 | ~50 ms |
| 首装解压 zip 中 | "拼命启动中..." | 隐藏 | ~50-500 ms |
| 拉远端配置中 | "拉取配置中..." | 隐藏 | ~500-2000 ms |
| H5 zip 下载中 | "下载更新中 XX%" | 0.0-1.0 | ~1000-3000 ms |
| WebView 加载 H5 中 | "加载大厅..." | 隐藏 | ~500-1000 ms |
| WebView didFinish | (淡出消失)| - | 300 ms 动画 |
**所有失败 / 阻塞终态都用 UIAlertController(非 splash 文字)**
- 短文本响应:弹 alert,标题 `gamehallname + 提醒`,单按钮"确定",无 tap 处理(永停)
- showmessage 非空:同上
- IPA 升级:弹 alert,单按钮"确定"→ openURL(appDownload),永停
- 网络错误 / 解析失败:弹 alert,单按钮"重试",点击重新 runBootPipeline()
- zip 下载失败:弹 alert,单按钮"重试" + 单按钮"使用旧版本"(回退用 IPA 内 Bundle zip
``` ```
#### 6.3.6 与 msext 历史实现的差异(不要照抄的部分) #### 6.3.6 与 msext 历史实现的差异(不要照抄的部分)