Upload files to "逐课课纲"
This commit is contained in:
@@ -0,0 +1,102 @@
|
|||||||
|
# 部门课 05:校园地点小指南与列表到详情的信息流
|
||||||
|
|
||||||
|
## 教学目标
|
||||||
|
|
||||||
|
- 区分上一节的“状态发生变化”和本节的“只读数据在页面之间传递”。
|
||||||
|
- 认识模型、实例、数组和当前列表项之间的关系。
|
||||||
|
- 共同定义轻量的 `CampusPlace`,理解各属性分别描述地点的哪些信息。
|
||||||
|
- 知道 `Identifiable` 帮助列表区分每一项,`Hashable` 让地点值能够参与值驱动导航,但不展开协议和哈希原理。
|
||||||
|
- 能阅读 `List(places) { place in ... }`,理解 `places` 是整组数据,`place` 是当前一项。
|
||||||
|
- 使用 `NavigationStack`、`NavigationLink(value:)` 和 `navigationDestination(for:)` 完成值驱动导航。
|
||||||
|
- 理解详情页通过普通的 `let place` 接收只读数据,不需要 `@State` 或 `@Binding`。
|
||||||
|
- 能说明为什么同一个详情 View 在点击不同列表项后会显示不同内容。
|
||||||
|
- 继续使用 Preview 辅助观察,并以模拟器 Build/Run 作为最终验证。
|
||||||
|
|
||||||
|
## 教学内容
|
||||||
|
|
||||||
|
### 1. 从状态变化到只读信息流
|
||||||
|
|
||||||
|
- 课堂从只有占位标题、已经 Build/Run 的独立项目开始,全部共同代码放在同一 Swift 文件中。
|
||||||
|
- 展示完成版校园地点小指南,将数据流画成:
|
||||||
|
|
||||||
|
```text
|
||||||
|
CampusPlace 模型
|
||||||
|
→ 地点数组
|
||||||
|
→ List 逐项展示
|
||||||
|
→ NavigationLink 送出当前地点值
|
||||||
|
→ navigationDestination 接收
|
||||||
|
→ CampusPlaceDetailView 读取并展示
|
||||||
|
```
|
||||||
|
|
||||||
|
- 对比部门课 4:`@Binding` 用于子 View 修改父 View 的状态,普通的 `let place` 用于详情 View 只读地接收一个值。
|
||||||
|
- 强调本节不修改地点数组,也不要求使用 `@State`;重点是追踪一条地点数据怎样从来源到达详情页。
|
||||||
|
|
||||||
|
### 2. 共同建立模型和两条数据
|
||||||
|
|
||||||
|
- 共同定义轻量、不可变的 `CampusPlace`,包含唯一 `id`、地点名称、类别、SF Symbol 名称、简介和一条建议。
|
||||||
|
- 只用入门语言说明:模型规定“每个地点有哪些信息”,实例是一个具体地点,数组保存多个同类型地点。
|
||||||
|
- 为模型采用 `Identifiable` 和 `Hashable`:前者帮助列表区分每一项,后者让地点值能够参与本节的值驱动导航。
|
||||||
|
- 共同建立包含两个地点的只读数组,观察同一个模型怎样产生内容不同、结构一致的实例。
|
||||||
|
- 不展开协议、哈希算法、类型系统或 `UUID` 的实现原理。
|
||||||
|
|
||||||
|
### 3. 从数组生成列表
|
||||||
|
|
||||||
|
- 使用 `List(places) { place in ... }` 按数组内容生成列表行,不额外引入 `ForEach`。
|
||||||
|
- 解释 `places` 表示整组地点,闭包中的 `place` 表示当前正在生成的一项;不系统讲授闭包语法。
|
||||||
|
- 列表行直接写在 `List` 中,使用此前学过的 `HStack`、`Text`、`Image` 和基础 Modifier 展示系统图标、地点名称和类别。
|
||||||
|
- 修改其中一个地点的内容并重新运行,观察列表怎样从同一份数组读取变化。
|
||||||
|
|
||||||
|
### 4. 值驱动导航与详情参数
|
||||||
|
|
||||||
|
- 使用 `NavigationStack` 建立列表页、详情页和系统返回操作组成的页面层级。
|
||||||
|
- 使用 `NavigationLink(value: place)`,让每一行在点击后送出当前的 `CampusPlace` 值。
|
||||||
|
- 使用 `.navigationDestination(for: CampusPlace.self)` 接收同类型导航值,并创建 `CampusPlaceDetailView(place: place)`。
|
||||||
|
- 只将详情页拆成独立 View;详情页使用 `let place: CampusPlace`,读取收到地点的名称、图标、简介和建议。
|
||||||
|
- 分别点击两个地点并返回,检查详情内容是否始终对应当前选择。
|
||||||
|
- 让学员在参考课堂结构的情况下加入第三个地点并重新 Build/Run。
|
||||||
|
- 本课使用完整的 `CampusPlace` 作为轻量、不可变的教学导航值,不将其扩展为正式项目的通用路由架构,也不引入 `NavigationPath`、路径恢复或编程式导航。
|
||||||
|
|
||||||
|
授课干部可以根据课堂节奏选择以下 AI 互动,不作为共同完成要求:
|
||||||
|
|
||||||
|
- 让 AI 标出一条地点数据从数组到详情页经过的代码位置,再由学员自行核对。
|
||||||
|
- 让 AI 用入门语言解释 `Identifiable` 和 `Hashable` 在本例中的作用。
|
||||||
|
- 让 AI 检查新增地点是否缺少必要属性,或建议一处局部文案和样式调整。
|
||||||
|
|
||||||
|
AI 不应通过整段重写代替课堂中的因果追踪,所有修改仍需重新 Build/Run。
|
||||||
|
|
||||||
|
本节不讲授数组的新增、删除和编辑、`@State` 化的数据集合、`ForEach` 的独立用法、搜索、筛选、按类别分组、收藏、MapKit、定位、网络、异步图片、持久化、CRUD、`NavigationPath`、深层导航、路由架构、协议原理、哈希算法或完整 Swift 类型系统。
|
||||||
|
|
||||||
|
授课干部备课时可参考 [Understanding the navigation stack](https://developer.apple.com/documentation/swiftui/understanding-the-navigation-stack) 和 [Displaying data in lists](https://developer.apple.com/documentation/swiftui/displaying-data-in-lists)。
|
||||||
|
|
||||||
|
## 课后作业
|
||||||
|
|
||||||
|
### 最低完成标准
|
||||||
|
|
||||||
|
- 完成并个性化“校园地点小指南”。
|
||||||
|
- 保留或修改课堂共同完成的两个地点,并参考课堂结构自行补充第三个地点。
|
||||||
|
- 第三个地点可以是真实公共校园空间或虚构地点,不包含个人住址、具体活动轨迹或其他私人信息。
|
||||||
|
- 三个地点均显示在列表中;点击不同地点后,同一个 `CampusPlaceDetailView` 显示对应内容,系统返回功能正常。
|
||||||
|
- 不为每个地点分别写死一个详情页面。
|
||||||
|
- 实际 Build/Run 后,通过个人 Fork 和作业分支提交完整项目源码 PR。
|
||||||
|
- 简要说明 `places` 与当前 `place` 的区别,以及“列表项—导航值—详情参数”的传递关系。
|
||||||
|
|
||||||
|
允许参考课堂代码、步骤资料或 AI,不要求脱离参考独立默写模型。
|
||||||
|
|
||||||
|
### 建议完成标准
|
||||||
|
|
||||||
|
- 增加更多地点。
|
||||||
|
- 新增一个展示字段。
|
||||||
|
- 调整列表或详情样式。
|
||||||
|
|
||||||
|
本节不要求提交截图或录屏。目录、分支、PR 模板和操作细节在备课时确定;未完成或提交受阻时按[共同安排](../iOS部2026十课初步规划.md)记录当前状态并求助。
|
||||||
|
|
||||||
|
## 验收方式
|
||||||
|
|
||||||
|
- 授课干部能够从 PR 中取得完整项目,并在模拟器中成功 Build/Run。
|
||||||
|
- 列表至少包含三个地点,其中第三个由学员自行补充;点击至少两个不同地点时,详情与选择一致,系统返回正常。
|
||||||
|
- `CampusPlace` 能被列表识别并用于值驱动导航;使用 `NavigationLink(value:)` 和 `navigationDestination(for:)`,不为每项写死独立详情。
|
||||||
|
- `CampusPlaceDetailView` 通过普通只读参数接收地点。
|
||||||
|
- 结合 PR 简短说明了解数据流理解;说明不完整用于反馈,不因表达不够准确拒绝已达到基础要求的作品。
|
||||||
|
- 按共同安排检查目录和隐私,确认后给出简短反馈并合并。
|
||||||
|
- 不要求解释协议或哈希底层原理,也不要求额外地图、网络、搜索、收藏或复杂路由,不以视觉精致度和地点数量排名。
|
||||||
|
- 部门课 06 开头可选少量作品复习信息流,不转化为逐人技术答辩、评分或淘汰。未完成的 Draft PR 保留并提供帮助。
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
# 部门课 06:从输入到清单与内存数据
|
||||||
|
|
||||||
|
## 教学目标
|
||||||
|
|
||||||
|
- 区分输入框中尚未确认的草稿和已经加入清单的数据。
|
||||||
|
- 理解 `TextField` 通过 Binding 读写当前输入内容。
|
||||||
|
- 理解当前 View 可以使用 `@State` 保存临时字符串和可变数组。
|
||||||
|
- 认识模型、单个条目和条目数组之间的关系。
|
||||||
|
- 理解按钮如何读取草稿、创建条目并将其加入数组。
|
||||||
|
- 观察数组改变后,SwiftUI 如何更新 `List`。
|
||||||
|
- 知道条目需要唯一身份供 `List` 区分。
|
||||||
|
- 知道这些数据只存在于当前运行,重新启动 App 后会恢复初始状态。
|
||||||
|
- 能根据授课干部的基础 Demo,将同一数据流迁移为自己的主题清单。
|
||||||
|
|
||||||
|
## 教学内容
|
||||||
|
|
||||||
|
本节围绕以下数据流展开:
|
||||||
|
|
||||||
|
```text
|
||||||
|
用户输入
|
||||||
|
→ TextField 更新草稿
|
||||||
|
→ 点击添加
|
||||||
|
→ 检查草稿
|
||||||
|
→ 创建新条目
|
||||||
|
→ 加入数组
|
||||||
|
→ List 更新
|
||||||
|
→ 清空输入框
|
||||||
|
```
|
||||||
|
|
||||||
|
主要讲授:
|
||||||
|
|
||||||
|
- `draft` 与 `items` 分别承担什么作用。
|
||||||
|
- `TextField`、`$draft` 和 `@State` 的关系。
|
||||||
|
- 单字段条目模型、唯一身份和数组。
|
||||||
|
- Button action 如何读取并修改数据。
|
||||||
|
- 使用 `append` 向内存数组加入新条目。
|
||||||
|
- 防止空内容进入清单,并在添加成功后清空输入框。
|
||||||
|
- `List` 怎样根据数组内容显示多条数据。
|
||||||
|
- App 重新运行后清单消失的原因,以及它与下一节持久化课程的关系。
|
||||||
|
|
||||||
|
授课干部使用一个基础的“临时词条收集器”作为课堂 Demo。Demo 与学员作业同源,基本对应作业的基础版本,包括单字段条目、输入框、添加按钮、列表、空输入处理、添加后清空输入框和重启后恢复为空。
|
||||||
|
|
||||||
|
Demo 提前完成并经过 Build/Run,课堂上由授课干部实际敲写和演示关键部分,同时保留完成版作为演示失败时的备用。学员主要观察、预测和讨论,不要求同步跟抄。
|
||||||
|
|
||||||
|
本节不展开删除、编辑、搜索、排序、网络、复杂表单、Observable、SwiftData、完整 CRUD 或持久化。
|
||||||
|
|
||||||
|
后续细化备课时可参考 Apple 的 [TextField](https://developer.apple.com/documentation/SwiftUI/TextField) 和 [Managing user interface state](https://developer.apple.com/documentation/swiftui/managing-user-interface-state)。
|
||||||
|
|
||||||
|
## 课后作业
|
||||||
|
|
||||||
|
作业方向为“我的主题清单”。学员从系统 SwiftUI 空白模板开始,可以选择书、电影、游戏、歌曲、食物、愿望或其他非敏感主题。
|
||||||
|
|
||||||
|
### 最低完成标准
|
||||||
|
|
||||||
|
- 项目能够 Build/Run。
|
||||||
|
- 有明确的自选清单主题。
|
||||||
|
- 使用带唯一身份的单字段条目模型。
|
||||||
|
- 使用 `@State` 保存输入草稿和条目数组。
|
||||||
|
- 能通过 `TextField` 输入内容,并通过按钮把新条目加入 `List`。
|
||||||
|
- 不能加入空内容,添加成功后输入框清空。
|
||||||
|
- 能在一次运行中连续加入多条内容。
|
||||||
|
- 重新启动 App 后允许恢复为空,不要求保存。
|
||||||
|
|
||||||
|
达到最低标准即可视为完成,不要求视觉精致度或额外功能。
|
||||||
|
|
||||||
|
### 建议完成标准
|
||||||
|
|
||||||
|
鼓励根据兴趣选择一项或多项尝试:
|
||||||
|
|
||||||
|
- 增加空清单提示。
|
||||||
|
- 显示当前条目数量。
|
||||||
|
- 自定义 SF Symbol、颜色和列表样式。
|
||||||
|
- 支持按回车添加。
|
||||||
|
- 为条目增加第二个简单字段。
|
||||||
|
- 尝试删除单条内容。
|
||||||
|
- 使用 AI 协助解释错误或进行局部改造,并自行运行判断结果。
|
||||||
|
|
||||||
|
建议项不作为作业合并和参与记录的门槛。具体题目、功能说明、项目目录、分支、Commit、PR 标题和提交安排,统一放入开课前单独制作的部门课 06 课后作业文档。
|
||||||
|
|
||||||
|
## 验收方式
|
||||||
|
|
||||||
|
- 学员将完整项目源码通过个人 Fork 和作业分支向部门公开仓库发起 PR。
|
||||||
|
- 本节涉及输入和动态列表效果,学员可以通过约定渠道发送简短运行视频,授课干部也可以直接取得 PR 源码运行检查。
|
||||||
|
- 下节课可选择少量作品轻量复习,形式可以是现场运行、追踪一次数据流,或者简要说明 `draft`、数组和 `List` 的关系。
|
||||||
|
- 回答不完整用于发现共同问题和安排复习,不作为淘汰或横向排名依据。
|
||||||
|
- 未完成时允许提交 Draft PR,记录当前状态、错误和已经尝试的步骤。
|
||||||
|
|
||||||
|
验收主要确认最低完成标准是否成立。建议完成内容只用于鼓励探索,不影响基础作业通过。
|
||||||
|
|
||||||
|
课纲不规定视频编码、文件格式、大小、完整命令或其他操作细节;这些内容如确有需要,放入单独的课后作业文档。
|
||||||
|
|
||||||
|
源码提交、工具故障时的参与记录和隐私边界按[共同安排](../iOS部2026十课初步规划.md)执行;具体材料与操作细节由授课干部在备课阶段准备。
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
# 部门课 07:习惯印章簿与轻量持久化
|
||||||
|
|
||||||
|
## 教学目标
|
||||||
|
|
||||||
|
- 区分 `@State` 临时状态和跨 App 启动保留的持久化数据。
|
||||||
|
- 知道不同形态的数据适合不同保存方式,而不是所有内容都使用同一种工具。
|
||||||
|
- 理解 `@AppStorage` 适合少量、简单、非敏感的设置或偏好。
|
||||||
|
- 认识 `@AppStorage` 中存储标识、默认值、当前值和界面 Binding 的关系。
|
||||||
|
- 认识 SwiftData 中 `@Model`、`ModelContainer`、`ModelContext` 和 `@Query` 的基本职责。
|
||||||
|
- 区分尚未确认的 `@State` 输入草稿和已经插入 SwiftData 的持久记录。
|
||||||
|
- 能追踪以下两条数据流:
|
||||||
|
|
||||||
|
```text
|
||||||
|
简单设置
|
||||||
|
→ @AppStorage
|
||||||
|
→ App 重启后恢复
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
输入备注
|
||||||
|
→ 创建 HabitStamp
|
||||||
|
→ ModelContext 插入并保存
|
||||||
|
→ @Query 取得记录
|
||||||
|
→ List 更新
|
||||||
|
→ App 重启后恢复
|
||||||
|
```
|
||||||
|
|
||||||
|
- 知道 SwiftData 模型具有可供列表区分的持久身份,本节不要求手动建立 UUID。
|
||||||
|
- 能通过彻底终止并重新启动同一个 App,判断数据是否真正保存。
|
||||||
|
|
||||||
|
## 教学内容
|
||||||
|
|
||||||
|
授课干部使用“喝水印章簿基础版”作为课堂 Demo,学员课后完成同源的“我的习惯印章簿”。
|
||||||
|
|
||||||
|
Demo 包含:
|
||||||
|
|
||||||
|
- 一个由 `@AppStorage` 保存的习惯名称;
|
||||||
|
- 一个由 `@State` 保存的印章备注草稿;
|
||||||
|
- `HabitStamp` SwiftData 模型,包含简短备注和创建时间;
|
||||||
|
- 一个盖章按钮;
|
||||||
|
- 使用 `@Query` 展示的印章数量和记录列表;
|
||||||
|
- App 重新启动后的数据恢复。
|
||||||
|
|
||||||
|
课堂按以下方向讲解:
|
||||||
|
|
||||||
|
1. 先使用临时状态保存习惯名称,重新运行后观察数据消失。
|
||||||
|
2. 改用 `@AppStorage`,认识存储标识和默认值,再次验证跨启动恢复。
|
||||||
|
3. 说明单一设置与多条记录的区别,引出 SwiftData。
|
||||||
|
4. 定义包含备注和创建时间的 `HabitStamp` 模型。
|
||||||
|
5. 为 App 配置相应的模型存储容器。
|
||||||
|
6. 从 SwiftUI 环境取得 `ModelContext`,使用 `@Query` 取得已有印章。
|
||||||
|
7. 使用 `@State` 保存尚未确认的备注草稿。
|
||||||
|
8. 点击按钮后检查空内容、创建印章、插入 context,并清空草稿。
|
||||||
|
9. 观察 `@Query` 和 `List` 如何随新记录更新。
|
||||||
|
10. 彻底结束并重新启动同一个 App,确认习惯名称和多条印章记录均被恢复。
|
||||||
|
|
||||||
|
本节采用授课干部 Demo 讲解型:
|
||||||
|
|
||||||
|
- 授课干部提前完成基础版并通过 Build/Run。
|
||||||
|
- 课堂实际敲写和解释关键数据链路。
|
||||||
|
- 保留完成版作为现场演示失败时的备用。
|
||||||
|
- 学员主要观察、预测、追踪数据和讨论,不要求同步跟抄。
|
||||||
|
- 学员课后参考 Demo、步骤资料或 AI 完成同源作品。
|
||||||
|
|
||||||
|
共同内容只使用 SwiftData 的新增和读取能力,不扩展为完整 CRUD。本节不讲:
|
||||||
|
|
||||||
|
- 编辑、删除、撤销和复杂表单;
|
||||||
|
- 查询筛选、排序原理和关系模型;
|
||||||
|
- 多个习惯、每日唯一打卡、连续天数和漏签判断;
|
||||||
|
- 提醒通知、日历、统计图和云同步;
|
||||||
|
- Schema 迁移、数据库底层原理和架构分层;
|
||||||
|
- 直接操作 `UserDefaults`、Codable 文件或 Keychain;
|
||||||
|
- 敏感信息保存。
|
||||||
|
|
||||||
|
`@AppStorage` 基于 `UserDefaults`,适合简单的非敏感设置;SwiftData 通过模型、容器、上下文和查询管理结构化记录。后续细化备课时可参考 Apple 的 [AppStorage](https://developer.apple.com/documentation/swiftui/appstorage)、[UserDefaults](https://developer.apple.com/documentation/foundation/userdefaults) 和 [SwiftData 跨启动保存](https://developer.apple.com/documentation/swiftdata/preserving-your-apps-model-data-across-launches)。
|
||||||
|
|
||||||
|
## 课后作业
|
||||||
|
|
||||||
|
作业方向为“我的习惯印章簿”。具体题目、项目目录、分支、Commit、PR 标题和提交期限以后写入独立的课后作业文档。
|
||||||
|
|
||||||
|
### 最低完成标准
|
||||||
|
|
||||||
|
- 项目能够 Build/Run。
|
||||||
|
- 选择一个轻松、非敏感的习惯主题。
|
||||||
|
- 使用 `@AppStorage` 保存习惯名称,并提供合理默认值。
|
||||||
|
- 定义包含简短备注和创建时间的 SwiftData 印章模型。
|
||||||
|
- 正确配置模型容器,并使用 `ModelContext` 插入记录。
|
||||||
|
- 使用 `@State` 保存尚未提交的备注草稿。
|
||||||
|
- 空备注不能生成印章。
|
||||||
|
- 成功盖章后清空输入框。
|
||||||
|
- 使用 `@Query` 读取记录,并显示印章数量和记录列表。
|
||||||
|
- 能在一次运行中连续加入多枚印章。
|
||||||
|
- 彻底结束并重新启动 App 后,习惯名称和已有印章仍然存在。
|
||||||
|
- 至少完成一处主题文字、图标或配色修改。
|
||||||
|
|
||||||
|
达到最低标准即可视为完成,不要求视觉精致度、额外模型或完整数据管理功能。
|
||||||
|
|
||||||
|
### 建议完成标准
|
||||||
|
|
||||||
|
鼓励选择一项或多项探索:
|
||||||
|
|
||||||
|
- 增加空印章簿提示。
|
||||||
|
- 调整日期的显示形式。
|
||||||
|
- 让最新印章优先显示。
|
||||||
|
- 自定义印章图标、颜色或卡片样式。
|
||||||
|
- 增加第二个由 `@AppStorage` 保存的简单偏好。
|
||||||
|
- 尝试删除单条印章或清空全部印章,并考虑是否需要确认。
|
||||||
|
- 使用 AI 解释局部代码、排查错误或完成一处小改造,再自行运行验证。
|
||||||
|
|
||||||
|
建议内容不影响基础作业通过,不要求发展成完整习惯管理 App。
|
||||||
|
|
||||||
|
## 验收方式
|
||||||
|
|
||||||
|
- 学员通过个人 Fork 和本周作业分支,向部门公开仓库提交完整项目源码 PR。
|
||||||
|
- 本节需要证明跨启动恢复,可通过约定渠道发送简短运行视频,展示修改习惯名称、添加印章、彻底结束 App、重新启动后数据仍然存在。
|
||||||
|
- 授课干部也可以取得 PR 源码,在自己的模拟器中完成同样验证。
|
||||||
|
- 下节课可选择少量作品轻量复习,可以现场运行,也可以围绕以下问题交流:
|
||||||
|
- 哪个值由 `@AppStorage` 保存;
|
||||||
|
- 哪些内容仍然只是 `@State`;
|
||||||
|
- `@Model`、容器、context 和 query 分别承担什么作用;
|
||||||
|
- 为什么重新启动后记录仍然存在。
|
||||||
|
- 回答不完整用于发现共同问题和安排复习,不用于排名或淘汰。
|
||||||
|
- 未完成时允许提交 Draft PR,说明当前状态、完整错误和已经尝试的步骤。
|
||||||
|
|
||||||
|
验收只判断最低完成标准。建议完成内容不影响基础作业通过,课纲不规定视频编码、文件格式、大小、完整命令或其他操作细节。
|
||||||
|
|
||||||
|
源码提交、工具故障时的参与记录和隐私边界按[共同安排](../iOS部2026十课初步规划.md)执行;具体材料与操作细节由授课干部在备课阶段准备。
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
# 部门课 08:项目组织与 MVVM 架构入门
|
||||||
|
|
||||||
|
## 教学目标
|
||||||
|
|
||||||
|
- 认识 SwiftUI App 入口、根页面、功能页面、子组件、模型、状态对象和资源分别承担什么作用。
|
||||||
|
- 理解项目组织的核心是让职责和依赖清楚,而不是建立固定数量的文件夹。
|
||||||
|
- 正式理解 MVVM 的三类职责:
|
||||||
|
- Model 保存业务数据和规则;
|
||||||
|
- View 负责显示状态并传递用户操作;
|
||||||
|
- ViewModel 管理页面需要的状态和操作,连接 Model 与 View。
|
||||||
|
- 能追踪以下数据流:
|
||||||
|
|
||||||
|
```text
|
||||||
|
用户操作
|
||||||
|
→ View 传递意图
|
||||||
|
→ ViewModel 处理操作
|
||||||
|
→ Model 或业务状态改变
|
||||||
|
→ ViewModel 发布新状态
|
||||||
|
→ View 更新
|
||||||
|
```
|
||||||
|
|
||||||
|
- 认识 `ObservableObject`、`@Published`、`@StateObject` 和 `@ObservedObject` 在本课拟采用的 MVVM 写法中的基本职责,Treversi 中的实际对应关系在备课时核对,不展开底层发布机制。
|
||||||
|
- 知道状态对象应有清楚的所有者,接收者只观察同一个对象,不随意建立重复的事实来源。
|
||||||
|
- 能初步判断一段代码更适合放在 Model、ViewModel 还是 View。
|
||||||
|
- 认识小项目可以按代码角色组织,较大项目也可以按 Game、Settings 等功能组织。
|
||||||
|
- 知道 MVVM 是一种常用选择,不是 SwiftUI 或所有 App 强制采用的唯一架构。
|
||||||
|
- 能在保持功能正常的前提下逐步拆分旧项目,并在每一步重新 Build/Run。
|
||||||
|
|
||||||
|
授课干部后续备课时可参考 Apple 的 [App organization](https://developer.apple.com/documentation/swiftui/app-organization) 和 [Managing model data in your app](https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app)。
|
||||||
|
|
||||||
|
## 教学内容
|
||||||
|
|
||||||
|
本节采用 Demo 讲解型。授课干部先运行作为课程案例的 Treversi 游戏,再从完整产品反向阅读项目结构和一次真实的数据流。课堂主要讲授:
|
||||||
|
|
||||||
|
1. 认识 `TreversiApp.swift`、资源文件、Core、Features 和 UI 等项目组成,以及 App 入口、页面、组件、模型、状态和资源之间的关系。
|
||||||
|
2. 区分“代码角色”和“目录结构”:MVVM 描述职责与依赖,Core、Features、UI 等目录描述工程中的组织方式,两者不是同一件事。
|
||||||
|
3. 从 Treversi 中寻找三类职责:
|
||||||
|
- 棋盘、玩家、配置和规则等 Model;
|
||||||
|
- 游戏状态、设置预览状态和操作处理等 ViewModel 职责;
|
||||||
|
- 棋盘、游戏、菜单和设置页面等 View。
|
||||||
|
4. 追踪一次“玩家点击合法落子位置”的完整链路:View 接收点击,状态对象处理落子意图,游戏规则和模型改变,可观察状态更新,棋盘 View 重新显示。
|
||||||
|
5. 结合源码实际写法认识:
|
||||||
|
- `ObservableObject` 表示可被观察的状态对象;
|
||||||
|
- `@Published` 标记会驱动界面变化的状态;
|
||||||
|
- `@StateObject` 用于当前 View 创建并拥有的对象;
|
||||||
|
- `@ObservedObject` 用于接收并观察外部已有对象。
|
||||||
|
6. 比较两种常见的项目组织思路:
|
||||||
|
- 小项目按 `Models / ViewModels / Views / Resources` 等代码角色分类;
|
||||||
|
- 较大项目按 Feature 分组,并保留 Core 和共享 UI。
|
||||||
|
7. 讨论何时值得拆分:某段代码已有独立职责、组件会被重复使用、页面同时混入大量显示与业务规则,或者修改功能时已经难以判断代码应该放在哪里。
|
||||||
|
8. 给出旧项目的渐进重构顺序:
|
||||||
|
- 先记录并运行原始行为;
|
||||||
|
- 标出数据、操作、规则和界面;
|
||||||
|
- 先提取 Model;
|
||||||
|
- 再建立 ViewModel 并移动状态与操作;
|
||||||
|
- 让 View 主要读取状态并传递意图;
|
||||||
|
- 最后整理文件和目录;
|
||||||
|
- 每移动一步就重新 Build/Run。
|
||||||
|
9. 简要介绍 MVC、MVI、Redux、TCA 和 Clean Architecture 的存在,只说明它们提供了不同的职责划分或状态管理方法,不比较优劣,也不要求记忆。
|
||||||
|
|
||||||
|
Treversi 只作为真实工程阅读案例。授课干部在备课阶段取得真实源码并核对 `GameState`、`PreviewBoardState` 等类型和观察关系,再确定最终课堂图示和讲解口径。
|
||||||
|
|
||||||
|
本节不讲协议抽象、依赖注入、Service、Repository、Coordinator、复杂路由、单元测试、Mock、测试驱动开发、多 Target、Swift Package 或工程模块化,也不展开 Clean Architecture、TCA 等其他架构的具体代码。不要求所有页面机械地套入 ViewModel,不以文件数量、文件夹层级或代码行数判断架构质量。
|
||||||
|
|
||||||
|
## 课后作业
|
||||||
|
|
||||||
|
作业方向为“整理我的旧作品”。具体提交目录、分支、Commit、PR 标题和截止时间以后写入独立的课后作业文档。
|
||||||
|
|
||||||
|
### 最低完成标准
|
||||||
|
|
||||||
|
- 优先选择部门课 4“小植物补水卡”或部门课 6“主题清单”;没有合适旧作品时,可以使用授课干部以后提供的备用项目。
|
||||||
|
- 重构前先确认原项目能够 Build/Run。
|
||||||
|
- 根据项目内容建立清楚的 Model、ViewModel 和 View 职责。
|
||||||
|
- 将主要职责拆入不同 Swift 文件,并使用自己能够理解的命名和目录组织。
|
||||||
|
- ViewModel 按课堂确认的 Treversi 观察方式连接 View。
|
||||||
|
- View 主要负责界面显示和传递用户意图,不继续保存主要业务规则。
|
||||||
|
- Model 不依赖具体 View。
|
||||||
|
- 重构后原有核心操作仍然正常运行。
|
||||||
|
- 完成至少一处可见修改,并能指出这项修改分别影响了哪个层次。
|
||||||
|
- 能简要追踪一次:
|
||||||
|
|
||||||
|
```text
|
||||||
|
用户操作
|
||||||
|
→ View
|
||||||
|
→ ViewModel
|
||||||
|
→ Model 或状态
|
||||||
|
→ View 更新
|
||||||
|
```
|
||||||
|
|
||||||
|
达到最低标准即可,不要求把项目改造成正式工程,也不要求目录与授课干部示例完全一致。
|
||||||
|
|
||||||
|
### 建议完成标准
|
||||||
|
|
||||||
|
鼓励根据兴趣选择一项或多项尝试:
|
||||||
|
|
||||||
|
- 将重复界面拆成独立子 View。
|
||||||
|
- 在角色分类的基础上尝试按 Feature 组织文件。
|
||||||
|
- 为 ViewModel 增加一个范围明确的新操作。
|
||||||
|
- 画一张简单的项目职责图或数据流图。
|
||||||
|
- 比较传统 `ObservableObject` 写法与现代 `@Observable` 的差异。
|
||||||
|
- 请 AI 检查某段代码是否放错职责,但由自己决定是否调整并重新运行。
|
||||||
|
|
||||||
|
建议内容不影响基础作业通过。
|
||||||
|
|
||||||
|
## 验收方式
|
||||||
|
|
||||||
|
- 学员通过个人 Fork 和作业分支向部门公开仓库提交完整项目源码 PR。
|
||||||
|
- 授课干部取得源码后确认项目能够 Build/Run,重构前后的核心行为一致。
|
||||||
|
- PR 中简要说明:
|
||||||
|
- 哪些文件属于 Model、ViewModel 和 View;
|
||||||
|
- ViewModel 拥有哪些状态和操作;
|
||||||
|
- 一次用户操作怎样经过三层并更新界面;
|
||||||
|
- 自己完成了哪一处可见修改。
|
||||||
|
- 如需证明互动效果,可以通过约定渠道发送简短运行视频,不规定视频格式和编码。
|
||||||
|
- 结合本课 PR 和作业反馈确认职责划分与操作链,不把技术抽查安排到部门课 09。
|
||||||
|
- 简短说明不完整时给予反馈,不因表达不够准确拒绝已达到基础要求的作品,也不安排逐人技术答辩、排名或淘汰。
|
||||||
|
- 未完成时允许提交 Draft PR,记录当前结构、完整错误和已经尝试的步骤。
|
||||||
|
- 不以文件数量、目录是否与授课干部完全一致、是否使用高级抽象或能否背诵其他架构作为通过门槛。
|
||||||
|
|
||||||
|
源码提交、工具故障时的参与记录和隐私边界按[共同安排](../iOS部2026十课初步规划.md)执行;具体材料与操作细节由授课干部在备课阶段准备。
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# 部门课 09:学习回顾与继续出发
|
||||||
|
|
||||||
|
## 教学目标
|
||||||
|
|
||||||
|
- 能用自己的理解回顾前八节接触过的工具、界面、交互、数据和项目组织能力。
|
||||||
|
- 知道九节部门课只是学习入口,不代表已经完成系统的 iOS 开发训练。
|
||||||
|
- 了解继续完善旧作品、自主学习、参加爱特杯或其他比赛、组队实践等后续入口。
|
||||||
|
- 能从授课干部的真实经历中认识项目范围、学习方法、比赛协作和常见卡点。
|
||||||
|
- 能分享一个印象较深的收获,并表达一个想尝试的方向或仍未解决的问题。
|
||||||
|
- 通过集中答疑获得与自己当前阶段相符的下一步建议;允许暂时没有确定方向。
|
||||||
|
|
||||||
|
## 教学内容
|
||||||
|
|
||||||
|
本节围绕以下过程展开:
|
||||||
|
|
||||||
|
```text
|
||||||
|
回顾课程地图
|
||||||
|
→ 授课干部分享学习、项目与比赛经验
|
||||||
|
→ 学员逐人轻量分享
|
||||||
|
→ 集中答疑并形成后续方向
|
||||||
|
```
|
||||||
|
|
||||||
|
- 按课程地图回顾部门课 1–8,不重新讲授具体 API:
|
||||||
|
- 第一次运行;
|
||||||
|
- AI 与 Git;
|
||||||
|
- 页面组合;
|
||||||
|
- 交互状态;
|
||||||
|
- 列表到详情的信息流;
|
||||||
|
- 输入与内存数据;
|
||||||
|
- 轻量持久化;
|
||||||
|
- 项目组织与 MVVM。
|
||||||
|
- 授课干部分享个人学习、项目、组队和比赛经验,同时说明真实困难、投入和踩坑,不把个人经历包装成唯一正确路线。
|
||||||
|
- 介绍后续发展入口,包括继续修改旧作品、补充某项技术、制作个人小项目、参加爱特杯或当期有效比赛、寻找队友,以及继续向部门成员求助。
|
||||||
|
- 比赛名称、时间和规则不在课纲中写死,开课前在 PPT 或方向资料中依据当年信息核对。
|
||||||
|
- 每名学员进行约 2–3 分钟的轻量分享,至少包括:
|
||||||
|
- 一个印象较深的收获;
|
||||||
|
- 一个下一步想尝试的方向,或一个仍想询问的问题。
|
||||||
|
- 分享不要求运行作品、展示完整成果或解释技术细节,不转化为逐人技术答辩。
|
||||||
|
- 授课干部结合分享内容进行共性答疑和轻量个别指导;适合单独讨论的问题留到课后继续沟通。
|
||||||
|
- 本节不讲新的 SwiftUI 知识,不安排现场编程,不要求形成新项目、想法卡、项目计划或可运行验证。
|
||||||
|
|
||||||
|
## 课后作业
|
||||||
|
|
||||||
|
- 本节没有统一课后作业、GitHub PR、截图或视频提交。
|
||||||
|
- 愿意继续的学员可以自愿选择完善旧作品、了解比赛、寻找队友、学习感兴趣的技术,或继续向授课干部咨询。
|
||||||
|
- 这些后续行动不设置统一截止时间,不作为课程完成门槛,也无需另建正式作业文档。
|
||||||
|
|
||||||
|
## 验收方式
|
||||||
|
|
||||||
|
- 本节只记录课堂参与,不验收新的技术作品。
|
||||||
|
- 参与表现为完成一次轻量分享,并参与或认真听取答疑。
|
||||||
|
- 不评价作品复杂度、表达技巧、方向是否成熟或技术回答是否准确。
|
||||||
|
- 暂时没有后续方向的学员可以用一个真实问题代替方向选择。
|
||||||
|
- 缺席者不设置惩罚性补交,可以自行查看课程回顾材料并在之后提出问题。
|
||||||
Reference in New Issue
Block a user