# ReadSDK 代码规范 ## 适用范围 本文档适用于 `ReadSDK` 新增代码与重构代码。 - 规范覆盖 `Sources/` 与 `RDReaderDemo/` 中的 Swift 代码。 - 命名、分层与职责边界以 SDK 可维护性和可扩展性为优先。 - 本文档分为两类内容: - `已观察到的约定`:当前工程常见写法。 - `建议统一的规范`:后续统一执行的规则。 ## 新架构目录与职责 ### 已观察到的约定 - `Sources/RDReaderView`:阅读核心容器、翻页能力与基础视图。 - `EPUBCore`:EPUB 解析、资源定位、导航状态、分页与会话协调。 - `EPUBTextRendering`:文本渲染引擎与分页支持。 - `EPUBUI`:可开箱即用的 Reader UI 层。 - `RDReaderDemo`:示例应用与调试入口。 ### 建议统一的规范 - 新增业务能力优先归入 `Sources/RDReaderView` 下的对应模块目录。 - `Core` 层只承载解析、会话、状态机与通用能力,不写页面级交互。 - `UI` 层只承载展示、事件分发和轻量状态同步,不直接处理底层解析逻辑。 - 若模块持续膨胀,优先在当前模块下继续拆分子文件,不跨目录散落实现。 ## 命名规范 ### 已观察到的约定 - 当前工程历史命名以 `SS`、`RDEPUB` 开头。 - 控制器常用 `...Controller`,视图常用 `...View`,会话对象常用 `...Session`。 - 扩展文件采用 `类型名+功能域.swift`,如 `Parser+Archive.swift`。 - 事件方法常使用 `Action` 结尾,例如 `pageTapAction()`、`themeChangeAction()`。 - 绑定数据的方法常使用 `bind`、`update`、`refresh`、`configure` 等动词。 ### 建议统一的规范 - **所有新增类型必须以 `RD` 开头。** - EPUB 相关类型统一以 `RDEPUB` 开头。 - 方法名、变量名沿用 Swift 小驼峰,不增加额外前缀。 - 类型名应反映职责,不使用过宽泛的后缀;只有真正承担协调逻辑时才使用 `Manager`、`Handler`。 - 事件处理方法统一使用"对象/意图 + Action"命名,例如 `pageTapAction`、`themeChangeAction`。 - 数据绑定方法优先使用以下语义: - `bind...`:将模型绑定到视图或模块。 - `update...`:增量刷新已有界面或状态。 - `configure...`:一次性配置样式或依赖。 - `refresh...`:重新拉取或重建数据状态。 - 避免新增拼写不一致的方法名;若发现历史命名拼写错误,新增代码必须使用正确拼写,旧接口修复时应配合调用点一起调整。 命名示例: - `RDReaderView` - `RDEPUBParser` - `RDEPUBPublication` - `RDEPUBReadingSession` - `RDEPUBReaderController` - `RDEPUBReaderTheme` 扩展文件命名示例: ```text RDEPUBParser.swift RDEPUBParser+Archive.swift RDEPUBParser+Package.swift RDEPUBParser+TOC.swift RDEPUBParser+Resources.swift RDEPUBWebView.swift RDEPUBWebView+Configuration.swift RDEPUBWebView+Reflowable.swift ``` ## 分层与职责边界 ### 已观察到的约定 - 阅读入口负责容器装配、翻页模式切换和事件分发。 - EPUB 核心层负责解析、导航、分页、资源读取与定位。 - 渲染层负责 HTML/富文本渲染与分页支持。 - UI 层负责主题、工具栏、目录、设置等交互能力。 ### 建议统一的规范 - `RD...Controller`:负责页面级编排与流程调度,不承载复杂渲染细节。 - `RD...View`:负责展示与局部交互,不承载完整业务流程。 - `RDEPUB...Core`:负责解析、会话状态与数据模型,不依赖具体页面。 - 配置、主题、定位、进度模型统一下沉为 `struct`。 - 跨层通信优先通过会话层或协议,不做跨层直接写状态。 ## UI 与布局规范 ### 已观察到的约定 - 视图多采用 `lazy var` 初始化,并在闭包内完成默认配置。 - 自定义 View 通常在 `init(frame:)` 或业务绑定后调用 `initView()` 完成视图树搭建。 - 复杂页面使用分区 extension 组织代理与事件实现。 - 页面或组件内部按职责拆分样式方法,例如 `topBarStyle()`、`contentStyle()`。 - 页面经常通过回调闭包把交互抛给外层,例如 `pageChangeCallback`、`selectionCallback`。 - 模块内存在多处布局方式混用情况。 ### 建议统一的规范 - SDK 层新增 UI 使用 Auto Layout 原生约束,Demo 层可使用 SnapKit;单文件内不混用多套布局体系。 - 视图层初始化顺序保持一致: - 定义属性与子视图 - 在 `initView()` 中组装视图树 - 在独立方法中拆分样式和状态刷新逻辑 - 当约束会被多次切换时: - 首次创建使用 `makeConstraints`(SnapKit)或 `NSLayoutConstraint` - 重建结构使用 `remakeConstraints`(SnapKit)或先移除再添加 - 仅修改常量时使用 `updateConstraints`(SnapKit)或修改 `constant` 属性 - 布局分支明显时,优先拆成语义化私有方法,不要把所有状态分支堆在一个超长方法里。 - 对外暴露的 UI 刷新入口建议以 `bind` 或 `update` 开头,避免把布局细节暴露给调用方。 - 交互事件通过闭包或 delegate 抛出,避免子视图持有上层业务依赖。 ## 交互与状态处理规范 ### 已观察到的约定 - 事件处理使用 `@objc` + selector。 - 异步回调中广泛使用 `[weak self]`。 - 状态判断常通过 `guard` 提前返回。 ### 建议统一的规范 - 按钮、通知、系统回调放入独立 extension 分组。 - 异步闭包默认先使用 `[weak self]`,仅在必要时改强引用。 - 多前置条件入口统一先 `guard` 校验,减少嵌套。 - 导航与进度恢复统一通过 `RDEPUBReadingSession` 协调。 ## 代码风格细则 ### 已观察到的约定 - extension 分区较常见。 - 解析与渲染模型多数采用 `struct`。 - 存在少量历史命名不统一与可选值处理不一致情况。 ### 建议统一的规范 - 默认遵循”最小可见性”:`private` > `fileprivate` > `internal` > `public`。 - 纯数据模型使用 `struct` + `Codable` + `Equatable`。 - 服务对象使用 `final class`,避免无意义继承。 - 错误类型统一使用 `enum + LocalizedError`。 - 新增代码避免强制解包;若当前上下文无法避免,至少先在上层收敛边界。 - 统一优先使用 `guard` 做前置失败处理,减少深层嵌套。 - extension 的拆分原则以”单一职责”优先: - 事件处理一组 - 代理 / DataSource 实现一组 - 工具方法一组 - 通知适配一组 - 调试代码继续使用 `#if DEBUG` 包裹,不把调试边框、日志、测试分支直接带入正式逻辑。 ## 文件组织规范 ### 已观察到的约定 - 目录按阅读容器、EPUB Core、渲染、UI 分层组织。 - 大类通过 `+Extension` 文件拆分职责。 ### 建议统一的规范 - 目录保持以下分层,不跨层放置实现: - `Sources/RDReaderView/EPUBCore` - `Sources/RDReaderView/EPUBTextRendering` - `Sources/RDReaderView/EPUBUI` - 单文件建议不超过 `600` 行;超出后按职责拆分 extension 文件。 - extension 文件命名统一 `RD类型名+功能域.swift`。 ## 注释规范 - 注释、错误提示、日志统一使用中文。 - 关键流程方法保留”为什么这样做”的注释,不写重复代码字面行为的注释。 - 调试输出统一放在 `#if DEBUG` 下。 - 新代码保留有信息量的注释,避免重复描述显而易见的代码行为。 ## 已观察到的项目模式 ### 模式 1:统一入口 + 扩展拆分 - `RDEPUBParser` 负责 EPUB 解析主入口,不同能力拆到 `+Archive`、`+Package`、`+TOC`、`+Resources` 等扩展文件。 - `RDEPUBReadingSession` 负责阅读会话主入口,状态管理、分页、定位等能力拆到扩展文件。 - 该模式适合继续用于解析器、会话管理、控制器工具方法等横向能力。 ### 模式 2:页面编排在 Controller,局部交互下沉到 View - `RDEPUBReaderController` 负责阅读页整体编排:翻页容器装配、工具栏切换、阅读位置恢复。 - `RDReaderView` 负责分页容器布局与翻页交互,并通过 DataSource / Delegate 把数据需求交回外层。 - 该模式保持 Controller 管流程、View 管展示的职责分离。 ### 模式 3:列表与容器逻辑通过扩展拆开 - 复杂容器将 `UICollectionViewDataSource`、`UICollectionViewDelegate`、`UICollectionViewDelegateFlowLayout` 分别拆分到 extension。 - 该模式降低单文件中主逻辑与代理逻辑的耦合,适合继续用于任何包含列表或容器的组件。 ### 模式 4:渲染路径抽象 - `RDEPUBReadingProfile` 根据 EPUB 特征自动选择渲染路径(`webFixedLayout` / `webInteractive` / `textReflowable`)。 - 新增渲染相关功能时,必须评估对三种路径的覆盖情况。 ## 待统一项 - 当前访问控制级别存在混用:同一类里 `public`、默认 `internal`、`private` 并存,建议后续新增代码默认从最小可见范围开始声明。 - 当前存在少量强制解包,建议新增代码优先通过前置校验收敛风险。 - 当前存在拼写不一致问题,建议后续新增代码统一使用标准英文单词,旧接口如需修复应配合调用点一起调整。 - 当前部分注释偏”过程说明”或遗留调试注释,建议新代码保留有信息量的注释。 - 当前个别 View 在数据绑定阶段再次调用 `initView()` 重建界面,这种方式在复杂组件中容易引入重复添加子视图或状态不一致。建议新增组件优先区分”初始化视图结构”和”刷新数据状态”两个阶段。 ## 禁忌事项 | 禁忌 | 替代做法 | |------|----------| | 新增类型不加 `RD` 前缀 | 所有新增类型统一 `RD` / `RDEPUB` 前缀 | | UI 层直接拼装解析状态 | 通过 `RDEPUBReadingSession` 获取状态 | | 控制器直接操作底层解析细节 | 通过 `RDEPUBPublication`、`RDEPUBParser` 暴露接口 | | 强制解包可选值 | `guard let` / `if let` | | 用页号单独恢复阅读进度 | 统一使用 `href + progression` | ## 使用建议 - 新增功能前先确定目录归属和职责边界。 - 命名先定前缀再落代码:类型一律 `RD` 开头。 - 若需迁移历史 `SS` 前缀,按模块渐进替换,优先替换新增与重构触达文件。 ## 旧 SS 命名迁移到 RD 的分阶段执行清单 ### 阶段 0:冻结新增 SS 命名(立即执行) - 目标:从当前时点开始,不再引入新的 `SS`/`RDEPUB` 类型名。 - 动作: - 新增类型统一使用 `RD`/`RDEPUB` 前缀。 - Code Review 增加命名检查项:发现新增 `SS` 命名必须驳回。 - 在 PR 模板中加入“本次是否新增旧前缀命名”勾选项。 - 验收: - 新提交代码中,新增类型 `SS` 前缀数量为 `0`。 ### 阶段 1:建立迁移映射表(第 1 周) - 目标:明确“旧名 -> 新名”一一映射,避免多人并行改名冲突。 - 动作: - 统计核心公开类型、内部核心类型、测试类型三类清单。 - 建立命名映射表,例如: - `RDReaderView` -> `RDReaderView` - `RDEPUBParser` -> `RDEPUBParser` - `RDEPUBReadingSession` -> `RDEPUBReadingSession` - 对外 API 单独标记“需兼容过渡”的类型。 - 验收: - 映射表覆盖全部高频核心类型,且团队评审通过。 ### 阶段 2:先迁移内部类型(第 2-3 周) - 目标:优先改内部实现,降低外部兼容压力。 - 动作: - 按模块分批迁移:`EPUBCore` -> `EPUBTextRendering` -> `EPUBUI`。 - 每批次只改一个子模块,避免超大 PR。 - 同步修复调用点、扩展文件名与注释中的旧命名。 - 验收: - 目标模块内类型命名全部满足 `RD` 规则。 - 编译通过,Demo 阅读主流程可用。 ### 阶段 3:迁移公开 API 并保留兼容层(第 3-4 周) - 目标:完成对外接口改名,同时给接入方提供平滑升级窗口。 - 动作: - 对外公开类型切换为 `RD` 命名。 - 旧公开类型保留兼容别名,并标注废弃说明(`deprecated`)。 - 在 Release Note 提供“旧名/新名对照表”和迁移示例。 - 验收: - 新接入示例仅使用 `RD` 命名。 - 旧接入代码在兼容期内无需立即改动即可编译。 ### 阶段 4:清理兼容层与收口(下一主版本) - 目标:在约定主版本移除旧前缀,完成命名收口。 - 动作: - 删除 `SS` 兼容别名与过渡代码。 - 清理文档、注释、示例工程中的旧前缀残留。 - 对外发布最终迁移公告与升级说明。 - 验收: - 工程内无 `SS`/`RDEPUB` 类型定义残留。 - 文档与示例代码全部为 `RD`/`RDEPUB` 命名。 ### 迁移过程约束 - 每次迁移 PR 必须包含: - 命名改动清单 - 影响范围说明 - 回归验证结果(编译、Demo 主流程、关键阅读路径) - 禁止在同一 PR 中同时做“大规模命名迁移 + 业务逻辑重构”。 - 若改名会影响外部接入,必须先补迁移文档再合并代码。