From 17a8b96cf8b1314e9723fa11546a16fed956b61b Mon Sep 17 00:00:00 2001 From: joywayer Date: Sun, 21 Jun 2026 21:46:19 +0800 Subject: [PATCH] =?UTF-8?q?docs=EF=BC=9A=E4=BE=9D=E8=B5=96=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E6=94=B9=E4=B8=BA=E7=BA=AF=20SPM=20+=20Vendor?= =?UTF-8?q?=EF=BC=8C=E7=A7=BB=E9=99=A4=20CocoaPods=EF=BC=88ADR-006?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 调研发现 Qiniu SDK 已官方支持 SPM(https://github.com/qiniu/objc-sdk v8.9.x),AMap 仍仅支持 CocoaPods 或手动 XCFramework;CocoaPods 1.15.2 不兼容 Xcode 26 的 PBXFileSystemSynchronizedRootGroup,需绕道 Bundler 才能升到 1.16+。综合性价比决定不引入 CocoaPods 工具链,所有闭源 SDK (含 AMap)统一走 Vendor .xcframework 手动接入。 - Design §14.1 三层策略改写为两层(SPM + Vendor),新增 Vendor 接入 标准流程 6 步法 - Design §14.2 AMap 接入方式从 CocoaPods 改为 Vendor,Qiniu 标注 SPM URL 与版本 - Plan §2.3 / §5 Phase 5.1 / §6.1 / §8 同步 AMap Vendor 化 - Plan 新增 ADR-006 完整决策记录 - CLAUDE.md 新增「依赖管理约定」节,禁止引入 CocoaPods --- CLAUDE.md | 11 +++++++ docs/Development-Plan.md | 40 +++++++++++++++++----- docs/H5-Native-Implementation-Design.md | 44 +++++++++++++++++-------- 3 files changed, 74 insertions(+), 21 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 5147c2d..8a3f06d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -34,6 +34,17 @@ daoqi 仓库当前并存两条工作线: --- +## 依赖管理约定 + +新项目采用**纯 SPM + Vendor `.xcframework` 两层管理**,**禁止引入 CocoaPods**(详见 `docs/Development-Plan.md` ADR-006)。 + +- **首选 SPM**:开源 / 官方提供 Swift Package 的库(Sentry / ZIPFoundation / 七牛 `https://github.com/qiniu/objc-sdk` v8.9.x 等) +- **Vendor `.xcframework` 手动**:所有闭源二进制(微信 / QQ / 高德 AMap / opencore-amr 等),按 Design §14.1「Vendor 接入标准流程」放 `Vendor//` + Target → General → Frameworks 加入 + `Vendor//README.md` 记录版本与来源 +- **禁止 CocoaPods**:项目内不放 `Podfile` / `Podfile.lock` / `Pods/` / `.xcworkspace`。工程入口固定为 `ylgamehall.xcodeproj` +- **新增第三方依赖时**:按 SPM → Vendor 顺序尝试,PR 描述写明选型理由;若发现某 SDK 既无 SPM 又无 XCFramework,回到 ADR-006 决策框架重新评估 + +--- + ## Resources/ 目录约定 仓库根目录的 `Resources/` 是新外壳运行所需的**项目方提供资源**统一入库位置,由打包脚本在 Build Phase 引入,**不**直接放进 Xcode 工程内的 `ylgamehall/` 源码目录。 diff --git a/docs/Development-Plan.md b/docs/Development-Plan.md index c862b93..6e67869 100644 --- a/docs/Development-Plan.md +++ b/docs/Development-Plan.md @@ -62,7 +62,7 @@ Contract Design Plan(本文档) | 微信 AppID(`wx586a9b321e56efb7`)+ Universal Links 配置 | 微信回调 | Phase 4 | 项目方 | | QQ OpenSDK `.framework` + AppID | QQ 分享 | Phase 4 | 项目方 | | 后台 `/wechat/login` 中转接口 | secret 不入 IPA 的前提 | Phase 4 | 后台团队 | -| 高德地图 `APIKey` + SDK Pod | 定位 | Phase 5 | 已有 key `b0d4a8e3fcbbcc0dd96283b7df6a4494`,但需新建 Bundle ID 注册 | +| 高德地图 `APIKey` + SDK XCFramework | 定位 | Phase 5 | 已有 key `b0d4a8e3fcbbcc0dd96283b7df6a4494`(但绑死 msext Bundle ID,需用新 Bundle ID 重新申请);SDK 走 Vendor 手动接入(ADR-006),下载页 https://lbs.amap.com/api/ios-location-sdk/download | | 七牛上传 token 颁发接口 | 录音上传 | Phase 3 | 后台团队 | | ~~极光 AppKey~~ | ~~用户统计~~ | **已决策暂不集成(ADR-005),未来启用时再协调** | — | | Sentry DSN | 崩溃监控 | Phase 9 | 项目方(需开通账号) | @@ -464,11 +464,11 @@ Contract Design Plan(本文档) #### 前置 - Phase 1 完成 -- ⏳ 高德 SDK(CocoaPods 接入,APIKey 已有但 Bundle ID 需重新注册) +- ⏳ 高德 SDK XCFramework(Vendor 手动,ADR-006)+ 新 Bundle ID 重新申请 APIKey #### 任务清单 -- [ ] **5.1** 加 Podfile + 引入 `AMapLocation` 9.x +- [ ] **5.1** 从 [高德官方下载页](https://lbs.amap.com/api/ios-location-sdk/download) 拉取最新 `AMapLocationKit.xcframework` + `AMapFoundationKit.xcframework`,放 `Vendor/AMap/`;Target → General → Embed Frameworks 加入;按 Design §14.1 Vendor 接入标准流程操作;`Vendor/AMap/README.md` 记录版本号 / 下载日期 - [ ] **5.2** Info.plist 加 `NSLocationWhenInUseUsageDescription = "{gamehallname}需要访问您的位置以提供本地化服务"` - [ ] **5.3** `Source/SDK/AMap/AMapWrapper.swift`:启动期 `updatePrivacyShow` + `updatePrivacyAgree` + `apiKey` 设置 - [ ] **5.4** `Source/Location/LocationService.swift`(actor) @@ -680,10 +680,10 @@ H5 调 `OpenurlTitleData` 打开弹层 WebView,弹层内 H5 用 `window.settin | 集成方式 | SDK | 集成 Phase | |---------|-----|----------| -| **SPM 优先** | ZIPFoundation / Sentry / 七牛 v8+ | Phase 1 / 9 / 3 | -| **CocoaPods** | 高德 AMap | Phase 5 | -| **Vendor `.framework`** | 微信 / QQ;闲聊 / Agora / **JAnalytics** 均暂不集成(Noop 占位) | Phase 4 | +| **SPM 优先** | ZIPFoundation / Sentry / 七牛 v8.9.x(`https://github.com/qiniu/objc-sdk`) | Phase 1 / 9 / 3 | +| **Vendor `.xcframework`** | 微信 / QQ / **高德 AMap**(ADR-006);闲聊 / Agora / JAnalytics 均暂不集成(Noop 占位) | Phase 4 / 5 | | **Vendor `.a`** | opencore-amr | Phase 3 | +| ~~CocoaPods~~ | **不引入**(ADR-006,纯 SPM + Vendor 两层管理) | — | 每集成一个 SDK 立即提交一个独立 commit(CLAUDE.md "及时提交" 规则)。 @@ -792,7 +792,7 @@ H5 调 `OpenurlTitleData` 打开弹层 WebView,弹层内 H5 用 `window.settin - [ ] 4.12 远程图片分享 ### Phase 5 定位 -- [ ] 5.1 AMap Pod +- [ ] 5.1 AMap Vendor 接入(XCFramework) - [ ] 5.2 定位权限文案 - [ ] 5.3 AMapWrapper 启动注册 - [ ] 5.4 LocationService @@ -895,7 +895,31 @@ H5 调 `OpenurlTitleData` 打开弹层 WebView,弹层内 H5 用 `window.settin 5. Info.plist 加 AppKey + 隐私授权文案 - **复盘节点**:上线 3 个月后业务方根据数据需求决定是否启用 +### ADR-006:纯 SPM + Vendor `.xcframework` 两层依赖管理,不引入 CocoaPods(2026-06-21) +- **背景**:Design 原方案是"SPM 优先 + CocoaPods 兜底 + Vendor 手动"三层策略,CocoaPods 仅服务于高德 AMap 定位 SDK 一个依赖 +- **触发事件**:尝试 `pod init` 时,CocoaPods 1.15.2(Homebrew 上的最新版)自带的 `xcodeproj` gem 1.24.0 不识别 Xcode 26.5 默认的 `PBXFileSystemSynchronizedRootGroup`,pod init 直接抛 `unknown ISA` 异常 +- **调研结论**: + - 七牛 SDK 已官方支持 SPM(`https://github.com/qiniu/objc-sdk`,v8.9.x),原计划的"七牛走 CocoaPods"完全无必要 + - 高德 AMap 定位 SDK 官方未提供 SPM(截至 2026-06),但提供 XCFramework 直接下载 + - CocoaPods 1.16+ 才支持新 ISA,但 Homebrew 未跟进;需绕道 Bundler + Gemfile / brew --HEAD / rbenv 自管 Ruby,均增加工具链复杂度 +- **决策**:依赖管理简化为**两层 — SPM 优先 + Vendor `.xcframework` 手动**,**完全不引入 CocoaPods** + - SPM:ZIPFoundation / Sentry / Qiniu / WVJB JS(资源文件) + - Vendor:微信 / QQ / **高德 AMap** / opencore-amr;闲聊 / Agora / JAnalytics 暂不集成(Noop 占位) +- **理由**: + - 项目实际只有高德 AMap 1 个 SDK 让 CocoaPods 必要;为 1 个依赖引入整套 Ruby + Bundler + CocoaPods + workspace 工具链 + Xcode 26 兼容性维护,性价比低 + - 闭源 SDK 本就是 Vendor 性质(微信 / QQ / 闲聊 / JAnalytics / opencore-amr),AMap 走同款流程不增加心智负担 + - 生态趋势:Google 已宣布 2026 Q2 后 iOS SDK 全部停止 CocoaPods 支持;CocoaPods 维护活跃度下降 + - 工程入口仍是 `.xcodeproj`(不需要 `.xcworkspace`),团队 / CI 配置不需要切换 +- **接入标准流程**(Vendor,写入 Design §14.1): + 1. 二进制放 `Vendor//.xcframework` + 2. Target → General → Frameworks → `+`,动态库选 "Embed & Sign",静态库 "Do Not Embed" + 3. Library / Header Search Paths 用 `$(PROJECT_DIR)/Vendor/` 相对路径 + 4. `Vendor//README.md` 记录版本号 / 下载日期 / 官方更新页 URL + 5. 二进制入 git(项目规模可控,暂不强制 git-lfs) +- **回滚条件**:未来某天出现 ≥ 3 个仅有 podspec 而无 SPM / XCFramework 的 SDK 必接需求时,重新评估是否引入 CocoaPods +- **维护责任**:AMap 升级(年度级)→ 下载新 XCFramework 覆盖 `Vendor/AMap/` + 更新 README + 跑契约测试 + --- 文档完成日期:2026-06-21 -最后更新:2026-06-21(ADR-005 极光降级 + Resources/ 目录记录) +最后更新:2026-06-21(ADR-006 纯 SPM + Vendor 策略 + ADR-005 极光降级 + Resources/ 目录记录) diff --git a/docs/H5-Native-Implementation-Design.md b/docs/H5-Native-Implementation-Design.md index 401cb87..2611e42 100644 --- a/docs/H5-Native-Implementation-Design.md +++ b/docs/H5-Native-Implementation-Design.md @@ -54,7 +54,7 @@ | H5 桥 | 自研 Swift 桥(实现 WebViewJavascriptBridge 的 JS 端协议作为契约边界)+ 弹层注入 `window.settings.*` polyfill | | 网络 | **URLSession + async/await + Codable**,自研 HTTPClient,Endpoint 协议化 | | 持久化 | UserDefaults + FileManager;**不引入 CoreData/SQLite** | -| 依赖管理 | **SPM 优先**,微信/QQ/声网/高德等无 SPM 的用 CocoaPods | +| 依赖管理 | **纯 SPM + Vendor `.xcframework` 手动**(完全不引入 CocoaPods 工具链);开源 / 官方 SPM 走 SPM,闭源二进制无 SPM 的走 Vendor 手动 | | 架构 | **Modular Clean Architecture** + **Coordinator 路由** | | 监控 | **默认接入 Sentry**(崩溃 + 性能 + breadcrumb);提供编译开关 `SENTRY_ENABLED` 作为关闭逃生口 | | 包名 / Target | 与现 `msext` 解耦,新名 `Daoqi`(支持企业签 / 超签 / TF 同包并存) | @@ -2078,22 +2078,40 @@ done ## 14. 第三方 SDK 集成清单 -### 14.1 依赖管理策略:SPM 优先 + CocoaPods 兜底 + Vendor 手动 +### 14.1 依赖管理策略:SPM 优先 + Vendor `.xcframework` 手动 -新项目采用**三层混合策略**,按优先级从高到低: +新项目采用**两层混合策略**,**完全不引入 CocoaPods 工具链**: -1. **首选 SPM** —— Apple 官方,Xcode 原生集成,无第三方工具依赖,无衍生工程文件污染,新人 clone 即用。适用于:Sentry、ZIPFoundation、Agora 4.x、七牛 v8+ 等**已发布官方 Swift Package** 的库 -2. **CocoaPods 兜底** —— 用于"有 podspec 但暂无 SPM 包"的 SDK,主要是高德定位、部分 Agora 旧版兼容场景。Podfile 仅写必须的 Pod,不引入大规模 transitive 依赖 -3. **Vendor 手动** —— `Vendor/` 目录直接放闭源 `.framework` / `.a`,xcconfig 配置 link flag。适用于:微信 OpenSDK、QQ OpenSDK、闲聊 SDK(暂不集成)、JAnalytics(暂不集成)、opencore-amr 等**官方不提供 SPM / Pod 的私有 framework** +1. **首选 SPM** —— Apple 官方,Xcode 原生集成,无第三方工具依赖,无衍生工程文件污染,新人 clone 即用。适用于:Sentry、ZIPFoundation、七牛 v8.9+ 等**已发布官方 Swift Package** 的库 +2. **Vendor 手动** —— `Vendor/` 目录直接放闭源 `.xcframework` / `.framework` / `.a`,target Build Phase 加入 `Link Binary With Libraries` + `Embed Frameworks`(动态库需要)。适用于:微信 OpenSDK、QQ OpenSDK、AMap 高德定位、opencore-amr 等**官方不提供 Swift Package** 的二进制 + +#### 为什么不引入 CocoaPods + +ADR-006 决策记录(见 `docs/Development-Plan.md` §9)详细背景。简短理由: + +- **唯一会触发 Pods 的 SDK 是 AMap 高德定位**(其它 SDK 要么 SPM,要么本就是 Vendor 手动);为 1 个 SDK 引入完整 Ruby + Bundler + CocoaPods 工具链性价比低 +- **Xcode 26 与 CocoaPods 的兼容性问题**:Xcode 26 默认 `PBXFileSystemSynchronizedRootGroup`,Homebrew 的 CocoaPods 1.15.2 自带 `xcodeproj` gem 1.24.0 不识别,需绕道 Bundler + Gemfile 锁定 1.16+ +- **生态趋势**:Google 已宣布 2026 Q2 后停止 iOS SDK 的 CocoaPods 支持,Firebase / GoogleMaps 转 SPM +- **AMap 手动接入成本可控**:闭源 XCFramework 升级频率年度级,每次 30 分钟手动操作,对比 CocoaPods 工具链长期维护更低成本 #### 为什么不选纯 SPM -微信 / QQ / 闲聊 / JAnalytics 等闭源 SDK 官方至今(2026)未提供 Swift Package,强行纯 SPM 化需要自己包一层私有 Package,维护成本反而上升。 -#### 为什么不选纯 CocoaPods -Sentry / ZIPFoundation 等纯 Swift 包走 Pods 需绕一道 podspec,失去 SPM 的"Xcode 原生 resolve、增量缓存、零 Ruby 依赖"优势;且 CI 上每次 `pod install` 都拉远端,慢且不稳定。 +微信 / QQ / 闲聊 / JAnalytics / AMap 等闭源 SDK 官方至今(2026)未提供 Swift Package,强行纯 SPM 化需要自己包一层私有 Wrapper Package,维护成本反而上升。 #### 决策方法 -新增第三方依赖时,**按 SPM → Pods → Vendor 顺序尝试**,前一项无法满足才退到下一项。任何团队成员改变接入方式(如把某 SDK 从 SPM 迁到 Pods)需要在 PR 描述里写明降级理由。 + +新增第三方依赖时,**按 SPM → Vendor 顺序尝试**,前一项无法满足才退到下一项。任何团队成员把已 SPM 化的 SDK 改成 Vendor,需在 PR 描述里写明理由。**禁止引入 CocoaPods**(违反 ADR-006)。 + +#### Vendor 接入标准流程 + +闭源 SDK Vendor 化按以下顺序操作,保持工程一致性: + +1. SDK 二进制放 `Vendor//.xcframework`(动态库)或 `Vendor//lib.a` + 头文件目录(静态库) +2. Xcode → Target → General → Frameworks, Libraries, and Embedded Content → `+` 加入,动态库选 "Embed & Sign",静态库选 "Do Not Embed" +3. 静态库 `Library Search Paths` / 头文件 `Header Search Paths` 写到 `$(PROJECT_DIR)/Vendor/` 相对路径,**不写绝对路径** +4. 新增 module bridging header(若是 Swift 调 OC SDK) +5. `Vendor//README.md` 写明版本号 / 下载日期 / 官方更新页 URL +6. SDK 二进制入 git(配合 git-lfs 处理大文件;当前项目不强制 lfs,因为 SDK 体积可控) ### 14.2 SDK 详细清单 @@ -2103,8 +2121,8 @@ Sentry / ZIPFoundation 等纯 Swift 包走 Pods 需绕一道 podspec,失去 SPM | QQShare | Vendor | latest | ✅ 启用,启动注册 | | **Xianliao** | — | — | ⏸️ **暂不集成**(`ShareCenter.xianliao = NoopSharePlatform`,§8.2)。当 H5 调 `friendsShare...({sharetype:"3"})` 时,Noop 立即回 `sharesuccess`,业务流不中断。启用时把 Vendor `.framework` 放入 + 把 `NoopSharePlatform` 换为 `XianliaoShare` | | **Agora RTC** | — | — | ⏸️ **暂不集成**(`VideoRoom = NoopVideoRoom`,§8.3)。子游戏视频房间 3 个 handler 当前 stub 实现。启用时按 §8.3 末尾的 4 步说明切换 | -| AMap Location | CocoaPods(官方暂无 SPM) | 2.9+ | ✅ 启用,启动注册 | -| Qiniu SDK | **SPM**(官方 v8+ 支持 Swift Package) | latest | ✅ 启用,录音上传时初始化 | +| AMap Location | Vendor `.xcframework`(官方暂无 SPM,ADR-006 决策不引 CocoaPods) | latest 2.x | ✅ 启用,启动注册;下载页 `https://lbs.amap.com/api/ios-location-sdk/download` | +| Qiniu SDK | **SPM** `https://github.com/qiniu/objc-sdk` | **8.9.x** | ✅ 启用,录音上传时初始化;import `QiniuSDK` | | opencore-amr | 静态 `.a`(直接接入 — H5 端通过此库收发的 AMR 音频是契约边界) | 2014 版本 | ✅ 启用 | | ZIPFoundation | **SPM** | latest | ✅ 启用 | | Sentry-Cocoa | **SPM**(默认 ON,编译开关 `SENTRY_ENABLED` 提供逃生口) | latest | ✅ 启用,启动并发注册 | @@ -2183,7 +2201,7 @@ Sentry / ZIPFoundation 等纯 Swift 包走 Pods 需绕一道 podspec,失去 SPM - 本项目独立目录(`/Daoqi/`),不与 `/msext/` 共用任何文件 - `gamehall.zip` 直接复用现网产物,放进 `Resources/` -- `Vendor/` 下的 SDK 二进制可以直接从 `msext/Pods/` / `msext/Frameworks/` 拷贝 +- `Vendor/` 下的 SDK 二进制可参照 msext 的 `Pods/` / `Frameworks/` 找到对应版本,但**不要走 CocoaPods 流程**(ADR-006);从官方下载页或 msext 仓库直接拷贝 `.xcframework` / `.framework` / `.a` 到 `Vendor/` - 渠道配置脚本可与现 `msext` 共享同一 Jenkins job 参数表 - 灰度期间旧 msext 外壳保持线上不动(避免双线推送);新外壳全量切流稳定后再评估是否归档 msext target