- 架构层: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>
669 lines
19 KiB
Markdown
669 lines
19 KiB
Markdown
# 缺失项与待补全方向
|
||
|
||
> 基于 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 Apple(`Demo与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(简单偏好)
|
||
- Keychain(Token、敏感信息)
|
||
- 后续可考虑 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.swift,`ZXBackHeader` 在 DailyThinkingPage.swift,`ZXCardRow` 在 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。
|
||
|
||
---
|
||
|
||
# 登录页第一版最终方案
|
||
|
||
## 正式入口只保留
|
||
|
||
```text
|
||
Sign in with Apple
|
||
```
|
||
|
||
## 删除这些入口
|
||
|
||
```text
|
||
手机号登录
|
||
邮箱登录
|
||
微信登录
|
||
验证码登录
|
||
第三方微信登录
|
||
```
|
||
|
||
## “跳过”要不要保留?
|
||
|
||
我建议:
|
||
|
||
```text
|
||
开发环境可以保留
|
||
正式环境不要明显展示
|
||
```
|
||
|
||
也就是说:
|
||
|
||
```swift
|
||
#if DEBUG
|
||
Button("跳过,进入演示模式") { ... }
|
||
#endif
|
||
```
|
||
|
||
不要把“跳过”作为正式登录入口,不然后面学习记录、知识库、AI 分析都会涉及匿名用户迁移,很麻烦。
|
||
|
||
---
|
||
|
||
# 登录流程应该这样设计
|
||
|
||
```text
|
||
打开 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 需要实现的文件
|
||
|
||
登录相关最少这些:
|
||
|
||
```text
|
||
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 登录成功后给后端:
|
||
|
||
```swift
|
||
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?
|
||
}
|
||
```
|
||
|
||
后端返回:
|
||
|
||
```swift
|
||
struct AuthResponse: Decodable {
|
||
let accessToken: String
|
||
let refreshToken: String
|
||
let expiresIn: Int
|
||
let user: User
|
||
}
|
||
```
|
||
|
||
Token 存储:
|
||
|
||
```text
|
||
accessToken → Keychain
|
||
refreshToken → Keychain
|
||
不要存 UserDefaults
|
||
```
|
||
|
||
---
|
||
|
||
# 给 Trae 的修改指令
|
||
|
||
你可以直接发这段:
|
||
|
||
```text
|
||
现在调整知习 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
|
||
```
|
||
|
||
---
|
||
|
||
# 登录页内容建议
|
||
|
||
页面可以非常简单:
|
||
|
||
```text
|
||
知习
|
||
|
||
更懂你,更会学。
|
||
|
||
用 AI 把知识库、主动回忆和间隔复习连接起来,
|
||
从“看过”走向“真正学会”。
|
||
|
||
[ Sign in with Apple ]
|
||
|
||
登录即代表你同意《用户服务协议》和《隐私政策》
|
||
```
|
||
|
||
就够了。
|
||
第一版登录越简单越好,别再做多登录方式。
|