Commit Graph
6 Commits
Author SHA1 Message Date
joywayer 65505b0a79 Phase 1.9:BridgedWebView 封装 WKWebView + JS 注入 + BridgeBus
WebContainerViewController(Phase 1.10)的视图层依赖项:把 WKWebView 配置 /
WVJB.js 注入 / 桥总线 / scrollView 契约配置 4 件事一锅端打包成单一 UIView,
后续大厅 / 子游戏 / 弹层 VC 直接 add 进自己的内容区即可。

- 新增 ylgamehall/Source/WebView/BridgedWebView.swift
  - @MainActor final class,继承 UIView
  - 内含 public let webView: WKWebView 与 public let bridge: BridgeBus
  - init() 全自动:
    - WKWebViewConfiguration 按契约 §4.1:
      defaultWebpagePreferences.allowsContentJavaScript = true(iOS 14+
      现代 API,替代已弃用的 preferences.javaScriptEnabled)、
      javaScriptCanOpenWindowsAutomatically = NO、minimumFontSize = 10、
      allowsInlineMediaPlayback = true、processPool = SharedProcessPool.shared
    - 用 WKUserScript(.atDocumentStart) 把 WebViewJavascriptBridge.js 注入
      所有加载的 H5 页面,确保 H5 业务代码运行前 bridge 已就绪
    - 创建 BridgeBus,自动向 controller 注册 WVJBHandler 通道
    - scrollView:bounces=NO / isScrollEnabled=NO(契约硬约束,漏则 H5
      上下滑动失控)/ contentInsetAdjustmentBehavior=.never / 双向无指示器
    - WebView 撑满 self,自动布局
  - SharedProcessPool 命名空间:跨 WebView 共享 WKProcessPool,Cookie /
    资源缓存复用,启动加速(Design §3.5)
- BuildProject 通过,下一步 Phase 1.10 WebContainerViewController 用
  BridgedWebView 嵌入 + 16:9 letterbox + 加载 lobbyIndex
2026-06-22 00:19:51 +08:00
joywayer 4a6b1e1631 Phase 1.7:BridgeBus 实现 H5↔Native 消息总线 + WKScriptMessageHandler
桥核心的运行时层:把 BridgeProtocol 落到 WKWebView 上,承担 JS → Native
消息分发 + Native → H5 主动调用 + 双向 callback 配对。

- 新增 ylgamehall/Source/Bridge/BridgeBus.swift
  - @MainActor final class,BridgeProtocol + WKScriptMessageHandler 双重
    conform;init(webView:, controller:) 自动向 controller 注册 WVJBHandler
    通道
  - JS → Native(userContentController:didReceive:)
    - body 兼容单条 dict 与 batch [[dict]]
    - 优先识 responseId → 查 pendingCallbacks 派给 Native 端 callback
    - 否则按 handlerName 查 handlers,构造异步 responseCallback 闭包,
      handler 是 @Sendable async 用独立 Task 执行
  - Native → H5(call(_:data:callback:))
    - 拼 payload {handlerName, data, callbackId?},base64 后注入
      WebViewJavascriptBridge._handleMessageFromObjC('<base64>')
    - callback 用 nativeCallbackCounter 递增成 objc_cb_<n> 入
      pendingCallbacks,等 JS 响应回收
  - sendResponseToJS:拼 {responseId, responseData} 走同一通道
  - makeResponseCallback 辅助方法:把多层嵌套闭包拆出来,避免
    Swift 6 类型推断在深嵌套 + actor 隔离上 ICE
- 资源生命周期:webView 弱引用避免循环;BridgeBus 由 controller.add(self)
  retains,WebView 释放后 controller 一并清空 handler 列表自然 release
- BuildProject 通过,Swift 6 严格并发 0 warning;下一步 Phase 1.8 注入
  marcuswestin/WVJB JS 端配套协议即可双向消息打通
2026-06-22 00:11:54 +08:00
joywayer f740274d59 Phase 1.6:BridgeProtocol + BridgeData 协议骨架
桥核心的纯类型层,不依赖 WKWebView,便于 Phase 1.7 BridgeBus 实现与
单测注入 mock 都基于此抽象。

- 新增 ylgamehall/Source/Bridge/BridgeProtocol.swift
  - protocol BridgeProtocol: AnyObject, Sendable
    - register(_:handler:) 注册 H5 → Native handler,重复名覆盖
    - call(_:data:callback:) Native → H5 主动调用,callback 可选
  - typealias BridgeHandler = @Sendable (BridgeData?, BridgeCallback?)
    async -> Void,允许 handler 内部 await
  - typealias BridgeCallback = @Sendable (BridgeData?) -> Void
  - enum BridgeData: Sendable 六态(string/number/bool/null/array/object),
    与 JSON 值同构
- BridgeData JSON 互转:
  - init?(jsonObject:) 从 JSONSerialization 输出递归构造;NSNumber
    Bool/Double 鉴别用 CFGetTypeID 避免 NSNumber(value:true) 被当 1.0
  - var jsonObject: Any 反向,供 evaluateJavaScript 拼 JSON 字符串
- BridgeData 访问语法糖:asString/asInt/asDouble/asBool/asArray/asObject
  + subscript(key:) 对象字段访问 + subscript(index:) 数组访问
- BridgeData 字面量构造:ExpressibleByStringLiteral/IntegerLiteral/
  FloatLiteral/BooleanLiteral/ArrayLiteral/DictionaryLiteral,
  fixture / handler 代码 ["k": 1, "v": true] 直接构造 .object
- BuildProject 通过,Sendable 全自动派生
2026-06-22 00:05:26 +08:00
joywayer 463f0fc6fe Phase 1.5:ResourceUnzipper 解压 gamehall.zip 到 Caches 全链路打通
启动期完成「ChannelConfig.plist 读 → SandboxPaths 算路径 →
gamehall.zip 解压」三步,lobbyIndex 真实存在于沙盒 Caches,
为 Phase 1.10 WebContainer file:// 加载就绪。

- 新增 ylgamehall/Source/Resource/ResourceUnzipper.swift
  - public actor,全局单例 ResourceUnzipper.shared
  - ensureReady() async throws:检查 lobbyVersionXML 不存在则调
    FileManager.unzipItem (ZIPFoundation 扩展) 解压 Bundle 内
    gamehall.zip 到 SandboxPaths.lobbyRoot
  - UnzipError 类型化错误(bundleZipNotFound / unzipFailed)
  - 幂等:已就绪直接 return,并发调用安全(actor 串行)
- 新增 ylgamehall/Resources/gamehall.zip:从 docs/res 拷一份入
  Bundle(11.5 MB,2023-12 旧版;上线前由 H5 团队替换最新版,
  ADR-002 已记录)
- Swift 6 严格并发适配:
  - SandboxPaths:所有 static 成员标 nonisolated(无状态命名空间,
    从 actor / 非 MainActor 上下文都能直接读 caches/documents/bundle
    及 lobby 路径计算)
  - BundleConfig:class 整体 nonisolated(Sendable + all-let 属性,
    跨 actor 边界安全),允许 ResourceUnzipper actor 读 channel 配置
- RootViewController 补 ResourceUnzipper 烟雾测试 Task:
  await ensureReady() 后打印耗时 + lobbyIndex 是否真实存在
- 真机验证(iOS Simulator):首次 1.266s 解压成功,二次启动 0s 跳过
2026-06-22 00:01:08 +08:00
joywayer 730574a2e2 Phase 1.3:SandboxPaths 统一沙盒 / Bundle / H5 路径入口
集中管理 NSSearchPathFor... 调用,避免散落,方便后续 ResourceUnzipper
(Phase 1.5)与 WebView 加载(Phase 1.10)复用。

- 新增 ylgamehall/Source/Resource/SandboxPaths.swift
  - 基础:caches / documents / bundle
  - 大厅 H5:lobbyRoot / lobbyIndex / lobbyVersionXML(基于 BundleConfig
    当前渠道值动态拼接)
  - 子游戏 H5:subGameRoot(_:) / subGameIndex(_:_:)(Phase 6 调用,
    入参传 SwitchOverGameData 解出的 dir/start)
  - 全部 enum + static,无状态,Swift 6 严格并发零负担
- RootViewController.viewDidLoad 补 SandboxPaths 烟雾打印 6 项路径,
  便于首次启动 console 校验:
  - lobbyIndex 应为 {Caches}/FtJf073.../gamehall/index.html
  - lobbyVersionXML 应为 {Caches}/FtJf073.../gamehall/version.xml
- BuildProject 通过,synchronized group 自动收编新文件
2026-06-21 23:52:23 +08:00
joywayer dcc12bb76d Phase 1.2:BundleConfig 读 ChannelConfig.plist 完成 11 项渠道注入加载
- 新增 ylgamehall/Source/Resource/BundleConfig.swift
  - 11 个 public let 属性(qiniuDomain / gameId / channel / gameDir
    / gameStart / gameConfig / market / agent / appVersion / other
    / appleConfig)
  - public init(bundle: Bundle = .main) —— 默认走 main bundle,单测
    可注入 mock bundle 验证不同 plist fixture
  - PropertyListSerialization 反序列化 ChannelConfig.plist
  - final class + Sendable(all-let,String 是 Sendable,Swift 6
    严格并发下编译器静态校验通过)
  - 单例 BundleConfig.shared,业务统一通过此访问
- RootViewController.viewDidLoad 加烟雾测试 print 11 项值,首次运行
  真机/模拟器即可在 console 验证读取正确
- BuildProject 通过,synchronized group 自动收编 BundleConfig.swift
  为 source(.swift 文件类型走 SwiftCompile,不再卡 plist 那样的
  resource 扁平化问题)
- 单元测试待 Phase 0.2 单测 target 建立后补齐
2026-06-21 23:47:23 +08:00