Phase 1.12:LocalVersionReader 读 version.xml + ATS 全局放行
新增 LocalVersionReader 读两个本地版本号 + Info.plist 加
NSAllowsArbitraryLoads 全局放行 HTTP(为 Phase 1.13 zip 下载链路准备)。
- 新增 ylgamehall/Source/Resource/LocalVersionReader.swift
- public enum LocalVersionReader,nonisolated static 命名空间
- localAppVersion: Int 从 BundleConfig.shared.appVersion 转 Int
(沿用 ChannelConfig.plist 注入),解析失败返回 0
- localGameVersion: Int 用 Foundation XMLParser SAX 风格解析
SandboxPaths.lobbyVersionXML 的 /game/version@value 节点,
缺失 / 损坏返回 0(msext NewRootVC.m:664-682 `[nil intValue]=0`
兜底语义,是首装后首次升级的关键机制)
- subGameVersion(dir:start:) 子游戏版本号读取,Phase 6 用
- 内部 VersionXMLHandler 标 nonisolated(Swift 6 严格并发下
XMLParserDelegate 跨边界),SAX 风格只在 didStartElement 抓
version 节点的 value 属性
- Info.plist 加 NSAppTransportSecurity / NSAllowsArbitraryLoads = true
- 企业签 / 超签分发场景,后端域名易变(zip CDN / 配置服 /
七牛 / 错误日志上报等),白名单维护成本高于安全收益
- 全局放行的副作用主要影响第三方 SDK 流量;本项目无意外引入未审计
SDK,且第三方 SDK 自身一般也走 HTTPS,不受影响
- 详见 ADR-008 决策上下文与 CLAUDE.md「不上架 App Store,企业签 /
超签 / TF 分发」契约
- RootViewController 烟雾测试串接 LocalVersionReader:打印本地 vs 远端
对比 + IPA / zip 升级判定
- 真机实测(现网 .txt 本地 + 当前 IPA 内 gamehall.zip 解压物):
- 本地 appVersion = 43, 远端 = 43 → 不升级
- 本地 gameVersion = 260(IPA 内 2023-12 zip 的 version.xml),
远端 = 261 → 需要 zip 升级。Phase 1.13 将实现下载 +
解压 + 替换链路
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// LocalVersionReader.swift
|
||||
// ylgamehall
|
||||
//
|
||||
// 读本地两个版本号:
|
||||
// - 原生 IPA 版本(来自 ChannelConfig.plist 的 appversion 字段)
|
||||
// - H5 包版本(来自 Library/Caches/{gamedir}/{gamestart}/version.xml 的 /game/version@value)
|
||||
//
|
||||
// 详见 docs/Development-Plan.md ADR-008-F / msext NewRootVC.m:664-682。
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum LocalVersionReader {
|
||||
|
||||
/// 原生 IPA 版本。等价 msext `[FuncPublic filename:@"appversion"] integerValue`
|
||||
/// 来源:ChannelConfig.plist 的 `appversion` 字段(沿用渠道注入约定,
|
||||
/// IPA 后处理改 plist 可动态调整)。
|
||||
/// 解析失败返回 0(msext `[nil intValue] = 0` 兜底语义)。
|
||||
nonisolated public static var localAppVersion: Int {
|
||||
Int(BundleConfig.shared.appVersion) ?? 0
|
||||
}
|
||||
|
||||
/// 大厅 H5 包版本。等价 msext `NewRootVC.m:664-682` 的 GDataXML 路径,
|
||||
/// 新外壳用 Foundation `XMLParser`(无外部依赖)解析。
|
||||
///
|
||||
/// 文件路径:`SandboxPaths.lobbyVersionXML` =
|
||||
/// `Library/Caches/{gamedir}/{gamestart}/version.xml`
|
||||
/// 解析 `/game/version` 节点的 `value` 属性,转 Int。
|
||||
///
|
||||
/// **缺失 / 损坏返回 0**(必须保留,首装后首次升级的兜底机制:
|
||||
/// `[nil intValue] = 0`,任何远端 gameVersion > 0 触发下载)。
|
||||
nonisolated public static var localGameVersion: Int {
|
||||
readGameVersion(from: SandboxPaths.lobbyVersionXML) ?? 0
|
||||
}
|
||||
|
||||
/// 子游戏 H5 包版本。等价 msext `gameController.m:1249-1254`。
|
||||
nonisolated public static func subGameVersion(dir: String, start: String) -> Int {
|
||||
let url = SandboxPaths.caches
|
||||
.appendingPathComponent(dir)
|
||||
.appendingPathComponent(start)
|
||||
.appendingPathComponent("version.xml")
|
||||
return readGameVersion(from: url) ?? 0
|
||||
}
|
||||
|
||||
// MARK: - 内部 XML 解析
|
||||
|
||||
nonisolated private static func readGameVersion(from url: URL) -> Int? {
|
||||
guard let data = try? Data(contentsOf: url) else { return nil }
|
||||
let parser = XMLParser(data: data)
|
||||
let handler = VersionXMLHandler()
|
||||
parser.delegate = handler
|
||||
guard parser.parse(), let v = handler.gameVersion else { return nil }
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - XMLParser delegate
|
||||
|
||||
/// 仅提取 `<version value="N"/>`(在 `/game/version` 路径下)的 value 转 Int。
|
||||
/// msext 的 xpath 是 `/game/version` 取属性 `value`;新外壳用 SAX 风格 XMLParser
|
||||
/// 也只需要在 `version` 元素出现时取属性即可(嵌套层级宽容化处理)。
|
||||
nonisolated private final class VersionXMLHandler: NSObject, XMLParserDelegate {
|
||||
var gameVersion: Int?
|
||||
|
||||
func parser(_ parser: XMLParser,
|
||||
didStartElement elementName: String,
|
||||
namespaceURI: String?,
|
||||
qualifiedName qName: String?,
|
||||
attributes attributeDict: [String: String] = [:]) {
|
||||
if elementName == "version", let raw = attributeDict["value"], let v = Int(raw) {
|
||||
gameVersion = v
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user