Files
youle_app_ios_v2/ylgamehall/Source/Resource/ResourceUnzipper.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

52 lines
1.6 KiB
Swift

//
// ResourceUnzipper.swift
// ylgamehall
//
// 启动期把 Bundle 内的 gamehall.zip 解压到 Library/Caches/{gamedir}/。
// 详见 docs/H5-Native-Implementation-Design.md §1.1 / §7。
//
import Foundation
import ZIPFoundation
/// 大厅 H5 资源包解压器。actor 隔离保证同一进程内并发调用 `ensureReady()` 不重复解压。
///
/// 解压触发条件:`{Caches}/{gamedir}/{gamestart}/version.xml` 不存在
/// (首次安装 / 系统清空 Caches 后命中)。日常启动直接跳过,耗时为 0。
public actor ResourceUnzipper {
public static let shared = ResourceUnzipper()
public enum UnzipError: Error {
case bundleZipNotFound
case unzipFailed(underlying: Error)
}
private init() {}
/// 异步确保大厅 H5 资源在 Caches 内就绪。幂等。
///
/// - 已就绪(version.xml 存在)→ 立即返回
/// - 未就绪 → 创建 `{Caches}/{gamedir}/`,解压 `Bundle/gamehall.zip` 到其中
public func ensureReady() async throws {
if FileManager.default.fileExists(atPath: SandboxPaths.lobbyVersionXML.path) {
return
}
guard let zipURL = Bundle.main.url(forResource: "gamehall", withExtension: "zip") else {
throw UnzipError.bundleZipNotFound
}
let destination = SandboxPaths.lobbyRoot
let fm = FileManager.default
do {
try fm.createDirectory(at: destination,
withIntermediateDirectories: true)
try fm.unzipItem(at: zipURL, to: destination)
} catch {
throw UnzipError.unzipFailed(underlying: error)
}
}
}