清阅阅读器搭建过程
清阅阅读器:实现过程与搭建指南(脱敏版)
整理日期:2026-10-09。对应代码版本:1.4.3(构建号 8)。
本文依据已有源码和开发验证记录整理,说明应用如何实现、如何在另一台 Mac 上搭建,以及如何验证和更新。本文编写期间未重新执行应用构建或测试,文中历史结果均注明对应版本。
文档使用项目相对路径、通用系统路径和占位符,不包含真实开发者账号、签名团队标识、证书、设备标识、个人电脑目录、私人图书内容或阅读记录。文中的命令在项目根目录执行;占位符需要替换成操作者自己的配置。
1. 应用范围与技术选型
清阅是一款面向 iPhone 和 iPad 的 iOS 27 原生阅读器。应用以本地阅读为基础,增加受限书源、离线下载、笔记、听书和阅读统计。基本运行不需要自建后端、数据库服务器或外部语音服务。
| 能力 | 当前实现 |
|---|---|
| 本地图书 | TXT、可重排 EPUB、PDF |
| 漫画与图片 | 图片导入,CBZ/ZIP、CBR/RAR、CB7/7z 漫画,横向和条漫阅读 |
| 书架 | 木质分层展示、统一封面对齐、搜索、分类、子分类、标签、手动排序和批量操作 |
| 阅读交互 | 左右滑动、右侧轻点下一页、中间点击显示工具栏、目录、章节切换和可拖动进度条 |
| 阅读设置 | 主题、文字和背景颜色、字号、行段距、边距、导入字体、背景图片、单本设置 |
| 阅读资料 | 进度恢复、书签、高亮批注、笔记搜索、Markdown 摘录导出 |
| 辅助阅读 | 全文搜索、TXT 自定义分章、简繁显示、自动滚动、亮度与保持亮屏 |
| 听书与统计 | 系统语音朗读、语速和定时设置、后台音频、阅读与听书时长、日历和读完报告 |
| 数据交换 | 系统文件导入、Wi-Fi 传书、压缩包选书、单本导出和本地备份恢复 |
| 在线内容 | 清阅 JSON v1 文字/漫画书源,搜索、目录、章节读取和主动离线下载 |
技术栈如下:
| 技术 | 用途 |
|---|---|
| SwiftUI | 书架、设置、导航、笔记和统计界面 |
| UIKit / TextKit | TXT 选字与排版、点击和滑动手势、翻页过渡、漫画列表复用 |
| WebKit | EPUB 净化后的 HTML/CSS 渲染、分栏分页、DOM 位置恢复 |
| PDFKit | PDF 展示、文字搜索、选区与页级定位 |
| AVFoundation / MediaPlayer | 系统语音、音频会话、后台播放与锁屏媒体控制 |
| Network.framework | 主动开启的局域网文件接收服务 |
| CoreText / ImageIO | 导入字体校验注册、图片下采样 |
| Swift Concurrency | 后台解析、取消任务、资料库和网络状态隔离 |
| Codable / 原子 JSON 文件 | 元数据持久化与版本迁移 |
| Keychain | 独立存储书源认证信息 |
| Swift Package Manager | 管理内部核心模块和第三方依赖 |
第三方运行依赖精确固定为 ZIPFoundation 0.9.20 和 SwiftSoup 2.13.9,均使用 MIT 许可。RAR/7z 读取通过 CArchiveBridge 调用 Apple SDK 提供的系统 libarchive。Readium 曾进行调研,当前工程没有集成其完整阅读导航器。
2. 项目结构
项目根目录/├── Qingyue.xcodeproj/ Xcode 工程├── project.yml XcodeGen 工程清单├── Package.swift 核心包、命令行校验器和依赖配置├── Package.resolved Swift Package Manager 依赖锁定├── Qingyue/│ ├── App/ 应用入口与 AppStore│ ├── Library/ 书架、导入、整理、备份、设置与统计│ ├── Readers/ 各格式阅读器、翻页、笔记、听书和阅读资源│ ├── Sources/ 书源管理和搜索界面│ ├── DesignSystem/ 色彩与通用视觉样式│ ├── Shared/ 公共组件和调试测试适配│ └── Resources/ Info.plist、图标与测试素材├── Sources/│ ├── QingyueCore/ 数据模型、解析、存储、书源和安全逻辑│ ├── CArchiveBridge/ 系统归档读取 API 桥接│ └── QingyueValidate/ 书源格式命令行校验器├── Tests/QingyueCoreTests/ 核心逻辑和协议测试├── QingyueUITests/ UI 自动化与交互测试├── Fixtures/ 原创公开测试素材├── scripts/ 素材生成和验证脚本└── docs/ 设计、协议、许可和验证说明项目采用一个应用配合内部 QingyueCore 包的结构。核心包可在 macOS 上独立测试;系统阅读控件和交互保留在应用层。
主要调用关系:
SwiftUI 页面 ↓ 用户操作AppStore(主线程上的应用状态与任务协调) ├── LibraryRepository actor:元数据、迁移、读写与导入事务 ├── 各格式解析器:章节、正文、图片和图书信息 ├── SourceEngine / HTTPTransport:书源解析与受限网络 └── 阅读器:展示内容、接收手势、回报稳定阅读位置 ↓ 保存进度、书签、笔记和阅读会话3. 实际开发阶段
| 阶段 | 实现重点 | 形成的结果 |
|---|---|---|
| 基础版本 | 工程、数据模型、文件导入、TXT/EPUB/PDF/漫画、书源与缓存 | 完成导入到阅读再到进度保存的基本流程 |
| 1.1 | 横向翻页动画、主题和排版设置、清理确认框、书架标题 | 形成可调整的日常阅读体验 |
| 1.2 | EPUB 图片前页、沉浸式工具栏、章节进度条、木质书架 | 修正前页排版和封面对齐,统一阅读操作入口 |
| 1.3 | 全文搜索、目录工具、图书整理、封面编辑、备份恢复 | 完成资料库管理和本地迁移 |
| 1.4 | 高亮笔记、字体与简繁、单本设置、听书、统计、Wi-Fi、RAR/7z | 增加阅读扩展与更多导入方式,资料库升级至 schema 3 |
| 1.4.1 | EPUB 章节末页偏移、前后台恢复错位 | 改进分页测量和生命周期恢复 |
| 1.4.2 | 单击右侧下一页 | 接入文字、PDF 和横向漫画的边缘点击 |
| 1.4.3 | 右侧点击等待过长 | 拆分点击识别、减少旧页截图等待,并增加响应时间断言 |
开发过程中,每类问题先使用固定样本复现,再修改实现和执行对应回归。测试失败及后续复测分开记录,避免把多轮结果写成一次全量通过。
4. 关键功能的实现方式
4.1 资料库与导入
主要代码位于 Qingyue/App/AppStore.swift 和 Sources/QingyueCore/LibraryRepository.swift。
文件导入经过以下步骤:
- 从系统文件选择器获取文件 URL,申请临时安全作用域访问。
- 使用
NSFileCoordinator协调文件提供方,把文件复制到应用拥有的暂存位置。 - 检查格式、体积与文件内容;压缩包先执行路径和资源限制检查。
- 解析书名、作者、封面、章节或图片列表,建立文件指纹和资源关联。
- 文件落地成功后再提交资料库元数据;失败时清理本次暂存内容。
LibraryRepository 使用 actor 串行处理资料库写入,元数据通过原子文件替换保存。当前数据模型为 schema 3,兼容旧版本字段,拒绝未知的未来版本;解码失败不会用空资料库覆盖原文件。
应用沙盒内的目录用途如下,均为通用结构:
Library/Application Support/Qingyue/ library.json 图书、设置、阅读资料和会话等元数据 Books/ 原书、TXT 分章文件、EPUB 或漫画资源 Cache/Temporary/ 自动产生的临时阅读缓存 Cache/Offline/ 用户主动下载的离线内容 Thumbnails/ 远端封面缩略图 Covers/ 自定义封面 Exports/ 导出产物 Temporary/ 导入、解析和恢复过程的暂存文件Documents/ 用户可见的文件交换目录清理临时缓存和删除主动离线下载是两个独立操作,使用系统确认界面呈现。原始图书与上述缓存分开存放,避免清理缓存时误删已导入的图书。
4.2 TXT 阅读与稳定位置
TextParser 处理常见编码、BOM 和换行归一化,再根据章节规则生成目录和分章文件。页面展示由 TextKit 和可选字的 UITextView 完成,分页测量考虑字号、行距、段距和边距。
保存位置采用归一化正文的 UTF-16 偏移、章节位置和上下文锚点。字号变化后重新分页,再根据文本位置恢复;界面页码只用作展示。用户修改 TXT 分章规则时,先预览结果,再重建章节,并映射已有进度、书签和笔记。
4.3 EPUB 渲染与分页
EPUBParser 读取容器、OPF、spine 和导航信息。渲染前清理不允许的内容,正文交给非持久化数据存储的 WKWebView 展示。出版物脚本被禁用,应用自身的位置逻辑放在隔离的 JavaScript content world 中。
横向阅读通过 CSS 分栏和受控滚动实现;位置保存使用章节相对路径、DOM 锚点、文字偏移及比例回退。字体、图片解码和视图尺寸稳定后才完成分页与位置恢复。
实际修复过的三个问题:
| 问题 | 原因 | 处理方式 |
|---|---|---|
| 封面或说明页空白 | 主题用 background 简写覆盖了出版物背景图片 | 分离背景颜色覆盖与原图显示,识别整页图片内容 |
| 章节末页文字向右挤 | 重复计算边距,且最后一列缺少完整滚动宽度 | 从正文 Range 的实际分栏边界计算页数,补齐末页可滚动范围 |
| 返回前台后正文错位 | 未完成动画、旧尺寸缓存和异步布局回调互相影响 | 失活时暂停上报并清理动画,激活后重新排版,按布局版本丢弃旧回调 |
工具栏作为覆盖层显示和隐藏,保持正文视图尺寸稳定,避免打开目录入口时触发一次无意义的重新分页。
4.4 翻页动画与点击响应
统一翻页入口位于 Qingyue/Readers/PageTurnHost.swift。
文字书支持滑动、覆盖、仿真、无动画和纵向滚动。滑动、覆盖使用视图变换,仿真使用 UIPageViewController 的 page curl;真实可选字正文在过渡后继续作为阅读内容。开启系统“减少动态效果”时关闭过渡动画。
最终的点击规则为:
- 横向阅读时,正文最右侧四分之一区域负责下一页;文字书章节末页继续点击进入下一章。
- 中间区域负责显示或隐藏工具栏;PDF 和漫画可在中间区域双击缩放。
- 右侧独立识别短按松手,不等待双击超时;超过 0.25 秒的按住动作不触发边缘翻页。
- 长按选字、已有选区、链接和原生控件继续优先处理对应操作。PDF、漫画放大后不通过右侧轻点换页。
- 纵向滚动和条漫模式不启用右侧单击分页。
1.4.3 的两项关键优化是拆分右侧与中间识别器,以及让已显示的旧页使用 snapshotView(afterScreenUpdates: false) 获取快照。仿真动画的新页仍在更新后截图,保证内容正确。
响应时间从手指松开开始,测到动画提交后的下一显示帧时间点,目标不超过 200 毫秒。这个指标与用户可调的完整动画播放时长分别计算。诊断探针只在 Debug 显式开启,Release 不包含探针代码。
4.5 PDF 与漫画
PDF 直接使用 PDFKit,支持缩略图、页码跳转、横向或纵向阅读。有文字层的 PDF 支持搜索、选字、笔记和听书;扫描图像本身不经过 OCR。笔记以应用内数据保存,显示时建立内存注释,不改写原 PDF。
漫画使用可复用的 UICollectionView 单元格和可缩放的滚动视图。图片通过 ImageIO 按目标尺寸解码,配合内存缓存、任务取消和少量相邻页预取,控制长漫画的内存占用。横向左右顺序与纵向条漫分别配置。
4.6 书架、主题与阅读工具
书架将封面放入统一尺寸区域,固定底边和书名区域,配合木纹、层板与阴影形成分层展示。图书分类、标签、排序和自定义封面由资料库管理,重启后恢复。
阅读设置使用 ReadingPreferences 保存。全局默认设置与单本设置分开;主题可以覆盖文字色和背景色,字号、字距、行段距、边距和部分动画速度可以调整。导入字体先校验再注册,导入背景图片先下采样。
ReaderScreen 统一组织顶部的返回、目录、搜索、书签、设置,以及底部的上一章、进度条和下一章。文字书进度按章节位置计算,PDF 与本地漫画按页计算;不会把文字书百分比当作整本书预先完成的页数统计。
4.7 搜索、高亮与笔记
BookSearch 在后台逐章搜索 TXT/EPUB,支持取消和结果上限。搜索与阅读使用一致的文字顺序和位置映射,使结果可以跳回原文。
选字后保存摘录、批注、颜色与 locator。TXT 使用 attributed text 高亮,EPUB 使用 CSS Highlight,PDF 使用内存注释。笔记可以按摘录、批注或书名搜索,支持回到原文和导出 Markdown。
简繁显示采用保持位置稳定的逐字转换,不改写原文件;不把它作为具有上下文理解能力的翻译。
4.8 听书与统计
SpeechReader 使用 AVSpeechSynthesizer 分段朗读,通过 AVAudioSession 管理音频,结合 MediaPlayer 提供媒体信息和控制入口。它保存在应用状态生命周期中,使离开正文页面后仍能显示迷你播放器。
听书实现语速、系统声音、暂停继续、章节切换、定时停止和断点保存。Info.plist 声明后台音频能力。音频中断、耳机断开和锁屏控制已加入处理逻辑,真实设备上的完整场景仍需专门验收。
ReadingActivity 统计正文可见且应用在前台时的阅读时间;打开遮挡正文的设置等页面时暂停计时,两分钟无交互时暂停,自动滚动时继续。听书根据实际语音回调计时,避免与该书阅读时长重复累计。跨午夜的时段拆分到对应日期,重复恢复备份时按会话 ID 去重。
4.9 书源、缓存与网络
书源使用清阅 JSON v1。SwiftSoup 处理 HTML/CSS 选择器,受限 JSON 路径处理 JSON 响应;支持搜索、详情、目录、正文或漫画图片提取。
网络层检查 HTTPS、标准端口和精确允许域名,重定向再次检查;请求有超时、并发、频率、体积和分页上限。配置文件不能携带敏感认证请求头,认证信息另存 Keychain。
临时阅读缓存与主动离线下载独立管理。章节和图片落地成功后再提交缓存元数据;下载任务记录未完成章节,失败或重启后可以继续剩余任务。这里的恢复是章节级恢复,不承诺任意服务器的字节级断点续传。
4.10 Wi-Fi、压缩包与备份
Wi-Fi 传书只在用户开启时启动局域网接收服务,每次生成临时访问码。接收后仍由手机确认导入,离开传书页面或进入后台时关闭服务。开启传书可能触发系统本地网络权限提示。
归档读取检查路径穿越、符号链接、重复文件、解压总量、单文件大小和异常压缩比。普通压缩包包含多本书时,先列出候选文件供用户选择。当前不支持加密或分卷压缩包。
.qingyuebackup 包含本地图书、封面、元数据、阅读资料、资源和设置等;在线缓存、主动离线下载和认证凭据不包含在内。恢复先解压校验并展示预览,再按“跳过重复”或“保留两份”合并,已有图书不直接覆盖。单本导出保留或重新打包原出版物,应用内编辑的书名、封面等信息通过资料库备份保存。
5. 在新电脑上搭建
5.1 环境要求
| 项目 | 要求 |
|---|---|
| 电脑 | 能运行 Xcode 27 的 Mac;现有验证记录以 Apple Silicon 环境为主 |
| Xcode | Xcode 27,包含 iOS 27 SDK |
| 模拟器 | 至少安装一个 iOS 27 iPhone 运行时;需要布局验证时增加小屏和 iPad 设备 |
| Swift | 工具链支持 Swift tools 6.0;项目和核心包采用 Swift 5 语言模式 |
| 网络 | 首次解析 SPM 依赖时可访问对应依赖仓库 |
| 签名账号 | 仅真机开发运行需要使用自己的 Apple 开发者账号和 Team |
| XcodeGen | 可选,仅在重新生成工程时需要 |
| Python 3 | 可选,仅在重新生成测试素材或使用相应辅助脚本时需要 |
先启动一次 Xcode,按提示完成许可与组件安装,并在设置中的平台或组件管理界面安装 iOS 27 模拟器运行时。
5.2 获取源码并确认工具链
通过自己的代码仓库或源码压缩包取得项目,保留工程、核心包、锁定文件、测试资源及许可文件。本文不包含原开发环境的仓库地址或登录凭据。
将下面的通用路径替换成实际项目根目录:
cd '/path/to/qingyue-reader'export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developerxcodebuild -versionxcodebuild -showsdksxcrun swift --versionxcrun simctl list runtimes如果 Xcode 安装在其他位置,应相应调整 DEVELOPER_DIR。仅安装 Command Line Tools 无法完成 iOS 应用构建;不要通过单改部署版本数字来绕过缺少 SDK 的问题。
5.3 解析依赖
xcodebuild -resolvePackageDependencies \ -project Qingyue.xcodeproj \ -scheme Qingyue保留现有 Package.resolved。依赖升级应单独验证解析、归档、安全规则和阅读行为,避免排查 UI 问题时同时改变依赖版本。
5.4 打开和运行
open Qingyue.xcodeproj在 Xcode 中选择 Qingyue scheme 和一个 iOS 27 模拟器,再点击 Run。首次正常启动应显示空书架;通过加号导入 Fixtures/ 中的原创样书,分别检查 TXT、EPUB、PDF 和漫画。
工程已包含生成好的 .xcodeproj,首次运行不需要 XcodeGen。只有修改 project.yml、增加目标或调整工程结构时,才需要安装 XcodeGen 并执行:
xcodegen generate生成后检查新增源码、资源、测试目标和签名设置。project.yml 中开发团队保持为空,由使用者自行配置。
5.5 使用命令行构建模拟器版本
xcodebuild \ -project Qingyue.xcodeproj \ -scheme Qingyue \ -destination 'generic/platform=iOS Simulator' \ -derivedDataPath DerivedData/LocalBuild \ CODE_SIGNING_ALLOWED=NO \ build看到 BUILD SUCCEEDED 才表示构建成功。这条命令只编译,不会自动安装到模拟器。
5.6 真机签名与安装
- 在 Xcode 账号设置中登录自己的开发者账号。
- 在应用 target 的 Signing & Capabilities 中选择自己的 Team,启用自动签名。
- 如果创建独立副本,将应用和 UI 测试 target 的 Bundle Identifier 改成自己管理的唯一标识。
- 连接设备,完成电脑信任和开发者模式设置;系统要求时,在设备中信任对应开发者 App。
- 选择设备运行。需要签名交互或启动验证时,保持设备解锁。
更新已有安装时,应保持兼容的 Bundle Identifier 和签名归属,先备份,再覆盖安装。修改 Bundle Identifier 会形成另一份应用安装,原有资料库不会自动迁移,应通过备份恢复处理。
个人免费签名受 Apple 当前的有效期和安装配额限制;出现配额不足时,通过 Xcode 查看实际原因。个人在用阅读器的卸载会影响其本地数据,不能把卸载重装当作普通更新步骤。
5.7 可选:生成测试素材
正常编译和现有测试使用的固定素材已随项目提供。需要调整原创样书或重建图片、PDF 等素材时,可在项目内创建独立 Python 环境:
python3 -m venv DerivedData/FixtureToolsDerivedData/FixtureTools/bin/python -m pip install Pillow reportlab pypdfDerivedData/FixtureTools/bin/python scripts/generate_fixtures.py素材生成脚本会更新固定测试文件和部分生成资源,执行后应审阅差异。scripts/generate_reading_fixtures.py 用于额外的归档测试样本,依赖系统归档工具支持对应写入格式;通常无需重新生成已存在的样本。
6. 测试与验收
6.1 核心逻辑和协议
在完成工具链配置后执行:
swift testswift run qingyue-validate docs/example-source.json核心测试覆盖解析、编码、位置映射、资料库迁移、搜索、归档限制、备份恢复、书源与网络策略,以及阅读统计等。书源校验器检查配置结构与语义,但配置通过不代表某个线上网站当前可访问。
6.2 模拟器 UI 测试
先查看本机可用模拟器,再把占位符替换为自己创建的测试模拟器标识。UI 测试会重置该模拟器中的测试资料库,不应指向保存个人图书的运行环境。
xcrun simctl list devices availableexport QINGYUE_SIMULATOR_ID='REPLACE_WITH_TEST_SIMULATOR_ID'对点击翻页和延迟执行定向回归:
xcodebuild \ -project Qingyue.xcodeproj \ -scheme Qingyue \ -destination "platform=iOS Simulator,id=$QINGYUE_SIMULATOR_ID" \ -derivedDataPath DerivedData/UITests \ -parallel-testing-enabled NO \ -resultBundlePath DerivedData/UITests/ReaderTap.xcresult \ -only-testing:QingyueUITests/ReaderUITests/testRightEdgeTapResponseTime \ -only-testing:QingyueUITests/ReaderUITests/testRightEdgeTapTextAndEPUB \ -only-testing:QingyueUITests/ReaderUITests/testRightEdgeTapPDFAndComic \ CODE_SIGNING_ALLOWED=NO \ test再次运行时,为 -resultBundlePath 指定一个新的结果包名称;Xcode 不会覆盖已有的同名结果目录。
现有综合脚本也可以使用:
./scripts/verify.sh --build-onlySIMULATOR_ID="$QINGYUE_SIMULATOR_ID" ./scripts/verify.sh--build-only 包含核心测试、书源示例校验和应用编译;完整脚本增加模拟器 UI 回归,排除需要额外素材的两项压力测试。部分历史专项脚本带有原开发环境的模拟器配置,换机器时应先审阅配置,优先使用上面可自行指定目标的命令。
生成的结果包和原始日志可能含设备标识、主机路径、界面截图等信息,应保留在本地。若要分享测试结果,另行导出脱敏摘要。
6.3 建议的人工验收流程
| 场景 | 检查内容 |
|---|---|
| 首次导入 | 四类格式可以打开;取消文件选择或导入失败时状态正常 |
| 书架 | 不同比例封面底边对齐;分类、拖动排序和批量操作可恢复 |
| 翻页 | 左右滑动、右侧轻点、章节末页、返回上一章末页均正确 |
| 工具栏 | 默认隐藏;点中间显示;显示时正文位置与尺寸稳定 |
| 排版 | 大小字号、行段距、主题、旋转后能恢复同一段文字 |
| EPUB 特殊页 | 封面、图片前页、长段落和章节末页没有空白或右偏 |
| 生命周期 | 返回书架、切后台、真实锁屏解锁后检查正文和位置 |
| 笔记 | 选字、高亮、批注、搜索、跳转和重启保留 |
| 听书 | 实际出声、语速、暂停、切章、定时、后台和中断处理 |
| 离线与缓存 | 已下载内容离线可读;清理临时缓存保留主动下载 |
| 备份 | 恢复预览、重复处理、阅读资料和资源一致性 |
| 更新 | 记录安装前基线,覆盖更新后检查图书、设置、笔记与进度 |
6.4 已有验证结果与边界
以下是历史开发记录的脱敏汇总,不表示本文生成时重新运行了这些测试:
| 对应阶段 | 已有结果 |
|---|---|
| 1.4 / 1.4.1 核心模块 | 记录中有 52 项核心测试通过 |
| 1.4.1 分页与恢复 | 主模拟器相关回归 6/6,小屏定向检查 1/1 |
| 1.4.3 点击响应 | 最终相关 UI 回归 5/5 通过 |
| 1.4.3 响应计时 | iPhone 17 / iOS 27 模拟器共 26 次,35.8~174.5 ms,中位数 75.8 ms,全部低于 200 ms |
| 1.4.3 交付 | 签名 Release 构建、安装、正常启动和更新前后资料库一致性核对通过 |
26 次计时包含 TXT 和 EPUB 各四种动画、每种三次,以及 PDF、漫画各一次。计时终点是动画提交后的下一显示帧时间点,并非硬件屏幕像素的直接测量;章节加载、其他设备和高系统负载需要另外评估。
前后台自动化不能替代真实手机的锁屏与密码解锁验收。听书的锁屏操作、耳机断开、来电和不同定时时长,以及跨设备 Wi-Fi 传书,应安排真机场景检查。旧版本测试结果也不能直接证明新版本的帧率或长期内存表现。
7. 版本更新与交付流程
- 确定修改范围。 用固定样本复现问题,记录当前行为和验收条件。
- 修改实现。 对分页、存储和手势等问题保留可重复的回归验证。
- 同步版本。 更新
project.yml与生成工程中的营销版本和构建号。 - 执行检查。 运行受影响的核心测试、模拟器用例、格式检查及 Release 编译。
- 核对资料。 更新前保存资料库备份和紧邻安装的最新状态,避免用较早备份回滚最新进度。
- 覆盖安装。 使用自己的签名配置安装,个人设备保持正常启动参数。
- 更新后核对。 检查启动、图书数量、阅读位置、书签、笔记、设置和必要资源。
- 记录结果。 写明测试范围、真实失败及后续复测、已知限制,另存可分享的脱敏摘要。
需要在终端生成签名开发版 Release 时,先替换 Team 占位符,并完成 Xcode 中的账号和签名配置:
export QINGYUE_SIGNING_TEAM='REPLACE_WITH_YOUR_TEAM_ID'xcodebuild \ -project Qingyue.xcodeproj \ -scheme Qingyue \ -configuration Release \ -destination 'generic/platform=iOS' \ -derivedDataPath DerivedData/Release \ -allowProvisioningUpdates \ DEVELOPMENT_TEAM="$QINGYUE_SIGNING_TEAM" \ build产物位于 DerivedData/Release/Build/Products/Release-iphoneos/Qingyue.app。编译成功与设备安装、正常启动分别验证。此流程是开发签名构建,不等于完成 App Store 上架;正式发布还需独立配置分发、归档、商店资料和审核流程。
8. 常见问题排查
| 现象 | 优先检查 |
|---|---|
提示找不到 simctl 或 iOS SDK | DEVELOPER_DIR 是否指向完整 Xcode;对应 SDK 和模拟器运行时是否安装 |
| 找不到指定模拟器 | 重新执行 simctl list devices available,使用自己机器的标识 |
| SPM 解析失败 | 网络、精确依赖版本、锁定文件与 Xcode 依赖解析结果 |
| 编译成功但手机无法安装 | 自己的 Team、Bundle Identifier、描述文件、设备开发者模式和配额 |
| 安装后启动被阻止 | 设备是否解锁,是否已完成系统要求的开发者信任 |
| EPUB 图片页空白 | 是否误覆盖背景图片,资源路径是否正确,图片和字体是否完成加载 |
| 末页偏移或多出一页 | 测量正文实际分栏边界,检查边距和末页滚动范围 |
| 返回前台后排版变化 | 动画是否清理,是否强制重排,旧异步回调是否被布局版本拒绝 |
| 单击翻页出现等待 | 单击是否依赖双击失败,旧页截图是否等待额外绘制,是否正在加载章节 |
| PDF 搜索或听书不可用 | 文件是否包含可提取的文字层 |
| 书源示例无法当作线上站点访问 | 仓库样例包含测试域名;线上使用需要符合协议且可访问的实际书源 |
| Wi-Fi 上传失败 | 服务是否处于开启页面、系统本地网络权限、同一网络、访问码与文件限制 |
| UI 测试结果路径已存在 | 为本轮指定新的 .xcresult 名称,保留旧结果便于对比 |
9. 当前范围限制与后续方向
当前明确未提供 DRM 解锁、固定版式或交互式 EPUB 的完整支持、OCR、账号云同步、OPDS、任意 JavaScript/Legado 书源、加密或分卷压缩包。EPUB 为经过样本验证的可重排实现,复杂竖排、ruby 和出版方样式仍需扩展兼容性验证。PDF 按页恢复,尚未保存精确页内缩放视窗。
后续可以依次扩充 EPUB 样本覆盖、真实锁屏与音频中断测试、大资料库压力测试,以及跨设备导入测试。资料库规模继续扩大时,可评估 SQLite 或 SwiftData,并延续现有稳定 ID、locator 和迁移规则。
10. 文档与资料的脱敏规则
本文件只保留可复现的工程信息和汇总测量值。分享工程或其他附件时,另外检查以下内容:
- 用占位符替换开发者账号、Team、设备标识、描述文件标识和本机个人目录。
- 证书私钥、签名导出文件、密码、认证头和访问码不随文档分发。
- 私人原书、选区摘录、书签、批注、听书断点和阅读统计不放入公共示例。
- 原始设备日志、结果包和截图逐项检查,不直接复制进说明文档。
.gitignore只能阻止通常的 Git 跟踪;手动打包整个目录时仍需排除个人资料和本地备份。- 示例使用项目原创素材或明确允许再分发的资源,随工程保留第三方许可。
本文没有嵌入私人截图、原始设备日志,也没有链接到可能包含设备或账号信息的验证产物,可作为独立 Markdown 说明文件使用。
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!
评论
正在加载评论…
登录后参与评论。





