WangDL 7066200b7b feat: MVVM 架构、全套 UI 页面、浅深色主题、本地持久化、等待名单、AI 动效
- 架构层:ViewModel/ObservableObject、Service/Repository、网络层 APIClient/APIEndpoint/APIError
- 设计系统:Color(light:dark:) 自适应 28 色 Token、ColorSchemeManager 深浅色切换
- 全页面:AI 对话/反馈/回忆/薄弱点、知识库 CRUD、学习工作台、复习计划、学习分析、个人中心/设置
- 登录与引导:Sign in with Apple、AppSession 状态管理、引导流程、演示模式
- 本地持久化:FileCache + PersistenceController(学习任务/复习任务/学习记录)
- 本地化:zh-Hans Localizable.strings ~120 条、ZXStrings 程序化引用、LanguageManager
- 组件库:ZXTabBar/ZXBackHeader/ZXSTaskRow/ZXChartView/ZXTypingIndicator 等 22 个共享组件
- 等待名单:WaitlistView 邮箱收集表单
- 动效:ZXTypingIndicator AI 打字动画、ZXShimmerModifier 骨架屏
- 测试:StudyHomeViewModel/AIChatViewModel/ReviewPlanViewModel/FileCache 共 28 条
- Dynamic Type 支持 + 范围限制

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 22:22:50 +08:00

19 KiB
Raw Blame History

缺失项与待补全方向

基于 v0.1 创业计划文档与当前 iOS 代码对比分析 整理时间2026-05-10

本文档系统性列出知习 iOS App 当前在架构、页面、功能、设计等方面的缺失项,并给出优先级建议。


一、架构层缺失

1.1 MVVM 分层

现状:全部代码写在 SwiftUI View 中,无任何 ViewModel/ObservableObject/@Published。grep 搜索 ViewModel、ObservableObject、@Published 均为零结果。

缺失

  • 无 ViewModel 层,业务逻辑、状态管理、数据转换全部堆在 View 里
  • 无 Model 层,数据结构通过 View 内的局部 struct 或硬编码数据隐式定义
  • 代码不可测试,无法单独验证业务逻辑

计划要求官网与技术基础.md 第 5.3 节):

AIStudyApp/
├── Features/
│   ├── Onboarding/
│   │   ├── Views/       ← 当前有,但无 ViewModel/Model 子目录
│   │   ├── ViewModels/  ← 缺失
│   │   └── Models/      ← 缺失

1.2 Service 层

现状:无任何 Service 类AI 分析、学习记录、用户管理等概念没有对应的服务抽象。

缺失

Service 职责 涉及的计划数据实体
AuthService Apple 登录、Token 管理、会话维护 User
LearningService 学习记录 CRUD、进度追踪 LearningSession
AIService AI 分析请求代理、结果解析 AIAnalysis
ReviewService 复习任务生成、调度 ReviewTask
KnowledgeService 知识库/路径/课程查询 KnowledgeBase, LearningPath, Lesson
FeedbackService 用户反馈提交 Feedback

1.3 Repository 层

现状:零数据持久化,所有"数据"均为 View 中硬编码的 mock。

缺失

  • 无数据访问抽象(未来可能切换 CoreData → API需要 Repository 隔离)
  • 无本地缓存层
  • 无网络数据源层

1.4 网络层

现状:无任何网络请求代码,无 APIClient无 URLSession 调用。

缺失

  • APIClient封装 URLSession注入 baseURL、header、token
  • APIEndpoint枚举化 API 路径,统一请求构建)
  • APIError统一错误模型和处理
  • 请求/响应拦截器日志、token 刷新)
  • Mock 层(本地开发和 UI 预览用)

1.5 依赖注入

现状:无任何 DI 模式Service 和 ViewModel 尚未创建,暂时不存在注入问题。但需要在架构搭建时建立模式。

建议:初期使用构造函数注入 + @EnvironmentObject,避免引入第三方 DI 框架。


二、核心能力缺失

2.1 Sign in with Apple

现状LoginPage 有 UI手机号/邮箱/微信/Apple 入口),但 AIStudyAppApp 仅用 @AppStorage("hasCompletedOnboarding") 控制是否进入主界面,无实际认证。

计划要求:第一版登录方式仅为 Sign in with AppleDemo与MVP.md 第 5.2 节)。

需实现

  • ASAuthorizationController 集成
  • 获取 appleUserId、email、displayName
  • 后端验证 identityToken
  • Token 本地安全存储Keychain
  • 登录状态管理

2.2 后端 API 对接

现状:所有页面为静态 UI无任何网络请求。

计划定义的 P0 API

  • POST /ai/analyze-learning-input — AI 分析用户学习输入
  • POST /ai/chat — AI 对话
  • 用户/知识库/学习记录/反馈 CRUD

2.3 真实 AI 集成

现状AI 相关页面全为静态文本。

需对接

  • 后端 AI Provider 抽象层MiniMax/DeepSeek/OpenAI 等)
  • 结构化 JSON 输出解析
  • AI 分析结果展示(掌握度评分、优缺点、建议)
  • AI 对话流式响应

2.4 本地数据持久化

现状:零持久化实现。

需实现

  • UserDefaults / @AppStorage简单偏好
  • KeychainToken、敏感信息
  • 后续可考虑 CoreData 或 SwiftData学习记录离线缓存

2.5 多语言本地化

现状:所有文案硬编码在 View 中,无 Localizable.xcstrings 文件。

计划要求Demo与MVP.md 第 6 节):

  • 默认简体中文
  • 预留英文
  • App UI 文案使用本地化资源

需实现

  • 创建 Localizable.xcstrings
  • 将所有硬编码文案迁移为 LocalizedStringKey
  • 支持语言切换

2.6 错误/加载/空状态处理

现状:无任何错误处理、加载态、空状态 UI。

至少需要

  • 网络请求 loading 指示器
  • 网络错误提示和重试按钮
  • AI 分析中的等待状态
  • 列表空状态(如知识库为空时的引导)
  • 登录失败错误提示

三、页面层面差距

3.1 与计划页面对比

计划页面 计划优先级 当前状态 说明
启动页/欢迎页 P1 已实现 SplashPage + WelcomePage
登录页 P0 ⚠️ 过度实现 UI 包含计划不做的手机号/邮箱/微信登录
语言与偏好页 P1 未实现 无页面
学习方向选择页 P0 ⚠️ 部分实现 GoalSetupPage 有目标选择,但非学习方向选择
学习路径页 P0 已实现 LibraryDetailPage
今日学习任务页 P0 已实现 StudyHomeView
内容阅读页 P0 已实现 KnowledgeDetailPage
主动回忆/笔记输入页 P0 已实现 DailyThinkingPage + RecallTestPage
AI 分析结果页 P0 已实现 AIFeedbackPage
AI 对话页 P0 已实现 AIChatPage
复习计划页 P0 未独立 仅在 StudyHomeView 任务列表中混合出现
学习进度页 P1 已实现 AnalysisHomeView
设置页 P1 ⚠️ 部分实现 ProfileView 有设置菜单,但功能入口为空
反馈页 P1 未实现 无反馈收集入口

3.2 复习计划页P0 缺失)

计划描述:系统生成复习任务,用户查看待复习内容,按推荐时间安排学习。

当前:复习任务混在 StudyHomeView 的任务列表里(如"高数 - 间隔复习 8 题"),缺少独立的复习计划视图。

需实现

  • 独立的 ReviewPlanView
  • 按时间线展示待复习任务(今天、明天、本周)
  • 复习项来源标注(哪个知识点的第几次复习)
  • 复习完成状态追踪

3.3 反馈页P1 缺失)

计划描述App 内反馈入口,让内测用户提交问题和建议。

需实现

  • 简洁的反馈表单(文本输入 + 分类选择)
  • 提交到后端 /feedback 接口
  • 确认提交状态页

3.4 等待名单入口

计划:官网 /waitlist 页面收集用户App 内也需要引导用户加入等待名单/申请内测。

需考虑:是否在 App 内嵌等待名单入口(如 Welcome 页或设置页)。


四、设计与交互差距

4.1 Tab 结构调整

计划设计4 个 Tab — 学习 | 知识库 | AI助手 | 我的

当前实现5 个 Tab — AI | 知识库 | 学习 | 分析 | 我的

差异分析

  • 当前把"学习"和"分析"拆成了两个独立 Tab
  • 计划把"AI助手"独立为一个 Tab当前 AI 已是独立 Tab
  • "分析"在计划中属于"学习"Tab 下的子页面,不需要顶层 Tab

建议(两种方案):

  • 方案 A:完全对齐计划 → 合并学习和分析为一个 Tab保持 4 Tab
  • 方案 B:保留 5 Tab 结构 → 更新计划文档,论证"分析"独立为 Tab 的合理性(学习数据可视化、学习进度监控是独立价值)

4.2 登录流程简化

计划要求:仅 Sign in with Apple不做手机号/邮箱/微信登录。

当前 UI:包含 4 种登录方式入口。

建议:第一版简化为仅 Apple 登录按钮 + 跳过选项,移除手机号/邮箱/微信登录 UI。

4.3 深色模式

现状:所有页面用 .preferredColorScheme(.dark) 强制深色,未验证浅色模式。

建议:确认是否需要支持浅色模式。如果只做深色,在 DesignTokens 中声明 colorScheme: .dark

4.4 无障碍

现状:未考虑 VoiceOver、Dynamic Type、高对比度等无障碍需求。

至少需做

  • 关键按钮添加 .accessibilityLabel
  • 确保 Dynamic Type 下布局不破碎
  • 重点页面 VoiceOver 测试

4.5 动效

计划要求官网与技术基础.md 第 6.3 节):

  • P0页面过渡、按钮反馈、加载状态、AI 分析中状态、学习完成反馈
  • P1今日任务卡片动效、进度条更新、AI 结果分块出现

当前:仅有基础 SwiftUI 隐式动画withAnimation未实现任何计划中的动效。


五、数据层缺失

5.1 Model 定义

现状:所有数据通过 View 内局部变量或硬编码定义,无独立 Model 文件。

计划中定义的核心实体Demo与MVP.md

User
├── id, appleUserId, displayName, email
├── preferredLanguage, createdAt, lastLoginAt, status

KnowledgeBase
├── id, title, description, language, targetUser
├── createdAt, updatedAt

LearningPath
├── id, knowledgeBaseId, title, description
├── estimatedDays, order

Lesson
├── id, pathId, title, content, objectives
├── keyPoints, recallQuestions, practicePrompt
├── order, estimatedMinutes

LearningSession
├── id, userId, lessonId
├── startedAt, endedAt, userInput
├── aiAnalysis, masteryScore, weakPoints
├── nextSuggestion, reviewAt

AIAnalysis
├── id, userId, sessionId
├── inputText, outputJson, masteryScore
├── weakPoints, suggestions
├── modelName, createdAt, costEstimate

ReviewTask
├── id, userId, lessonId, sourceSessionId
├── reviewType, scheduledAt, completedAt, status

Feedback
UserLearningProfile

需实现:在 Features/*/Models/ 下创建对应的 Swift struct需 Codable、Identifiable

5.2 API Contract

现状:无 API 类型定义。

建议:参考计划中定义的 JSON 结构,先创建 Swift Model再定义 API 请求/响应类型Request/Response struct实现前后端类型同构。

5.3 数据流规范

现状View 直接持有 @State无数据流管理。

建议

  • ViewModel 持有 @Published 状态
  • ViewModel 通过 Service 获取数据
  • Service 通过 Repository 访问数据源
  • View 通过 @StateObject / @ObservedObject 绑定 ViewModel

六、工程化缺失

6.1 大文件拆分

当前问题

文件 行数 包含内容
AIStudyAppApp.swift ~187 5 个完整页面 + 多个子组件
DailyThinkingPage.swift ~200+ 5 个页面 + 共享组件
LibrarySubpages.swift ~150+ 6 个页面 + 组件

建议:每个页面一个文件,共享组件移到 Shared/Components/

6.2 共享组件管理

现状ZXTabBar 在 ContentView.swiftZXBackHeader 在 DailyThinkingPage.swiftZXCardRow 在 LibrarySubpages.swift散落各处。

建议:集中到 Shared/Components/,建立组件目录如:

Shared/Components/
├── ZXTabBar.swift
├── ZXBackHeader.swift
├── ZXAIInputBar.swift
├── ZXScoreBox.swift
├── ZXIconBtn.swift
├── ZXCardRow.swift
├── ZXChip.swift
├── ZXQuickAction.swift
└── ZXStatBadge.swift

6.3 测试

现状:无任何测试代码。

至少需要

  • ViewModel 单元测试(当 ViewModel 创建后)
  • Service 层单元测试Mock Repository
  • 关键 UI 流程的 Snapshot 测试

6.4 CI/CD

现状:无。

建议(后续):

  • GitHub Actions / Xcode Cloud 自动构建
  • TestFlight 自动分发

6.5 崩溃监控与埋点

现状:无。

建议:接入 Firebase Crashlytics 或类似服务,至少在 TestFlight 阶段要有崩溃收集能力。


七、优先级建议

P0 — 必须在接后端前完成

优先级 项目 理由
P0 创建 Model 层(所有数据实体) 是 Service/ViewModel/API 的基础
P0 创建 API Contract 类型定义 前后端对齐的前提
P0 搭建 APIClient + APIEndpoint 所有后端交互的唯一通道
P0 实现 AuthService + Apple 登录 用户身份是学习记录的前提
P0 简化登录页为纯 Apple 登录 对齐计划,减少不必要 UI
P0 实现复习计划独立页 计划标记 P0当前缺失
P0 拆分大文件 降低后续修改的认知负担
P0 集中共享组件 避免组件散落导致重复开发
P0 添加加载/错误/空状态处理 真机使用的基本体验保障

P1 — 与后端对接同步推进

优先级 项目 理由
P1 搭建 ViewModel 层(逐步迁移) 架构分层,但不阻塞功能开发
P1 搭建 Service 层 随 API 对接自然建立
P1 实现本地化架构 越晚做返工越多
P1 实现反馈页 TestFlight 内测必须
P1 实现 Keychain 存储 Token 安全存储

P2 — App Store 前完成

优先级 项目 理由
P2 Repository 层 当需要本地缓存 + 网络切换时再做
P2 动效补充 体验优化,不阻塞功能
P2 无障碍适配 App Store 审核加分项
P2 测试 用户量增长后需要
P2 Tab 结构调整决策 需要更多用户反馈来决策

八、总结

当前 iOS 项目完成了 UI 层的全量搭建21 页),但缺少"能让产品真正运转"的架构底座和数据能力。核心矛盾是:

UI 超前,架构滞后。页面能点,但无数据、无认证、无 AI、无服务。

接下来最合理的推进顺序:

1. 建 Model 层(夯实数据基础)
       ↓
2. 搭网络层 + Apple 登录(接通身份和通信)
       ↓
3. 后端 API 逐个对接AI 分析和学习记录最先)
       ↓
4. 重构 ViewModel 层(顺带解决架构问题)
       ↓
5. 补充缺失页面(复习计划、反馈)
       ↓
6. 本地化 + 动效 + 测试(打磨)

一句话:先把骨架搭对,再长肉。

对,登录第一版只保留苹果登录,这是最正确的决策。

你整理的文档里也已经明确写了:当前 LoginPage 虽然有手机号、邮箱、微信、Apple 入口,但第一版计划要求只做 Sign in with Apple并且需要实现 Apple 授权、后端验证、Keychain 存 token 和登录状态管理。 文档后面也建议把登录页简化为 仅 Apple 登录按钮 + 可选跳过入口,移除手机号、邮箱、微信登录 UI。


登录页第一版最终方案

正式入口只保留

Sign in with Apple

删除这些入口

手机号登录
邮箱登录
微信登录
验证码登录
第三方微信登录

“跳过”要不要保留?

我建议:

开发环境可以保留
正式环境不要明显展示

也就是说:

#if DEBUG
Button("跳过,进入演示模式") { ... }
#endif

不要把“跳过”作为正式登录入口不然后面学习记录、知识库、AI 分析都会涉及匿名用户迁移,很麻烦。


登录流程应该这样设计

打开 App
↓
AppSession 检查 Keychain 里有没有 refreshToken
↓
如果有 token调用 /auth/refresh 或 /users/me
↓
成功:进入主界面
↓
失败:进入登录页
↓
用户点击 Sign in with Apple
↓
iOS 获取 identityToken / authorizationCode / userIdentifier
↓
POST /api/auth/apple
↓
后端返回 accessToken / refreshToken / user
↓
iOS 把 token 存到 Keychain
↓
AppSession 更新 currentUser
↓
判断 onboardingCompleted
↓
未完成:进入首次引导 / 学习目标设置
已完成:进入主界面

iOS 需要实现的文件

登录相关最少这些:

Features/Auth/Views/LoginView.swift
Features/Auth/ViewModels/LoginViewModel.swift

Core/Services/AuthService.swift
Core/Services/AuthServiceProtocol.swift

Core/Storage/KeychainStore.swift
Core/Storage/TokenStore.swift

App/AppSession.swift

Core/Models/AuthModels.swift
Core/Models/User.swift

接口请求结构

iOS 登录成功后给后端:

struct AppleLoginRequest: Encodable {
    let identityToken: String
    let authorizationCode: String?
    let userIdentifier: String
    let fullName: AppleFullName?
    let email: String?
}

struct AppleFullName: Encodable {
    let givenName: String?
    let familyName: String?
}

后端返回:

struct AuthResponse: Decodable {
    let accessToken: String
    let refreshToken: String
    let expiresIn: Int
    let user: User
}

Token 存储:

accessToken  → Keychain
refreshToken → Keychain
不要存 UserDefaults

给 Trae 的修改指令

你可以直接发这段:

现在调整知习 iOS App 的登录方案。

第一版登录方式只保留 Sign in with Apple。

请删除登录页中的:
- 手机号登录
- 邮箱登录
- 微信登录
- 其他第三方登录入口

正式 UI 只保留:
- Sign in with Apple 按钮
- 简短产品说明
- 隐私政策 / 用户协议入口

开发环境可以保留一个 DEBUG-only 的“跳过,进入演示模式”按钮,但不能出现在 Release 版本。

请实现以下架构:

1. LoginView
- 只展示 Apple 登录按钮
- 点击后调用 LoginViewModel.loginWithApple()
- 支持 loading / error 状态
- 登录失败时展示错误提示

2. LoginViewModel
- @MainActor
- ObservableObject
- @Published isLoading
- @Published errorMessage
- 调用 AuthService 处理 Apple 登录

3. AuthService
- 集成 ASAuthorizationController
- 获取 identityToken
- 获取 authorizationCode
- 获取 userIdentifier
- 获取 email / fullName如果 Apple 返回
- 调用后端 POST /api/auth/apple
- 接收 accessToken / refreshToken / user

4. TokenStore / KeychainStore
- accessToken 存 Keychain
- refreshToken 存 Keychain
- 支持 save / load / clear

5. AppSession
- 管理全局登录状态
- currentUser
- isAuthenticated
- bootstrap()
- loginWithApple()
- logout()
- refreshSession()

6. App 启动逻辑
- 启动时先检查 Keychain 中是否存在 refreshToken
- 如果存在,调用后端 refresh 或 /users/me
- 成功后进入主界面
- 失败则清空 token 并进入登录页

7. 登录成功后的跳转
- 如果 user.onboardingCompleted == false进入首次使用引导 / 学习目标设置
- 如果 user.onboardingCompleted == true进入 MainTabView

8. 注意
- 不要再使用 @AppStorage("hasCompletedOnboarding") 单独决定是否进入主界面
- 登录状态必须由 AppSession + Keychain token 决定
- 不要把 token 存到 UserDefaults
- 不要在 View 里直接写网络请求
- 不要在 View 里直接处理 Apple 登录细节
- 不要改变其他主页面 UI

登录页内容建议

页面可以非常简单:

知习

更懂你,更会学。

用 AI 把知识库、主动回忆和间隔复习连接起来,
从“看过”走向“真正学会”。

[ Sign in with Apple ]

登录即代表你同意《用户服务协议》和《隐私政策》

就够了。 第一版登录越简单越好,别再做多登录方式。