Files
youle_app_ios_v2/ylgamehall/Source/Resource/SandboxPaths.swift
T
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

72 lines
2.5 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// 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")
}
}