Reorganize docs and update reader search flows
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
# 架构分析:WXRead 参考 vs ReadViewSDK 当前
|
||||
|
||||
**分析日期:** 2026-05-23
|
||||
**分析基础:** `Doc/WXRead/decompiled-doc.md`、`Doc/WXRead/resources-doc.md`、`Doc/WXRead/读书EPUB阅读器实现架构.md`
|
||||
**状态:** Decisions captured
|
||||
|
||||
---
|
||||
|
||||
<domain>
|
||||
## 分析范围
|
||||
|
||||
对比读书 (WeRead v10.0.3) 逆向架构与当前 ReadViewSDK 项目的结构性差距,聚焦四大方向:分页引擎集成、CSS 处理、渲染架构、字体系统。
|
||||
|
||||
读书 EPUB 渲染的核心架构特征:
|
||||
- Path A (EPUB): 纯 CoreText 渲染 — `WRPageView.drawRect:` → `CTFrameDraw`,不用 UITextView/UILabel
|
||||
- 4 级语义断页:`WRCoreTextLayouter` + `WRCoreTextLayoutFrame` (语义边界 > 附件边界 > 块边界 > 帧限制)
|
||||
- 5 层 CSS 级联:`default.css < replace.css < dark.css < EPUB 内嵌 < 用户设置`
|
||||
- 字符级位置精度:`WREpubPositionConverter` (fileIndex, row, column) ↔ 全局字符偏移
|
||||
- 标注直接在 CTFrame 层叠加绘制,搜索高亮同理
|
||||
|
||||
</domain>
|
||||
|
||||
<decisions>
|
||||
## 已决事项
|
||||
|
||||
### Area 1: 分页引擎集成(P0)
|
||||
|
||||
| 决策 | 说明 |
|
||||
|------|------|
|
||||
| 将 `RDEPUBTextLayouter` 集成到 `RDEPUBTextBookBuilder` | 内部调用 `rd_paginatedFrames(size:)` 替代旧的 `ss_pageRanges(size:)`,外部 API 不变 |
|
||||
| 分页元数据暴露到 `RDEPUBTextPage` | 新增 `metadata: RDEPUBTextPageMetadata` 字段(含 breakReason/blockKinds/semanticHints),对外暴露分页质量数据 |
|
||||
| `RDEPUBTextChapterPaginationDiagnostic` 增强 | 透传 RDEPUBTextLayoutFrame 的诊断信息 |
|
||||
| 分页缓存 | 按 `bookID + fontSize + lineHeightMultiple + contentInsets` 生成缓存 key,缓存完整 `RDEPUBTextBook` 到磁盘 |
|
||||
|
||||
### Area 2: CSS `<link>` 外部样式表处理(P0)
|
||||
|
||||
| 决策 | 说明 |
|
||||
|------|------|
|
||||
| 预处理内联 CSS | 渲染前扫描 HTML 中 `<link>` 标签,从 EPUB 解压目录读取 CSS 内容,注入 `<style>` 替换 `<link>` |
|
||||
| 实现 5 层 CSS 级联 | `RDEPUBTextStyleSheetBuilder` 实现 `default < replace < dark < epub-embedded < user` 五层合并,使用已定义的 `RDEPUBTextStyleSheetPackage`/`RDEPUBTextStyleSheetLayer` |
|
||||
|
||||
### Area 3: CoreText 直接绘制迁移(P1 — 大架构变更)
|
||||
|
||||
| 决策 | 说明 |
|
||||
|------|------|
|
||||
| 直接替换 UITextView | 新实现完全替代 `RDEPUBTextContentView`,不保留 UITextView 渐进迁移路径 |
|
||||
| CoreText 原生选区 | `CTLineGetStringIndexForPosition` 坐标 hit test + 自定义选区绘制,不依赖 UITextView 选区 |
|
||||
| 标注渲染:CGContext 装饰层 | drawRect 中 CTFrameDraw 绘制文本后,遍历当前页 RDEPUBHighlight,CGContext 绘制背景矩形(highlight)/ 下划线(underline) |
|
||||
| 搜索高亮:CGContext 叠加绘制 | 不修改底层 attributedString,在 drawRect 中根据匹配范围直接绘制高亮背景 |
|
||||
|
||||
### Area 4: 字体系统(P1 — 延迟到后续版本)
|
||||
|
||||
| 决策 | 说明 |
|
||||
|------|------|
|
||||
| 方案:内嵌固定字体集 | SDK bundle 内嵌常用中文字体,Settings 面板新增字体选择。不做 CDN 动态下载 |
|
||||
| 本版本范围:暂不做 | 字体切换功能延迟到后续迭代 |
|
||||
|
||||
</decisions>
|
||||
|
||||
<canonical_refs>
|
||||
## 关键参考文档
|
||||
|
||||
**WXRead 逆向参考(必须阅读):**
|
||||
- `Doc/WXRead/decompiled-doc.md` — 44 个逆向文件的职责说明
|
||||
- `Doc/WXRead/resources-doc.md` — CSS/JS 资源文件清单与职责
|
||||
- `Doc/WXRead/读书EPUB阅读器实现架构.md` — 双渲染引擎架构总览
|
||||
- `Doc/WXRead/decompiled/WRCoreTextLayoutFrame.m` — 跨页避让、装饰元素、搜索高亮实现
|
||||
- `Doc/WXRead/decompiled/WRCoreTextLayouter.m` — 4 级语义断页配置
|
||||
- `Doc/WXRead/decompiled/WRPageView.m` — CoreText 直接绘制参考
|
||||
- `Doc/WXRead/decompiled/WREpubTypesetter.m` — CSS 级联合并参考
|
||||
- `Doc/WXRead/resources/css/replace.css` — 5 层 CSS 参考
|
||||
|
||||
**当前项目代码(已有的基础设施):**
|
||||
- `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextLayouter.swift` — 已实现 4 级语义断页,待集成
|
||||
- `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextLayoutFrame.swift` — 帧模型,含 breakReason/semanticHints
|
||||
- `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift` — 已定义 `RDEPUBTextStyleSheetPackage`/`RDEPUBTextStyleSheetLayer`,未完全使用
|
||||
- `Sources/RDReaderView/EPUBTextRendering/RDEPUBTextBookBuilder.swift` — 当前可能仍在用旧的 `ss_pageRanges`
|
||||
- `Sources/RDReaderView/EPUBUI/RDEPUBTextContentView.swift` — UITextView 实现,待替换为 CoreText
|
||||
|
||||
</canonical_refs>
|
||||
|
||||
<roadmap_mapping>
|
||||
## 与现有 Roadmap 的映射
|
||||
|
||||
| 决策 | 对应 Phase | 说明 |
|
||||
|------|-----------|------|
|
||||
| RDEPUBTextLayouter 集成 | Phase 8 (08-02) | 分页质量改善的核心 |
|
||||
| 分页元数据暴露 | Phase 8 (08-03) | 分页诊断输出的一部分 |
|
||||
| 分页缓存 | Phase 8 (08-01) | 缓存键与失效策略 |
|
||||
| CSS `<link>` 内联 | Phase 7 范畴外 | 当前 Roadmap 未覆盖,需新增 phase 或合并到 Phase 8 |
|
||||
| 5 层 CSS 级联 | Phase 7 + Phase 8 | Phase 7 已完成属性闭环,级联是后续增强 |
|
||||
| **CoreText 直接绘制** | **v1.2 或 v2.0** | **超出 v1.1 Roadmap,需要独立 milestone** |
|
||||
| CoreText 原生选区 | 随 CoreText 迁移 | 同上 |
|
||||
| 标注 CGContext 绘制 | 随 CoreText 迁移 | 同上 |
|
||||
| 字体系统 | 未来版本 | 延迟 |
|
||||
|
||||
**关键发现:** CoreText 直接绘制迁移是最大的架构变更,超出 v1.1 的增量改进范围。建议作为 v1.2 独立 milestone 规划。
|
||||
|
||||
</roadmap_mapping>
|
||||
|
||||
<deferred>
|
||||
## 延迟事项
|
||||
|
||||
- **字体系统**:内嵌固定字体集方案已决,延迟到后续版本
|
||||
- **位置精度提升**(字符级锚点):P2,与 CoreText 迁移关联
|
||||
- **章节数据模型内聚**(WRChapterData 模式):P2,架构清晰度改进
|
||||
- **TTS / DRM / Pencil / 多栏排版**:P3,独立功能模块
|
||||
- **翻页控制器健壮性**(UIPageViewController crash patch):P2,当前未遇到相关崩溃
|
||||
|
||||
</deferred>
|
||||
|
||||
---
|
||||
|
||||
*分析范围:WXRead 逆向文档 → ReadViewSDK 架构差距*
|
||||
*产出:4 个 Area、14 项决策、1 项延迟*
|
||||
+52
-29
@@ -1,6 +1,6 @@
|
||||
# ReadViewSDK 系统架构文档
|
||||
|
||||
> 最后更新:2026-06-04
|
||||
> 最后更新:2026-06-09
|
||||
|
||||
---
|
||||
|
||||
@@ -67,22 +67,27 @@ ReadViewSDK 是一个 iOS EPUB 阅读器 SDK,支持文本重排(Reflowable
|
||||
用户点击书籍
|
||||
│
|
||||
▼
|
||||
RDEPUBReaderController.openBook(epubURL:)
|
||||
RDEPUBReaderController(epubURL:delegate:persistence:) // 初始化,传入 epubURL
|
||||
│
|
||||
├─ RDEPUBParser.parse(epubURL:) // 解压 ZIP → 解析 OPF → 构建 spine/TOC
|
||||
│ └─ extractArchiveIfNeeded() // ZIPFoundation 解压到 Caches
|
||||
│ └─ parseContainerRootFile() // SAX 解析 container.xml
|
||||
│ └─ parseOPF() // SAX 解析 OPF (metadata/manifest/spine)
|
||||
│ └─ parseTOC() // NCX 或 Navigation Document
|
||||
├─ viewDidLoad() → startInitialLoadIfNeeded()
|
||||
│ │
|
||||
│ ├─ RDEPUBReaderLoadCoordinator.startInitialLoadIfNeeded()
|
||||
│ │ ├─ RDEPUBParser.parse(epubURL:) // 解压 ZIP → 解析 OPF → 构建 spine/TOC
|
||||
│ │ │ └─ extractArchiveIfNeeded() // ZIPFoundation 解压到 Caches
|
||||
│ │ │ └─ parseContainerRootFile() // SAX 解析 container.xml
|
||||
│ │ │ └─ parseOPF() // SAX 解析 OPF (metadata/manifest/spine)
|
||||
│ │ │ └─ parseTOC() // NCX 或 Navigation Document
|
||||
│ │ │
|
||||
│ │ ├─ RDEPUBPublication(parser:) // 创建门面对象
|
||||
│ │ ├─ 恢复持久化数据(书签/高亮/位置)
|
||||
│ │ └─ applyParsedPublication()
|
||||
│ │
|
||||
│ └─ 判断 readingProfile:
|
||||
│ ├─ .textReflowable → 文本排版路径(本 SDK 核心路径)
|
||||
│ ├─ .webInteractive → WebView 渲染路径
|
||||
│ └─ .webFixedLayout → 固定布局路径
|
||||
│
|
||||
├─ RDEPUBPublication(parser:) // 创建门面对象
|
||||
│
|
||||
├─ 判断 readingProfile:
|
||||
│ ├─ .textReflowable → 文本排版路径(本 SDK 核心路径)
|
||||
│ ├─ .webInteractive → WebView 渲染路径
|
||||
│ └─ .webFixedLayout → 固定布局路径
|
||||
│
|
||||
└─ paginateTextPublication() // 进入分页流程
|
||||
└─ runtime.paginatePublication() // 进入分页流程
|
||||
```
|
||||
|
||||
### 3.2 文本重排分页流程(大书优化路径)
|
||||
@@ -206,10 +211,17 @@ RDEPUBReaderController
|
||||
│ ├─ configuration, persistence
|
||||
│ └─ 便捷方法 (renderStyle, layoutConfig, cacheKey)
|
||||
│
|
||||
├─ RDEPUBReaderRuntime // 运行时协调器集合
|
||||
├─ RDEPUBReaderRuntime // 运行时协调器集合(Facade 模式)
|
||||
│ ├─ chapterLoader // 章节加载器
|
||||
│ ├─ chapterRuntimeStore // 内存缓存
|
||||
│ ├─ summaryDiskCache // 磁盘摘要缓存
|
||||
│ ├─ pageResolver // 页码解析器
|
||||
│ ├─ loadCoordinator // 加载协调器
|
||||
│ ├─ paginationCoordinator // 分页协调器
|
||||
│ ├─ locationCoordinator // 位置协调器
|
||||
│ ├─ searchCoordinator // 搜索协调器
|
||||
│ ├─ chromeCoordinator // 工具栏协调器
|
||||
│ ├─ annotationCoordinator // 标注协调器
|
||||
│ └─ viewportMonitor // 视口变化监控
|
||||
│
|
||||
├─ RDEPUBReaderPaginationCoordinator // 分页协调器
|
||||
@@ -335,7 +347,7 @@ Sources/RDReaderView/
|
||||
│ │ ├── cssInjector.js
|
||||
│ │ ├── WeReadApi.js
|
||||
│ │ ├── rangy-core.js / rangy-serializer.js
|
||||
│ │ ├── wxread-default.css / wxread-dark.css / wxread-replace.css
|
||||
│ │ ├── wxread-default.css / wxread-dark.css / wxread-replace-latin.css
|
||||
│ │ └── epub-fixed-layout.html
|
||||
│ ├── RDEPUBParser.swift # 核心解析器
|
||||
│ ├── RDEPUBParser+Archive.swift # ZIP 解压
|
||||
@@ -398,27 +410,38 @@ Sources/RDReaderView/
|
||||
│ │ ├── RDEPUBChapterLoader.swift
|
||||
│ │ ├── RDEPUBChapterRuntimeStore.swift
|
||||
│ │ ├── RDEPUBChapterSummaryDiskCache.swift
|
||||
│ │ ├── RDEPUBChapterCacheKey.swift
|
||||
│ │ ├── RDEPUBBookPageMap.swift
|
||||
│ │ ├── RDEPUBChapterSummary.swift
|
||||
│ │ ├── RDEPUBPageResolver.swift
|
||||
│ │ └── ...
|
||||
│ ├── RDEPUBReaderContext.swift // 共享状态中心
|
||||
│ ├── RDEPUBReaderRuntime.swift // 运行时协调器
|
||||
│ ├── RDEPUBReaderRuntime.swift // 运行时协调器(Facade)
|
||||
│ ├── RDEPUBReaderDependencies.swift // 依赖注入
|
||||
│ ├── RDEPUBReaderLoadCoordinator.swift // 加载协调器
|
||||
│ ├── RDEPUBReaderPaginationCoordinator.swift // 分页协调器
|
||||
│ ├── RDEPUBReaderAnnotationCoordinator.swift // 标注协调器
|
||||
│ ├── RDEPUBReaderLocationCoordinator.swift // 位置协调器
|
||||
│ ├── RDEPUBReaderSearchCoordinator.swift // 搜索协调器
|
||||
│ ├── RDEPUBReaderNavigationCoordinator.swift // 导航协调器
|
||||
│ ├── RDEPUBReaderChromeCoordinator.swift // 工具栏协调器
|
||||
│ ├── RDEPUBReaderAnnotationCoordinator.swift // 标注协调器
|
||||
│ ├── RDEPUBReaderAssemblyCoordinator.swift // 组装协调器
|
||||
│ ├── RDEPUBReaderViewportMonitor.swift // 视口监控
|
||||
│ └── ...
|
||||
├── Settings/ # 设置面板
|
||||
│ ├── RDEPUBReaderSettingsViewController.swift
|
||||
│ ├── RDEPUBReaderThemeSelector.swift
|
||||
│ ├── RDEPUBReaderConfiguration.swift # 配置模型
|
||||
│ ├── RDEPUBReaderSettings.swift # 持久化设置
|
||||
│ ├── RDEPUBReaderSettingsViewController.swift # 设置面板 VC
|
||||
│ ├── RDEPUBReaderTheme.swift # 主题定义
|
||||
│ └── ...
|
||||
├── TextPage/ # 文本页面渲染
|
||||
│ ├── RDEPUBTextContentView.swift
|
||||
│ ├── RDEPUBTextPageViewController.swift
|
||||
│ ├── RDEPUBTextContentView.swift # 文本内容视图(CoreText)
|
||||
│ ├── RDEPUBTextSelectionController.swift # 文本选择控制器
|
||||
│ ├── RDEPUBPageInteractionController.swift # 点击交互控制器
|
||||
│ ├── RDEPUBSelectionOverlayView.swift # 选区覆盖层
|
||||
│ ├── RDEPUBTextAnnotationOverlay.swift # 标注覆盖层
|
||||
│ ├── RDEPUBTextPageRenderView.swift # CoreText 绘制视图
|
||||
│ └── ...
|
||||
├── RDEPUBReaderController.swift # 主控制器
|
||||
├── RDEPUBReaderConfiguration.swift # 配置模型
|
||||
├── RDEPUBReaderController.swift # 主控制器(+ContentDelegates/DataSource/PublicAPI 等扩展)
|
||||
├── RDEPUBReaderDelegate.swift # 公开委托协议
|
||||
├── RDEPUBReaderPersistence.swift # 持久化协议
|
||||
└── ...
|
||||
```
|
||||
|
||||
@@ -432,7 +455,7 @@ Sources/RDReaderView/
|
||||
| **Facade** | `RDEPUBPublication` 封装 `RDEPUBParser`,`RDEPUBChapterData` 封装章节查询 |
|
||||
| **Builder** | `RDEPUBBookPageMap.Builder` 增量构建页码映射 |
|
||||
| **Strategy** | `RDEPUBTextRenderer` 协议,可替换渲染器实现 |
|
||||
| **Pipeline** | `RDEPUBTextTypesetterPipeline` 8 阶段排版管线 |
|
||||
| **Pipeline** | `RDEPUBTextTypesetterPipeline` 排版管线(8 个逻辑阶段,封装为 5-6 个顶层调用) |
|
||||
| **State Machine** | `RDEPUBNavigatorState` 管理阅读器状态转换 |
|
||||
| **Adapter** | `RDReaderLegacyDataSourceAdapter` 适配旧数据源协议 |
|
||||
| **三级缓存** | 内存 → 磁盘摘要 → 全书分页,逐级降级 |
|
||||
|
||||
+84
-10
@@ -1,6 +1,6 @@
|
||||
# ReadViewSDK 业务逻辑文档
|
||||
|
||||
> 最后更新:2026-06-04
|
||||
> 最后更新:2026-06-09
|
||||
|
||||
---
|
||||
|
||||
@@ -345,34 +345,46 @@ RTL 模式下左右互换。工具栏显示时,左右区域变为 `.center`(
|
||||
// 统一标注类型
|
||||
struct RDEPUBAnnotation {
|
||||
let id: String
|
||||
let bookIdentifier: String?
|
||||
let kind: RDEPUBAnnotationKind // .bookmark, .highlight, .underline
|
||||
let location: RDEPUBLocation
|
||||
let text: String?
|
||||
let color: String?
|
||||
let note: String?
|
||||
let rangeInfo: String? // CoreText 选区范围信息
|
||||
let chapterTitle: String?
|
||||
let createdAt: Date
|
||||
// computed: bookmark, highlight
|
||||
}
|
||||
|
||||
// 书签
|
||||
struct RDEPUBBookmark {
|
||||
let id: String
|
||||
let bookIdentifier: String?
|
||||
let location: RDEPUBLocation
|
||||
let chapterTitle: String?
|
||||
let note: String?
|
||||
let createdAt: Date
|
||||
}
|
||||
|
||||
// 高亮
|
||||
struct RDEPUBHighlight {
|
||||
let id: String
|
||||
let bookIdentifier: String?
|
||||
let location: RDEPUBLocation
|
||||
let text: String
|
||||
let style: RDEPUBHighlightStyle // .highlight, .underline
|
||||
let color: String
|
||||
let note: String?
|
||||
let rangeInfo: String?
|
||||
let createdAt: Date
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 选区处理流程
|
||||
|
||||
SDK 支持两条选区处理路径,分别对应 WebView 渲染和原生文本渲染:
|
||||
|
||||
**路径 A:WebView 渲染(webInteractive / webFixedLayout)**
|
||||
|
||||
```
|
||||
用户长按 → WKWebView 选区变化
|
||||
│
|
||||
@@ -387,6 +399,32 @@ RDEPUBReaderAnnotationCoordinator
|
||||
└─ 显示操作菜单(拷贝/高亮/批注)
|
||||
```
|
||||
|
||||
**路径 B:原生文本渲染(textReflowable)**
|
||||
|
||||
```
|
||||
用户长按 → RDEPUBTextContentView.handleLongPress(_:)
|
||||
│
|
||||
├─ layoutIfNeeded() // 确保布局完成
|
||||
│
|
||||
▼
|
||||
RDEPUBTextSelectionController.handleLongPress(_:)
|
||||
├─ RDEPUBPageInteractionController.characterIndexForViewPoint()
|
||||
│ └─ CoreText CTLineGetStringIndexForPosition 字符命中
|
||||
├─ 计算选区范围 (NSRange)
|
||||
├─ isSelecting = true
|
||||
│
|
||||
▼
|
||||
用户拖拽 → handlePan(_:)
|
||||
├─ 更新选区范围
|
||||
├─ RDEPUBSelectionOverlayView 绘制选区高亮
|
||||
└─ .ended/.cancelled/.failed → isSelecting = false(保留选区)
|
||||
│
|
||||
▼
|
||||
RDEPUBTextContentView delegate → RDEPUBReaderAnnotationCoordinator
|
||||
├─ 创建 RDEPUBSelection(含 rangeInfo)
|
||||
└─ 显示操作菜单(拷贝/高亮/批注)
|
||||
```
|
||||
|
||||
### 6.3 高亮渲染
|
||||
|
||||
文本模式下,高亮通过 NSAttributedString 属性注入:
|
||||
@@ -415,17 +453,34 @@ CoreText 渲染时识别这些自定义属性并绘制高亮背景/下划线。
|
||||
|
||||
### 7.1 全文搜索
|
||||
|
||||
SDK 提供两个搜索引擎,分别服务于不同的渲染路径:
|
||||
|
||||
**文本渲染路径:`RDEPUBTextSearchEngine`**
|
||||
|
||||
`RDEPUBTextSearchEngine.search(keyword:)`:
|
||||
|
||||
1. 遍历所有 linear spine 条目(html/xhtml 类型)
|
||||
2. 读取 HTML,转为纯文本:
|
||||
- 优先:`NSAttributedString(data:options:documentAttributes:)` with `.documentType: .html`
|
||||
- 回退:正则去除 HTML 标签
|
||||
3. 执行大小写不敏感的 `NSString.range(of:options:)` 搜索
|
||||
1. 遍历 `RDEPUBTextBook.chapters` 中的所有章节
|
||||
2. 对每个章节的 `attributedContent`(已渲染的 NSAttributedString)执行搜索
|
||||
3. 使用 `NSString.range(of:options:.caseInsensitive)` 进行大小写不敏感搜索
|
||||
4. 为每个匹配生成:
|
||||
- `progression`:0.0-1.0 的阅读进度
|
||||
- `previewText`:匹配位置前后各 12 字符
|
||||
- `rangeAnchor`:精确的文本锚点
|
||||
- `rangeAnchor`:精确的文本锚点(RDEPUBTextRangeAnchor)
|
||||
|
||||
**纯文本文件搜索:`RDEPUBTextSearchEngine.searchWithoutPublication(textBook:keyword:)`**
|
||||
|
||||
静态方法,用于 `.txt` 文件(有 TextBook 但无 Publication)的搜索:
|
||||
|
||||
1. 遍历 TextBook 章节的 attributedContent
|
||||
2. 跳过 publication 依赖的 href 规范化
|
||||
3. 不生成 rangeAnchor(纯文本无 CoreText 锚点)
|
||||
|
||||
**WebView 渲染路径:`RDEPUBHTMLSearchEngine`**
|
||||
|
||||
1. 遍历所有 linear spine 条目(html/xhtml 类型)
|
||||
2. 读取 HTML,转为纯文本(正则去除标签)
|
||||
3. 执行大小写不敏感搜索
|
||||
4. 通过 JS Bridge 在 WebView DOM 中绘制搜索高亮
|
||||
|
||||
### 7.2 搜索结果导航
|
||||
|
||||
@@ -439,7 +494,26 @@ struct RDEPUBSearchState {
|
||||
}
|
||||
```
|
||||
|
||||
WebView 通过 JS Bridge 接收搜索高亮数据,在 DOM 中绘制高亮矩形。
|
||||
**搜索结果导航流程:**
|
||||
|
||||
`RDEPUBReaderSearchCoordinator` 协调搜索结果的前进/后退导航:
|
||||
|
||||
1. `goToNextMatch()` / `goToPreviousMatch()` 更新 `currentMatchIndex`
|
||||
2. 根据匹配位置计算目标页码,触发页面跳转
|
||||
3. 通知 `RDEPUBReaderController` 更新搜索计数显示
|
||||
|
||||
**搜索结果渲染:**
|
||||
|
||||
- **WebView 路径:** 通过 JS Bridge 接收搜索高亮数据,在 DOM 中绘制高亮矩形
|
||||
- **原生文本路径:** `RDEPUBTextContentView` 根据 `rangeAnchor` 在 CoreText 绘制层高亮匹配文本
|
||||
|
||||
**搜索栏生命周期:**
|
||||
|
||||
搜索栏采用延迟安装模式:
|
||||
|
||||
1. `showSearchBar()` 设置 `isSearchBarVisible = true`,仅在工具栏可见时立即安装视图
|
||||
2. 若工具栏隐藏,延迟到 `handleToolViewVisibilityChanged(isVisible: true)` 时安装
|
||||
3. `installSearchBarView()` 负责实际的视图层级添加、约束、动画和焦点管理
|
||||
|
||||
---
|
||||
|
||||
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
# 代码库风险与关注点
|
||||
|
||||
**分析日期:** 2026-05-21
|
||||
|
||||
## 高风险:安全与隐私
|
||||
|
||||
### 1) CocoaPods 构建关闭了 User Script Sandboxing
|
||||
- **问题:** `post_install` hook 将 Pods *以及* 用户工程的 `ENABLE_USER_SCRIPT_SANDBOXING` 强制设置为 `NO`。
|
||||
- **影响:** 削弱对内嵌 Web 内容的纵深防御;增加 `WKWebView` 相关功能的风险面,并可能不符合组织/审核(含 App Store)安全基线。
|
||||
- **建议缓解:**
|
||||
- 移除全局覆盖,保持系统默认的 sandboxing。
|
||||
- 如确有依赖需要 workaround,仅对具体 Pod target 做最小范围的设置,并记录原因。
|
||||
- 在 CI 中增加校验:出现 `ENABLE_USER_SCRIPT_SANDBOXING=NO` 时告警/失败。
|
||||
|
||||
### 2) 外链打开未做 allowlist(scheme/host)
|
||||
- **问题:** EPUB 内容中的外部 URL 通过 `UIApplication.shared.open(...)` 打开时,未对 scheme/host 做校验,也未强制用户二次确认。
|
||||
- **影响:** 恶意 EPUB 可能触发钓鱼、隐私泄露,或打开意外的 URL scheme(包含跳转到其他 App 的 deep link)。
|
||||
- **建议缓解:**
|
||||
- 强制 allowlist(默认仅允许 `https`;如需 `mailto`/`tel` 等应在明确 UI 提示后允许)。
|
||||
- 弹出确认对话框,展示目标 host。
|
||||
- 对 bridge 消息与导航行为中的非 HTTP(S) scheme 做拒绝/过滤。
|
||||
|
||||
### 3) EPUB 解压未显式做 Zip Slip(路径穿越)加固
|
||||
- **问题:** 解压逻辑构造 `destinationURL = extractionURL.appendingPathComponent(entry.path)` 并解压 entry,但未显式验证标准化后的目标路径是否仍在 `extractionURL` 之内。
|
||||
- **影响:** 构造的 EPUB 可能尝试通过 `../...` 路径穿越覆盖目标目录外的文件(实际危害与 ZIPFoundation 行为及权限有关,但建议显式防护)。
|
||||
- **建议缓解:**
|
||||
- 解压前计算 `standardizedDestination = destinationURL.standardizedFileURL`,并确保其前缀位于 `extractionURL.standardizedFileURL.path + "/"` 之下。
|
||||
- 拒绝包含 `..`、绝对路径、或异常路径分隔符形式的 entry。
|
||||
- 考虑先解压到新的随机 UUID 目录,仅将校验通过的内容移动到最终目录。
|
||||
|
||||
### 4) 选中文本/高亮内容明文写入 UserDefaults
|
||||
- **问题:** 选中文本与高亮 payload 以 JSON 编码写入 `UserDefaults`(包含“新”持久化与 legacy 路径)。
|
||||
- **影响:** 可能将书籍敏感内容(高亮、选择文本)存入默认不加密的持久化位置,可能进入备份;同时数据量可能无限增长(性能与隐私风险)。
|
||||
- **建议缓解:**
|
||||
- 仅持久化稳定标识/范围(例如 CFI / rangeInfo),运行时再派生文本。
|
||||
- 如必须持久化文本,存入加密存储(例如 Keychain 或使用 `NSFileProtectionComplete` 的加密文件)并设置大小上限。
|
||||
- 增加数据保留策略,并提供 “Clear reading data” API。
|
||||
|
||||
## 性能与稳定性风险
|
||||
|
||||
### 5) Scheme handler 将整文件一次性读入内存(无流式)
|
||||
- **问题:** `WKURLSchemeHandler` 使用 `Data(contentsOf:)` 读取资源并一次性返回。
|
||||
- **影响:** EPUB 内的大图/字体/媒体资源可能导致内存峰值、加载变慢,甚至在内存压力下被系统终止。
|
||||
- **建议缓解:**
|
||||
- 尽可能改为基于 `InputStream` 的流式传输,并使用增量 `didReceive(...)`。
|
||||
- 对单个资源增加大小上限,超过则优雅失败并提示。
|
||||
|
||||
### 6) 隐藏的分页 WKWebView 可能加载外部资源
|
||||
- **问题:** `RDEPUBPaginator` 使用 `loadFileURL(...allowingReadAccessTo:)`,但未看到对 `navigationAction` 的 allowlist(例如仅允许 `file://` 或自定义 `ss-reader://`)。
|
||||
- **影响:** 分页测量阶段可能产生意外网络请求,引发隐私泄露与不可控性能波动。
|
||||
- **建议缓解:**
|
||||
- 在 paginator 场景加入导航策略:默认仅允许 `file://`(以及必要的自定义 scheme),对 `http(s)` 直接取消。
|
||||
- 视需求考虑内容拦截规则或禁用分页阶段的网络加载。
|
||||
|
||||
### 7) EPUB 解压缓存目录可能无限累积
|
||||
- **问题:** 解压路径基于文件名/大小/mtime 的确定性签名;若目录已存在则直接复用,且无清理策略。
|
||||
- **影响:** 书籍数量多或频繁更新时磁盘占用持续增长,造成存储压力与潜在卡顿。
|
||||
- **建议缓解:**
|
||||
- 为 `ssreaderview-epub` 缓存目录实现淘汰策略(LRU/按时间)。
|
||||
- 提供显式清理 API,并在低存储信号时触发清理。
|
||||
|
||||
## 可维护性与技术债
|
||||
|
||||
### 8) DEBUG 下日志与 Web Inspector 可能泄露内容
|
||||
- **问题:** Debug 辅助会打印导航/消息体;selection payload 可能包含用户选中文本;且 DEBUG 下可能默认开启 inspectable。
|
||||
- **影响:** 在 debug 构建(含内部测试/QA)中,日志可能包含敏感内容并被导出/分享。
|
||||
- **建议缓解:**
|
||||
- 默认对消息体做脱敏(仅记录类型/大小),需要时再显式 opt-in 输出内容。
|
||||
- 将 `isInspectable` 受控于 app 级 debug 开关,而不是在 DEBUG 下默认启用。
|
||||
|
||||
## 构建与依赖脆弱性
|
||||
|
||||
### 9) 仓库中包含 vendored 的 `Pods/` 目录
|
||||
- **问题:** 仓库根目录有 `Pods/`,示例工程内也有 `ReadViewDemo/Pods/`。
|
||||
- **影响:** diff 体积大、clone 慢、易产生 merge 冲突,也容易出现依赖陈旧导致的构建不一致。
|
||||
- **建议缓解:**
|
||||
- 通常建议不要提交 Pods,仅提交 `Podfile.lock`,在 CI 中执行 `pod install`。
|
||||
- 如确有 vendoring 政策,需文档化并提供同步/校验工具,避免漂移。
|
||||
|
||||
### 10) Podfile 与 podspec 的部署版本不一致
|
||||
- **问题:** `Podfile` 设为 `platform :ios, '15.6'`,而 podspec 声明 `s.platform = :ios, "15.0"`。
|
||||
- **影响:** 对外承诺与本地构建基线不一致,容易造成集成方预期偏差。
|
||||
- **建议缓解:**
|
||||
- 对齐 `Podfile`、`*.podspec`、Xcode project 的 deployment target。
|
||||
- 在 CI 中增加漂移检查,防止版本被无意改动。
|
||||
|
||||
### 11) podspec 将 Swift 版本钉死为 5.10
|
||||
- **问题:** `s.swift_versions = ["5.10"]` 将 pod 与特定 Swift 版本强绑定。
|
||||
- **影响:** 旧工具链的集成方无法使用;未来升级可能出现“突然不兼容”,尤其当依赖未同步时。
|
||||
- **建议缓解:**
|
||||
- 如兼容性允许,可声明更多支持版本(或范围),并在 CI 中覆盖多个 Xcode/Swift 版本构建验证。
|
||||
- 在文档中明确最低支持的 Xcode/Swift 版本。
|
||||
|
||||
## 不清晰/容易踩坑的契约
|
||||
|
||||
### 12) 默认的持久化协议实现可能静默丢失数据
|
||||
- **问题:** `RDEPUBReaderPersistence` 通过 protocol extension 提供了 bookmarks/settings 的默认 no-op 实现。
|
||||
- **影响:** 集成方可能误以为这些能力默认会持久化,但实际数据会被静默丢弃,且不易排查。
|
||||
- **建议缓解:**
|
||||
- 将关键方法改为必须实现(移除 no-op 默认实现);或在未实现且功能启用时输出日志/assert。
|
||||
- 提供并文档化 “最小持久化” 与 “完整持久化” 的参考实现。
|
||||
|
||||
---
|
||||
|
||||
## Evidence(仓库路径)
|
||||
|
||||
- Build flags:`Podfile`
|
||||
- Pod metadata/toolchain:`RDReaderView.podspec`
|
||||
- Archive extraction:`Sources/RDReaderView/EPUBCore/RDEPUBParser+Archive.swift`
|
||||
- Resource path validation(post-extraction):`Sources/RDReaderView/EPUBCore/RDEPUBParser+Resources.swift`
|
||||
- Scheme handler reads full file bytes:`Sources/RDReaderView/EPUBCore/RDEPUBResourceURLSchemeHandler.swift`
|
||||
- WebView bridge + message handling:`Sources/RDReaderView/EPUBCore/RDEPUBWebView+JavaScriptBridge.swift`
|
||||
- External link opening:`Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift`
|
||||
- UserDefaults persistence implementation:`Sources/RDReaderView/EPUBUI/RDEPUBReaderPersistence.swift`
|
||||
- Hidden paginator web view:`Sources/RDReaderView/EPUBCore/RDEPUBPaginator.swift`
|
||||
- Debug logging:`Sources/RDReaderView/EPUBCore/RDEPUBWebViewDebug.swift`
|
||||
- Vendored dependencies:`Pods/`、`ReadViewDemo/Pods/`
|
||||
|
||||
---
|
||||
|
||||
*风险与关注点审计:2026-05-21*
|
||||
@@ -0,0 +1,85 @@
|
||||
# 编码规范
|
||||
|
||||
**分析日期:** 2026-05-21
|
||||
|
||||
> 说明:本仓库以 iOS/Swift 为主,未检测到统一的 lint/format 工具配置;代码风格在不同模块间存在差异。文档按项目规则使用中文描述,但代码标识符保持英文。
|
||||
|
||||
## 语言与工程约束
|
||||
|
||||
- **主要语言**:Swift(Podspec 声明 `s.swift_versions = ["5.10"]`,见 `RDReaderView.podspec`)
|
||||
- **最低系统版本**:Podspec `iOS 15.0`(`RDReaderView.podspec`),示例工程 Podfile/构建设置里常见为 `iOS 15.6`(`Podfile`)
|
||||
- **依赖管理**:CocoaPods(`Podfile`、`ReadViewDemo/Podfile`、`Podfile.lock`)
|
||||
|
||||
## 命名约定
|
||||
|
||||
**文件/类型命名(Swift):**
|
||||
- 以类型名为文件名的单文件组织较常见:`Sources/RDReaderView/EPUBCore/RDEPUBParser.swift`
|
||||
- 大量使用前缀区分模块域:
|
||||
- `RD...`:阅读器 UI/控制器相关(如 `Sources/RDReaderView/RDReaderView.swift`、`Sources/RDReaderView/RDURLReaderController.swift`)
|
||||
- `RDEPUB...`:EPUB Core/UI/渲染相关(如 `Sources/RDReaderView/EPUBCore/RDEPUBModels.swift`)
|
||||
- Extension 文件使用 `+` 命名:`Sources/RDReaderView/EPUBUI/RDEPUBReaderController+ContentDelegates.swift`
|
||||
|
||||
**变量/函数命名:**
|
||||
- 基本遵循 Swift lowerCamelCase:`parse(epubURL:)`(`Sources/RDReaderView/EPUBCore/RDEPUBParser.swift`)
|
||||
- 常量多用 `static let`:`kRDEPUBHighlightAttributeName`(`Sources/RDReaderView/EPUBCore/Models/RDEPUBAnnotationModels.swift`)
|
||||
|
||||
## 代码风格与排版(从现有代码归纳)
|
||||
|
||||
**缩进与换行:**
|
||||
- 多数文件使用 4 空格缩进(示例:`ReadViewDemo/ReadViewDemo/ViewController.swift`、`Sources/RDReaderView/EPUBCore/RDEPUBModels.swift`)
|
||||
- 历史代码中更常见”强制换行/多行括号”风格
|
||||
|
||||
**空行与分组:**
|
||||
- UI 相关文件常用空行分隔属性/初始化/布局段落
|
||||
- `// MARK:` 用于分区组织(示例:`Sources/RDReaderView/RDReaderGestureController.swift`、`Sources/RDReaderView/EPUBTextRendering/RDPlainTextBookBuilder.swift`)
|
||||
|
||||
**类型组织:**
|
||||
- 偏好用 `extension` 拆分职责/协议实现(示例:`ReadViewDemo/ReadViewDemo/ViewController.swift` 的 `UITableViewDataSource/Delegate`)
|
||||
- API 暴露处使用 `public`、`public final class`、`public enum/struct`(示例:`Sources/RDReaderView/EPUBCore/RDEPUBModels.swift`)
|
||||
- “对外只读、内部可写”常用 `public internal(set)`(示例:`Sources/RDReaderView/EPUBCore/RDEPUBParser.swift`)
|
||||
|
||||
## 导入与依赖使用
|
||||
|
||||
**import:**
|
||||
- UIKit/UI 文件:`import UIKit`(大量文件)
|
||||
- Core/模型文件:`import Foundation`(如 `Sources/RDReaderView/EPUBCore/RDEPUBModels.swift`)
|
||||
- 三方依赖按需引入:
|
||||
- `SnapKit`:布局
|
||||
- `SSAlertSwift`:弹窗/提示
|
||||
|
||||
**Pods 目录说明:**
|
||||
- `ReadViewDemo/Pods/**` 为依赖源码/生成配置,通常不作为本仓库代码风格的“标准样式”参考。
|
||||
|
||||
## 错误处理与日志
|
||||
|
||||
- Core 解析层倾向用 `throws` + 自定义 `Error`(示例:`Sources/RDReaderView/EPUBCore/RDEPUBParser.swift`、`Sources/RDReaderView/EPUBCore/RDEPUBModels.swift` 的 `RDEPUBParserError`)
|
||||
- UI/控制器层常见 `guard` 早返回(示例:`ReadViewDemo/ReadViewDemo/ViewController.swift`)
|
||||
- 未检测到统一日志框架(未发现专用 logging package/config);出现时以系统 API/局部输出为主(需按具体文件核对)。
|
||||
|
||||
## 注释与文档
|
||||
|
||||
- **项目规则(强约束)**:代码标识符保持英文,但**代码注释/文档/提交信息使用中文**(见 `CONTEXT.md`)。
|
||||
- 历史文件常带 Xcode 头部注释块(示例:`Sources/RDReaderView/ReaderView/RDReaderView.swift`)。
|
||||
- 公共 API 处存在少量三斜线文档注释(示例:`Sources/RDReaderView/RDReaderView.swift` 的中文说明)。
|
||||
|
||||
## Lint / Formatter / 静态检查
|
||||
|
||||
**未检测到(仓库根与常见位置):**
|
||||
- SwiftLint 配置:`.swiftlint.yml` / `swiftlint.yml`
|
||||
- SwiftFormat 配置:`.swiftformat`
|
||||
- 通用格式化配置:`.editorconfig`
|
||||
- 其他:ESLint/Prettier/Biome 等(本仓库非 JS/TS 主体)
|
||||
|
||||
**可执行的工程级格式化/检查:**
|
||||
- 主要依赖 Xcode(或 Swift 编译器)自身检查;如需引入 SwiftLint/SwiftFormat,应先新增对应配置文件并在 CI/构建脚本中接入。
|
||||
|
||||
## Evidence(关键证据文件)
|
||||
|
||||
- `CONTEXT.md`
|
||||
- `Podfile`
|
||||
- `RDReaderView.podspec`
|
||||
- `ReadViewDemo/ReadViewDemo/ViewController.swift`
|
||||
- `Sources/RDReaderView/ReaderView/RDReaderView.swift`
|
||||
- `Sources/RDReaderView/EPUBCore/RDEPUBParser.swift`
|
||||
- `Sources/RDReaderView/EPUBCore/RDEPUBModels.swift`
|
||||
- `Sources/RDReaderView/EPUBUI/RDEPUBReaderController.swift`
|
||||
@@ -34,7 +34,7 @@ ReadViewSDK 当前对 EPUB 有三类渲染路径:
|
||||
|
||||
## 2. 关键事实核验(当前代码真实路径)
|
||||
|
||||
> 纠偏说明:`.planning/PROJECT.md` 在初始化时曾把“reflowable EPUB 当前路径”概括为偏 `WKWebView` 的历史性表述。经本次代码核验,当前真实主路径是 `.textReflowable` → `RDEPUBTextBookBuilder` → `RDEPUBDTCoreTextRenderer` → CoreText 分页 → `RDEPUBTextContentView`。本设计以代码事实为准,并默认后续计划/实现都按此理解推进。
|
||||
> 纠偏说明:项目初始化时曾把”reflowable EPUB 当前路径”概括为偏 `WKWebView` 的历史性表述。经本次代码核验,当前真实主路径是 `.textReflowable` → `RDEPUBTextBookBuilder` → `RDEPUBDTCoreTextRenderer` → CoreText 分页 → `RDEPUBTextContentView`。本设计以代码事实为准,并默认后续实现都按此理解推进。
|
||||
|
||||
### 2.1 渲染路径分流(已存在)
|
||||
|
||||
@@ -255,7 +255,7 @@ WXRead 的更高阶策略(参考 `Doc/WXRead/analysis/DTCoreText自定义修
|
||||
|
||||
## 9. 下一步(交接到开发)
|
||||
|
||||
建议按 `.planning/ROADMAP.md` 从 Phase 1 开始推进:
|
||||
建议按 `Doc/ARCHITECTURE-CONTEXT.md` 中的架构决策逐步推进:
|
||||
|
||||
- 先基于 `Doc/WXRead/analysis/*` 提炼“我们要实现的 CSS 分层最小集合”
|
||||
- 再在 `.textReflowable` renderer 里实现“分层 CSS 注入 + baseURL/资源解析兜底”
|
||||
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
# 测试说明
|
||||
|
||||
**分析日期:** 2026-06-09(更新)
|
||||
|
||||
## 测试框架与现状
|
||||
|
||||
**Runner:**
|
||||
- XCTest(iOS/Xcode 默认测试框架)
|
||||
- **当前仓库状态**:包含 UI 测试 Target `ReadViewDemoUITests`,位于 `ReadViewDemo/ReadViewDemoUITests/`
|
||||
|
||||
**断言库:**
|
||||
- XCTest 内建断言(`XCTAssert*`、`XCTAssertEqual`、`XCTAssertNotNil` 等)
|
||||
- XCUIElement 断言(`exists`、`waitForExistence`、`isHittable` 等)
|
||||
|
||||
## 测试文件组织
|
||||
|
||||
**位置:**
|
||||
- 测试文件:`ReadViewDemo/ReadViewDemoUITests/ReaderUITests/`
|
||||
- 辅助文件:`ReadViewDemo/ReadViewDemoUITests/Helpers/`
|
||||
|
||||
**文件清单(24 个测试文件):**
|
||||
|
||||
| 测试文件 | 覆盖功能 |
|
||||
|----------|----------|
|
||||
| BookmarkTests.swift | 书签基础操作 |
|
||||
| BookmarkManagementTests.swift | 书签管理(增删改查) |
|
||||
| ConcurrentParsingTests.swift | 并发解析稳定性 |
|
||||
| ConfigurableWindowTests.swift | 可配置章节窗口 |
|
||||
| DisplayTypeTests.swift | 显示模式切换 |
|
||||
| ErrorAndEdgeCaseTests.swift | 错误与边界情况 |
|
||||
| FanrenParseTimeTest.swift | 《凡人修仙传》解析性能基准 |
|
||||
| HighlightsManagementTests.swift | 高亮标注管理 |
|
||||
| LargeBookOnDemandTests.swift | 大书按需加载 |
|
||||
| LocationPersistenceTests.swift | 阅读位置持久化 |
|
||||
| MetadataParseBenchmarkTests.swift | 元数据解析性能基准 |
|
||||
| PageNavigationTests.swift | 页面导航 |
|
||||
| ReaderAnnotationTests.swift | 标注基础功能 |
|
||||
| ReaderAnnotationExtendedTests.swift | 标注扩展功能 |
|
||||
| ReaderOpenCloseTests.swift | 阅读器打开/关闭 |
|
||||
| ReaderToolbarTests.swift | 工具栏交互 |
|
||||
| SearchTests.swift | 全文搜索 |
|
||||
| SelectionMenuTests.swift | 选区菜单 |
|
||||
| SettingsEffectTests.swift | 设置效果验证 |
|
||||
| SettingsExtendedTests.swift | 设置扩展功能 |
|
||||
| SettingsPanelTests.swift | 设置面板交互 |
|
||||
| TableOfContentsTests.swift | 目录功能 |
|
||||
| TOCInteractionTests.swift | 目录交互 |
|
||||
| ToolbarStateTests.swift | 工具栏状态管理 |
|
||||
|
||||
**辅助文件(3 个):**
|
||||
|
||||
| 文件 | 用途 |
|
||||
|------|------|
|
||||
| AccessibilityIdentifiers.swift | UI 元素 accessibility identifier 定义 |
|
||||
| DemoReaderState.swift | 测试用阅读器状态辅助 |
|
||||
| XCUIApplication+Launch.swift | XCUIApplication 启动配置扩展 |
|
||||
|
||||
**统计:**
|
||||
- 测试类:23 个(XCTestCase 子类)
|
||||
- 测试方法:约 99 个(`func test...`)
|
||||
- 辅助文件:3 个
|
||||
|
||||
## 如何运行
|
||||
|
||||
### CocoaPods 依赖准备
|
||||
|
||||
> 仓库包含示例工程 `ReadViewDemo`,并已提交 `Pods/` 与 `Podfile.lock`。如本地环境未同步,可在仓库根或示例工程目录运行:
|
||||
|
||||
```bash
|
||||
pod install
|
||||
```
|
||||
|
||||
(依赖入口:`Podfile`、`ReadViewDemo/Podfile`)
|
||||
|
||||
### 运行 UI 测试
|
||||
|
||||
```bash
|
||||
xcodebuild test \
|
||||
-workspace ReadViewDemo/ReadViewDemo.xcworkspace \
|
||||
-scheme ReadViewDemo \
|
||||
-destination 'platform=iOS Simulator,name=iPhone 15' \
|
||||
-only-testing:ReadViewDemoUITests
|
||||
```
|
||||
|
||||
### 运行单个测试类
|
||||
|
||||
```bash
|
||||
xcodebuild test \
|
||||
-workspace ReadViewDemo/ReadViewDemo.xcworkspace \
|
||||
-scheme ReadViewDemo \
|
||||
-destination 'platform=iOS Simulator,name=iPhone 15' \
|
||||
-only-testing:ReadViewDemoUITests/SearchTests
|
||||
```
|
||||
|
||||
### 运行脚本
|
||||
|
||||
项目提供了自动化回归脚本:
|
||||
|
||||
```bash
|
||||
./scripts/run_ui_regression.sh
|
||||
```
|
||||
|
||||
测试报告输出到 `.artifacts/ui-tests/{timestamp}/UI-Test-Report.md`。
|
||||
|
||||
## 覆盖率(Coverage)
|
||||
|
||||
- 当前未启用代码覆盖率收集
|
||||
- 若需启用,可在 Xcode Scheme → Test → Options 中勾选 "Code Coverage"
|
||||
- 或通过 `xcodebuild test` 加 `-enableCodeCoverage YES` 参数
|
||||
|
||||
## Mocking / Fixtures
|
||||
|
||||
- 未使用统一 mocking 框架
|
||||
- 测试通过 Demo App 加载真实 EPUB 文件进行端到端验证
|
||||
- 辅助文件 `DemoReaderState.swift` 提供测试状态管理
|
||||
|
||||
## Evidence(关键证据文件)
|
||||
|
||||
- `ReadViewDemo/ReadViewDemo.xcodeproj/project.pbxproj`
|
||||
- `ReadViewDemo/ReadViewDemoUITests/` 目录
|
||||
- `ReadViewDemo/Podfile`
|
||||
- `ReadViewDemo/Podfile.lock`
|
||||
- `.artifacts/ui-tests/` 下的历史测试报告
|
||||
@@ -1,6 +1,6 @@
|
||||
# ReadViewSDK UML 类图
|
||||
|
||||
> 最后更新:2026-06-04
|
||||
> 最后更新:2026-06-09
|
||||
> 图表格式:Mermaid(可在 GitHub / VS Code / Mermaid Live Editor 中渲染)
|
||||
|
||||
---
|
||||
@@ -426,7 +426,14 @@ classDiagram
|
||||
+chapterLoader: RDEPUBChapterLoader
|
||||
+chapterRuntimeStore: RDEPUBChapterRuntimeStore
|
||||
+summaryDiskCache: RDEPUBChapterSummaryDiskCache
|
||||
+viewportMonitor: RDEPUBViewportMonitor
|
||||
+pageResolver: RDEPUBPageResolver
|
||||
+loadCoordinator: RDEPUBReaderLoadCoordinator
|
||||
+paginationCoordinator: RDEPUBReaderPaginationCoordinator
|
||||
+locationCoordinator: RDEPUBReaderLocationCoordinator
|
||||
+searchCoordinator: RDEPUBReaderSearchCoordinator
|
||||
+chromeCoordinator: RDEPUBReaderChromeCoordinator
|
||||
+annotationCoordinator: RDEPUBReaderAnnotationCoordinator
|
||||
+viewportMonitor: RDEPUBReaderViewportMonitor
|
||||
+applyBookPageMap(_:restoreLocation:)
|
||||
+refreshBookPageMapInPlace(_:)
|
||||
}
|
||||
@@ -440,6 +447,29 @@ classDiagram
|
||||
-buildPageMap(from:summaries:) RDEPUBBookPageMap
|
||||
}
|
||||
|
||||
class RDEPUBReaderLoadCoordinator {
|
||||
-context: RDEPUBReaderContext
|
||||
+startInitialLoadIfNeeded()
|
||||
-loadPublication()
|
||||
-applyParsedPublication()
|
||||
}
|
||||
|
||||
class RDEPUBReaderSearchCoordinator {
|
||||
-context: RDEPUBReaderContext
|
||||
+search(keyword:)
|
||||
+goToNextMatch()
|
||||
+goToPreviousMatch()
|
||||
+clearSearch()
|
||||
}
|
||||
|
||||
class RDEPUBReaderAnnotationCoordinator {
|
||||
-context: RDEPUBReaderContext
|
||||
+addHighlight(from:)
|
||||
+removeHighlight(id:)
|
||||
+toggleBookmark(at:)
|
||||
+handleSelectionMenuAction(_:)
|
||||
}
|
||||
|
||||
class RDEPUBChapterLoader {
|
||||
-context: RDEPUBReaderContext
|
||||
+loadChapter(spineIndex:store:) async throws RDEPUBRuntimeChapter
|
||||
@@ -496,6 +526,9 @@ classDiagram
|
||||
RDEPUBReaderRuntime --> RDEPUBChapterLoader : owns
|
||||
RDEPUBReaderRuntime --> RDEPUBChapterRuntimeStore : owns
|
||||
RDEPUBReaderRuntime --> RDEPUBChapterSummaryDiskCache : owns
|
||||
RDEPUBReaderRuntime --> RDEPUBReaderLoadCoordinator : owns
|
||||
RDEPUBReaderRuntime --> RDEPUBReaderSearchCoordinator : owns
|
||||
RDEPUBReaderRuntime --> RDEPUBReaderAnnotationCoordinator : owns
|
||||
RDEPUBReaderPaginationCoordinator --> RDEPUBReaderContext : uses
|
||||
RDEPUBChapterLoader --> RDEPUBReaderContext : uses
|
||||
RDEPUBChapterLoader --> RDEPUBChapterRuntimeStore : uses
|
||||
@@ -566,9 +599,13 @@ classDiagram
|
||||
|
||||
class RDEPUBAnnotation {
|
||||
+id: String
|
||||
+bookIdentifier: String?
|
||||
+kind: RDEPUBAnnotationKind
|
||||
+location: RDEPUBLocation
|
||||
+text: String?
|
||||
+rangeInfo: String?
|
||||
+chapterTitle: String?
|
||||
+createdAt: Date
|
||||
+bookmark: RDEPUBBookmark?
|
||||
+highlight: RDEPUBHighlight?
|
||||
}
|
||||
|
||||
+12
-3
@@ -1,6 +1,6 @@
|
||||
# ReadViewSDK 文档索引
|
||||
|
||||
> 最后更新:2026-06-04
|
||||
> 最后更新:2026-06-09
|
||||
|
||||
---
|
||||
|
||||
@@ -12,6 +12,15 @@
|
||||
| [UML_CLASS_DIAGRAMS.md](UML_CLASS_DIAGRAMS.md) | UML 类图(Mermaid 格式):模块关系、核心类图、并发模型、缓存键设计 |
|
||||
| [BUSINESS_LOGIC.md](BUSINESS_LOGIC.md) | 业务逻辑详解:EPUB 解析、文本渲染管线、后台解析优化、翻页容器、标注系统、搜索、设置 |
|
||||
|
||||
## 工程规范与风险
|
||||
|
||||
| 文档 | 说明 |
|
||||
|------|------|
|
||||
| [CONCERNS.md](CONCERNS.md) | 代码库风险与关注点:12 项安全/性能/可维护性风险 |
|
||||
| [TESTING.md](TESTING.md) | 测试基础设施:UI 测试文件清单、运行方式、覆盖率 |
|
||||
| [CONVENTIONS.md](CONVENTIONS.md) | 编码规范:命名约定、代码风格、导入规范、错误处理 |
|
||||
| [ARCHITECTURE-CONTEXT.md](ARCHITECTURE-CONTEXT.md) | WXRead vs ReadViewSDK 架构差距分析:4 个领域、14 项决策 |
|
||||
|
||||
## 专题文档
|
||||
|
||||
| 文档 | 说明 |
|
||||
@@ -21,7 +30,7 @@
|
||||
## 项目信息
|
||||
|
||||
- **模块总数:** 4 个(EPUBCore、EPUBTextRendering、RDReaderView、EPUBUI)
|
||||
- **Swift 文件数:** 138 个
|
||||
- **测试用例数:** 18 个测试类,66 个测试方法
|
||||
- **Swift 文件数:** 139 个(SDK Sources)
|
||||
- **测试用例数:** 23 个测试类,约 99 个测试方法(UI 测试)
|
||||
- **最低 iOS 版本:** 15.6
|
||||
- **构建方式:** CocoaPods(本地 pod)
|
||||
|
||||
Reference in New Issue
Block a user