视频加载失败

清阅阅读器搭建过程

7412 字
37 分钟
清阅阅读器搭建过程

清阅阅读器:实现过程与搭建指南(脱敏版)#

整理日期: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 / TextKitTXT 选字与排版、点击和滑动手势、翻页过渡、漫画列表复用
WebKitEPUB 净化后的 HTML/CSS 渲染、分栏分页、DOM 位置恢复
PDFKitPDF 展示、文字搜索、选区与页级定位
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.2EPUB 图片前页、沉浸式工具栏、章节进度条、木质书架修正前页排版和封面对齐,统一阅读操作入口
1.3全文搜索、目录工具、图书整理、封面编辑、备份恢复完成资料库管理和本地迁移
1.4高亮笔记、字体与简繁、单本设置、听书、统计、Wi-Fi、RAR/7z增加阅读扩展与更多导入方式,资料库升级至 schema 3
1.4.1EPUB 章节末页偏移、前后台恢复错位改进分页测量和生命周期恢复
1.4.2单击右侧下一页接入文字、PDF 和横向漫画的边缘点击
1.4.3右侧点击等待过长拆分点击识别、减少旧页截图等待,并增加响应时间断言

开发过程中,每类问题先使用固定样本复现,再修改实现和执行对应回归。测试失败及后续复测分开记录,避免把多轮结果写成一次全量通过。

4. 关键功能的实现方式#

4.1 资料库与导入#

主要代码位于 Qingyue/App/AppStore.swift 和 Sources/QingyueCore/LibraryRepository.swift。

文件导入经过以下步骤:

  1. 从系统文件选择器获取文件 URL,申请临时安全作用域访问。
  2. 使用 NSFileCoordinator 协调文件提供方,把文件复制到应用拥有的暂存位置。
  3. 检查格式、体积与文件内容;压缩包先执行路径和资源限制检查。
  4. 解析书名、作者、封面、章节或图片列表,建立文件指纹和资源关联。
  5. 文件落地成功后再提交资料库元数据;失败时清理本次暂存内容。

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 环境为主
XcodeXcode 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 获取源码并确认工具链#

通过自己的代码仓库或源码压缩包取得项目,保留工程、核心包、锁定文件、测试资源及许可文件。本文不包含原开发环境的仓库地址或登录凭据。

将下面的通用路径替换成实际项目根目录:

Terminal window
cd '/path/to/qingyue-reader'
export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
xcodebuild -version
xcodebuild -showsdks
xcrun swift --version
xcrun simctl list runtimes

如果 Xcode 安装在其他位置,应相应调整 DEVELOPER_DIR。仅安装 Command Line Tools 无法完成 iOS 应用构建;不要通过单改部署版本数字来绕过缺少 SDK 的问题。

5.3 解析依赖#

Terminal window
xcodebuild -resolvePackageDependencies \
-project Qingyue.xcodeproj \
-scheme Qingyue

保留现有 Package.resolved。依赖升级应单独验证解析、归档、安全规则和阅读行为,避免排查 UI 问题时同时改变依赖版本。

5.4 打开和运行#

Terminal window
open Qingyue.xcodeproj

在 Xcode 中选择 Qingyue scheme 和一个 iOS 27 模拟器,再点击 Run。首次正常启动应显示空书架;通过加号导入 Fixtures/ 中的原创样书,分别检查 TXT、EPUB、PDF 和漫画。

工程已包含生成好的 .xcodeproj,首次运行不需要 XcodeGen。只有修改 project.yml、增加目标或调整工程结构时,才需要安装 XcodeGen 并执行:

Terminal window
xcodegen generate

生成后检查新增源码、资源、测试目标和签名设置。project.yml 中开发团队保持为空,由使用者自行配置。

5.5 使用命令行构建模拟器版本#

Terminal window
xcodebuild \
-project Qingyue.xcodeproj \
-scheme Qingyue \
-destination 'generic/platform=iOS Simulator' \
-derivedDataPath DerivedData/LocalBuild \
CODE_SIGNING_ALLOWED=NO \
build

看到 BUILD SUCCEEDED 才表示构建成功。这条命令只编译,不会自动安装到模拟器。

5.6 真机签名与安装#

  1. 在 Xcode 账号设置中登录自己的开发者账号。
  2. 在应用 target 的 Signing & Capabilities 中选择自己的 Team,启用自动签名。
  3. 如果创建独立副本,将应用和 UI 测试 target 的 Bundle Identifier 改成自己管理的唯一标识。
  4. 连接设备,完成电脑信任和开发者模式设置;系统要求时,在设备中信任对应开发者 App。
  5. 选择设备运行。需要签名交互或启动验证时,保持设备解锁。

更新已有安装时,应保持兼容的 Bundle Identifier 和签名归属,先备份,再覆盖安装。修改 Bundle Identifier 会形成另一份应用安装,原有资料库不会自动迁移,应通过备份恢复处理。

个人免费签名受 Apple 当前的有效期和安装配额限制;出现配额不足时,通过 Xcode 查看实际原因。个人在用阅读器的卸载会影响其本地数据,不能把卸载重装当作普通更新步骤。

5.7 可选:生成测试素材#

正常编译和现有测试使用的固定素材已随项目提供。需要调整原创样书或重建图片、PDF 等素材时,可在项目内创建独立 Python 环境:

Terminal window
python3 -m venv DerivedData/FixtureTools
DerivedData/FixtureTools/bin/python -m pip install Pillow reportlab pypdf
DerivedData/FixtureTools/bin/python scripts/generate_fixtures.py

素材生成脚本会更新固定测试文件和部分生成资源,执行后应审阅差异。scripts/generate_reading_fixtures.py 用于额外的归档测试样本,依赖系统归档工具支持对应写入格式;通常无需重新生成已存在的样本。

6. 测试与验收#

6.1 核心逻辑和协议#

在完成工具链配置后执行:

Terminal window
swift test
swift run qingyue-validate docs/example-source.json

核心测试覆盖解析、编码、位置映射、资料库迁移、搜索、归档限制、备份恢复、书源与网络策略,以及阅读统计等。书源校验器检查配置结构与语义,但配置通过不代表某个线上网站当前可访问。

6.2 模拟器 UI 测试#

先查看本机可用模拟器,再把占位符替换为自己创建的测试模拟器标识。UI 测试会重置该模拟器中的测试资料库,不应指向保存个人图书的运行环境。

Terminal window
xcrun simctl list devices available
export QINGYUE_SIMULATOR_ID='REPLACE_WITH_TEST_SIMULATOR_ID'

对点击翻页和延迟执行定向回归:

Terminal window
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 不会覆盖已有的同名结果目录。

现有综合脚本也可以使用:

Terminal window
./scripts/verify.sh --build-only
SIMULATOR_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. 版本更新与交付流程#

  1. 确定修改范围。 用固定样本复现问题,记录当前行为和验收条件。
  2. 修改实现。 对分页、存储和手势等问题保留可重复的回归验证。
  3. 同步版本。 更新 project.yml 与生成工程中的营销版本和构建号。
  4. 执行检查。 运行受影响的核心测试、模拟器用例、格式检查及 Release 编译。
  5. 核对资料。 更新前保存资料库备份和紧邻安装的最新状态,避免用较早备份回滚最新进度。
  6. 覆盖安装。 使用自己的签名配置安装,个人设备保持正常启动参数。
  7. 更新后核对。 检查启动、图书数量、阅读位置、书签、笔记、设置和必要资源。
  8. 记录结果。 写明测试范围、真实失败及后续复测、已知限制,另存可分享的脱敏摘要。

需要在终端生成签名开发版 Release 时,先替换 Team 占位符,并完成 Xcode 中的账号和签名配置:

Terminal window
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 SDKDEVELOPER_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 说明文件使用。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
清阅阅读器搭建过程
https://bytefun.site/posts/new-ef1aed85/
作者
木子
发布于
2026-10-09
许可协议
CC BY-NC-SA 4.0

评论

正在加载评论…

登录后参与评论。

Profile Image of the Author
木子
一个小菜鸟
公告
少年恃险若平地,独倚长剑凌清秋
最新动态
站点统计
文章
5
分类
3
标签
2
总字数
26,224
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Local
博客版本
Firefly v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录