Phase 1.1:渠道注入用 ChannelConfig.plist 替代 msext 目录树(ADR-007)
契约 §0.3 描述 msext 把 11 个渠道值编码为 Bundle 根的 11 个空目录 子文件夹名(目录名本身就是值)。在 Xcode 26.5 默认的 synchronized group 下,此机制不兼容(子目录被扁平化、11 个 .gitkeep 撞名; folder reference 拖入流程失效;Run Script Phase 受 sandbox 阻碍且 默认 Based on dependency analysis 让 clean build 也不跑)。 CLAUDE.md 原则 B 落地——原生内部自由重构,不照搬 msext。改用单一 plist 存储等价语义,H5 端通过 app_data.js 看到的 11 个 JS 全局变量 行为完全不变(契约边界在 BundleConfig.shared.xxx,与底层无关)。 多渠道分发用 plutil -replace + 重签 + 重打包,工作量与 mv 目录名 重签完全相同。 - 新增 ylgamehall/Resources/ChannelConfig.plist:11 个 string key 含母包默认值(沿用 msext 现网值);synchronized group 自动入 Bundle - Design §7.0 / §7.2 重写为 plist 方案;BundleConfig 代码骨架改为 PropertyListSerialization + init(bundle:) 可注入式 - Plan §5 Phase 1.1 任务清单从 4 个子项简化为 1 项;ADR-004 文字 同步;新增 ADR-007 完整记录决策背景 / 触发事件 / 工程兼容性分析 / 理由 / 守护条款 - pbxproj:ENABLE_USER_SCRIPT_SANDBOXING 残留为 NO(前期 Run Script 方案探索时关闭,plist 方案下不再需要,但未恢复以避免再次 UI 操作; 无 Run Script 故无实际安全暴露面,未来可随时改回 YES) - BuildProject 验证:plist 已落在 .app 根,plutil -p 输出 11 个键值 完整
This commit is contained in:
+44
-11
@@ -227,13 +227,11 @@ Contract Design Plan(本文档)
|
||||
|
||||
##### 1.A 资源层
|
||||
|
||||
- [ ] **1.1.a** 写 `Scripts/channels/dev.env`:11 个渠道键值对(沿用 msext 现网 demo),git 跟踪
|
||||
- [ ] **1.1.b** 写 `Scripts/inject_channel.sh`:读 env → 清空并重新生成 `ylgamehall/ChannelInjection/` 目录树
|
||||
- [ ] **1.1.c** `.gitignore` 加 `ylgamehall/ChannelInjection/`
|
||||
- [ ] **1.1.d** 跑一次 `Scripts/inject_channel.sh dev` 生成本地 dev 渠道目录
|
||||
- [x] **1.1** 创建 `ylgamehall/Resources/ChannelConfig.plist` 含 11 个渠道键值对(沿用 msext 现网 demo 作为母包默认值;ADR-007 决策)
|
||||
- [ ] **1.2** 实现 `ylgamehall/Source/Resource/BundleConfig.swift`
|
||||
- `static func readInjected(_ key: String) -> String`:扫描 `Bundle.main.bundleURL.appendingPathComponent("ChannelInjection")` 下 `key` 子目录,返回首个非隐藏子项名
|
||||
- 单测 fixture:建立 mock bundle,验证 11 个 key 全部能读出
|
||||
- `init(bundle: Bundle = .main)`:读 `bundle.url(forResource: "ChannelConfig", withExtension: "plist")`,用 `PropertyListSerialization` 反序列化为 `[String: String]`
|
||||
- 公开 11 个只读属性:`qiniuDomain` / `gameId` / `channel` / `gameDir` / `gameStart` / `gameConfig` / `market` / `agent` / `appVersion` / `other` / `appleConfig`
|
||||
- 单测 fixture:建立 mock bundle 含 fixture plist,验证 11 个 key 全部能读出且缺失 key 返回空串
|
||||
- [ ] **1.3** 实现 `ylgamehall/Source/Resource/SandboxPaths.swift`
|
||||
- 常量:`caches` / `documents` / `bundle`
|
||||
- `lobbyIndex() -> URL` 拼出 `{Caches}/{gamedir}/{gamestart}/index.html`
|
||||
@@ -742,7 +740,7 @@ H5 调 `OpenurlTitleData` 打开弹层 WebView,弹层内 H5 用 `window.settin
|
||||
- [ ] 0.6 SPM 包目录决策(暂单 target)
|
||||
|
||||
### Phase 1 最小垂直闭环
|
||||
- [ ] 1.1 ChannelInjection 目录占位
|
||||
- [x] 1.1 ChannelConfig.plist 母包默认值(ADR-007)
|
||||
- [ ] 1.2 BundleConfig
|
||||
- [ ] 1.3 SandboxPaths
|
||||
- [ ] 1.4 ZIPFoundation SPM
|
||||
@@ -878,10 +876,9 @@ H5 调 `OpenurlTitleData` 打开弹层 WebView,弹层内 H5 用 `window.settin
|
||||
### ADR-004:项目资源目录结构由各 Phase 按需落地,不预先建仓库根 Resources/(2026-06-21 修订)
|
||||
- **背景**:原 ADR-004(2026-06-21 初版)决议"项目资源统一放仓库根 `Resources/` + 打包脚本 Build Phase 引入",并将 `gamehall.zip` / `Images.xcassets` / `Res/` 入库到 `Resources/`。但 `docs/res/` 后被维护者重新定义为**私人原始素材池**(不被项目感知,详见 CLAUDE.md),原 `Resources/` 入库的素材被搬走,使该 ADR 的语义崩塌
|
||||
- **修订决策**:废止"仓库根 `Resources/`"的固定结构假设,**项目内资源目录由各 Phase 实施时按需落地**:
|
||||
- 静态 Bundle 资源 → `ylgamehall/Resources/`(synchronized group 自动收集)
|
||||
- 静态 Bundle 资源 → `ylgamehall/Resources/`(synchronized group 自动收集,含 `gamehall.zip` / `ChannelConfig.plist` 等)
|
||||
- 原生 Asset Catalog → `ylgamehall/Assets.xcassets/`
|
||||
- 当前激活渠道注入产物 → `ylgamehall/ChannelInjection/`(.gitignore 忽略,由 `Scripts/inject_channel.sh` 生成)
|
||||
- 渠道值模板 → `Scripts/channels/<channel>.env`(git 跟踪)
|
||||
- 渠道注入值 → `ylgamehall/Resources/ChannelConfig.plist`(11 个 string key,母包默认值;ADR-007)
|
||||
- 闭源 SDK 二进制 → `Vendor/<SDK>/<SDK>.xcframework`
|
||||
- 私人原始素材池 → `docs/res/`(项目不感知,需要时拷贝到上述工程内目录)
|
||||
- **理由**:
|
||||
@@ -933,7 +930,43 @@ H5 调 `OpenurlTitleData` 打开弹层 WebView,弹层内 H5 用 `window.settin
|
||||
- **回滚条件**:未来某天出现 ≥ 3 个仅有 podspec 而无 SPM / XCFramework 的 SDK 必接需求时,重新评估是否引入 CocoaPods
|
||||
- **维护责任**:AMap 升级(年度级)→ 下载新 XCFramework 覆盖 `Vendor/AMap/` + 更新 README + 跑契约测试
|
||||
|
||||
### ADR-007:渠道注入用 `ChannelConfig.plist` 替代 msext 的"空目录名注入"机制(2026-06-21)
|
||||
- **背景**:Contract §0.3 描述 msext 把 11 个渠道值编码为 Bundle 根的 11 个目录子文件夹名(如 `channel/FtJf0.../`,目录名本身就是值)。新项目首版尝试照搬此机制
|
||||
- **触发事件**:与 Xcode 26.5 默认的 `PBXFileSystemSynchronizedRootGroup` 不兼容——
|
||||
- 子目录被同步组当作 group 处理,11 个 `.gitkeep` 在 Bundle 根撞名 → build 失败
|
||||
- 尝试 folder reference(蓝色文件夹):Xcode 26.5 拖入对话框流程对此不友好,pbxproj 不会写入正确条目
|
||||
- 尝试 Run Script Build Phase:
|
||||
1. 默认开启的 User Script Sandboxing 拒绝读 `Scripts/copy_channel_injection.sh` 与 `ChannelInjection/`
|
||||
2. 即使关掉 sandbox,Run Script 在没有 Input/Output 声明 + 默认勾选"Based on dependency analysis"时,被 Xcode 视为"无依赖故无需运行",clean build 也不跑
|
||||
- 综合工程成本估算:为保留"目录名编码"机制,需引入额外 Build Phase / 关闭 sandbox / 维护 Input/Output 列表,**全是 Xcode 行为兼容性维护,与项目目标无关**
|
||||
- **决策**:渠道注入存储改用**单一 `ylgamehall/Resources/ChannelConfig.plist`** 含 11 个 string key,与 msext 11 个目录一一对应
|
||||
- 存储介质:plist(iOS 原生)
|
||||
- Bundle 加载:synchronized group 自动收集,零配置
|
||||
- 运行时读取:`BundleConfig.init(bundle:)` 用 `PropertyListSerialization` 反序列化
|
||||
- **理由**:
|
||||
- **契约 100% 等价**:H5 端通过 `app_data.js` 看到的 11 个 JS 全局变量行为完全不变(契约边界在 `BundleConfig.shared.xxx`,与底层存储无关)
|
||||
- **CLAUDE.md 原则 B 落地**:原生内部自由重构,不要照搬旧项目;msext 那套是 iOS 9 / Xcode 14 时代的 hack,Xcode 26 + Swift 6 应当用更现代的存储
|
||||
- **IPA 后处理多渠道分发完全等价**:
|
||||
```bash
|
||||
plutil -replace channel -string "<新渠道 ID>" ChannelConfig.plist
|
||||
plutil -replace market -string "<新市场 ID>" ChannelConfig.plist
|
||||
codesign --force --sign "$IDENTITY" --entitlements "$ENT" <app>.app
|
||||
zip -r <channel>.ipa Payload/
|
||||
```
|
||||
工作量与 msext 的 `mv` 目录名 + 重签完全相同,且 plutil 比 mv 一组路径更可读 / 可自动化
|
||||
- **维护成本显著降低**:单文件、可读、单测可注入 mock bundle、不依赖 Xcode 任何特殊配置
|
||||
- **影响**:
|
||||
- Contract §0.3 描述的 msext 实现仅作为历史参考,新项目不照搬
|
||||
- Design §7.0 / §7.2 重写为 plist 方案
|
||||
- Plan Phase 1.1 简化为"建 plist"单步任务(取代原 1.1.a~d 四步 inject_channel.sh 方案)
|
||||
- `BundleConfig.swift` 接口(11 个公开属性)保持不变,仅 init 实现切换
|
||||
- **守护**:
|
||||
- **`ChannelConfig.plist` 必须保持 11 个 key 完整且类型为 string**;新增 key 视同契约边界变更,需更新 Design / Plan / Contract(如该值被 H5 通过 app_data.js 暴露)
|
||||
- 后处理工具必须**修改 plist 后立即重签**,否则 iOS 拒绝安装
|
||||
- 不允许把渠道值硬编码到 Swift 源码(违背"母包模式"的初衷:一份二进制 N 个渠道)
|
||||
- **回滚条件**:若未来 Xcode / iOS 改动让 plist 方案无法工作(极不可能)或后处理脚本失效,重新评估目录名方案或其它存储介质
|
||||
|
||||
---
|
||||
|
||||
文档完成日期:2026-06-21
|
||||
最后更新:2026-06-21(ADR-006 纯 SPM + Vendor 策略 + ADR-005 极光降级 + Resources/ 目录记录)
|
||||
最后更新:2026-06-21(ADR-007 渠道注入改 ChannelConfig.plist + ADR-006 纯 SPM + Vendor + ADR-005 极光降级 + Resources/ 目录记录)
|
||||
|
||||
Reference in New Issue
Block a user