为未来 Cocos 子游戏 H5 build 适配铺路(file:// 下 Cocos XHR/fetch 受 null-origin 限制几乎必踩坑),同时保留大厅 + 子游戏跨页 localStorage 共享语义(用单虚拟 host `h5` 让所有 H5 same-origin)。 变更: - 新增 AppSchemeHandler(WKURLSchemeHandler 单例 + Range/MIME/异步 IO/ 取消语义;闭包只携 Sendable ObjectIdentifier,不捕获 task) - SandboxPaths 加 lobbyIndexAppURL / subGameIndexAppURL builders - BridgedWebView 注册 scheme handler(WKWebView init 前) - WebContainerViewController / SubGameViewController 的 loadFileURL → webView.load(URLRequest),OverlayViewController 不变 文档: - Plan 新增 Phase 1.F (1.18-1.21) + ADR-010 决策记录 + 进度勾选 - Design 新增 §7.6 包含 URL 结构 / 实现要点 / 等价性表 / Cocos 预检脚本 - Contract §0.2 / §4.1 / §10 验收清单同步切换说明(H5 可观察差异: location.protocol "file:" → "ylgame:",项目方已 grep 确认现网 H5 不依赖此字面) 存量影响:file:// → ylgame:// origin 切换时老用户 localStorage 一次性 清零,已与项目方确认业务可接受、不做迁移补偿。 BuildProject 通过。Plan 进度已勾选 1.18/1.19/1.20。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
98 lines
3.7 KiB
Swift
98 lines
3.7 KiB
Swift
//
|
||
// SandboxPaths.swift
|
||
// ylgamehall
|
||
//
|
||
// 集中管理沙盒与 Bundle 路径,避免散落 NSSearchPathFor...。
|
||
// 详见 docs/H5-Native-Implementation-Design.md §7.1。
|
||
//
|
||
|
||
import Foundation
|
||
|
||
/// 沙盒、Bundle、H5 资源路径的统一入口。所有 ResourceUnzipper / WebView 加载
|
||
/// 等模块通过此命名空间获取路径,禁止重新写 `urls(for:in:)` 等散落调用。
|
||
public enum SandboxPaths {
|
||
|
||
// MARK: - 基础沙盒目录
|
||
|
||
/// `Library/Caches/`(系统空间不足时可能被清,需可重建)
|
||
nonisolated public static var caches: URL {
|
||
FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
|
||
}
|
||
|
||
/// `Documents/`(用户级持久化,iCloud 备份)
|
||
nonisolated public static var documents: URL {
|
||
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
|
||
}
|
||
|
||
/// `<App>.app/`
|
||
nonisolated public static var bundle: URL {
|
||
Bundle.main.bundleURL
|
||
}
|
||
|
||
// MARK: - 大厅 H5 路径(基于 BundleConfig.shared 的当前渠道)
|
||
|
||
/// 大厅 H5 解压根目录:`{Caches}/{gamedir}/`
|
||
nonisolated public static var lobbyRoot: URL {
|
||
caches.appendingPathComponent(BundleConfig.shared.gameDir)
|
||
}
|
||
|
||
/// 大厅 H5 入口 HTML:`{Caches}/{gamedir}/{gamestart}/index.html`
|
||
nonisolated public static var lobbyIndex: URL {
|
||
let cfg = BundleConfig.shared
|
||
return caches
|
||
.appendingPathComponent(cfg.gameDir)
|
||
.appendingPathComponent(cfg.gameStart)
|
||
.appendingPathComponent("index.html")
|
||
}
|
||
|
||
/// 大厅 H5 版本标记文件:`{Caches}/{gamedir}/{gamestart}/version.xml`
|
||
/// 由 ResourceUnzipper 判断是否需要解压初始 gamehall.zip。
|
||
nonisolated public static var lobbyVersionXML: URL {
|
||
let cfg = BundleConfig.shared
|
||
return caches
|
||
.appendingPathComponent(cfg.gameDir)
|
||
.appendingPathComponent(cfg.gameStart)
|
||
.appendingPathComponent("version.xml")
|
||
}
|
||
|
||
// MARK: - 子游戏 H5 路径(参数化,由 SwitchOverGameData 传入)
|
||
|
||
/// 子游戏 H5 解压根目录:`{Caches}/{dir}/`
|
||
nonisolated public static func subGameRoot(_ dir: String) -> URL {
|
||
caches.appendingPathComponent(dir)
|
||
}
|
||
|
||
/// 子游戏 H5 入口 HTML:`{Caches}/{dir}/{start}/index.html`
|
||
nonisolated public static func subGameIndex(_ dir: String, _ start: String) -> URL {
|
||
caches.appendingPathComponent(dir)
|
||
.appendingPathComponent(start)
|
||
.appendingPathComponent("index.html")
|
||
}
|
||
|
||
// MARK: - App scheme URL(Phase 1.F WKURLSchemeHandler 迁移)
|
||
//
|
||
// 由 AppSchemeHandler 接管的自定义 scheme URL。大厅 + 所有子游戏共用
|
||
// 同一虚拟 host (`ylgame://h5`),从而 same-origin → 共享 localStorage。
|
||
// 路径前缀区分目标 (`lobby` / `subgame/<dir>`),handler 内反向映射回
|
||
// 上面 lobbyRoot / subGameRoot(_:) 的沙盒物理路径。
|
||
|
||
/// 大厅 H5 入口的 app:// URL:`ylgame://h5/lobby/<gameStart>/index.html`
|
||
nonisolated public static var lobbyIndexAppURL: URL {
|
||
var c = URLComponents()
|
||
c.scheme = AppScheme.scheme
|
||
c.host = AppScheme.host
|
||
c.path = "/\(AppScheme.lobbyPrefix)/\(BundleConfig.shared.gameStart)/index.html"
|
||
// 字面下 path 不含空格 / 中文,force-unwrap 安全
|
||
return c.url!
|
||
}
|
||
|
||
/// 子游戏 H5 入口的 app:// URL:`ylgame://h5/subgame/<dir>/<start>/index.html`
|
||
nonisolated public static func subGameIndexAppURL(_ dir: String, _ start: String) -> URL {
|
||
var c = URLComponents()
|
||
c.scheme = AppScheme.scheme
|
||
c.host = AppScheme.host
|
||
c.path = "/\(AppScheme.subGamePrefix)/\(dir)/\(start)/index.html"
|
||
return c.url!
|
||
}
|
||
}
|